简体   繁体   中英

Determine arity of method with keyword arguments

I am developing a Ruby application where I am dynamically invoking methods based on JSON data. Loosely:

def items
  # do something
end

def createItem( name:, data:nil )
  # do something that requires a name keyword argument
end

def receive_json(json) # e.g. { "cmd":"createItem", "name":"jim" }
  hash = JSON.parse(json)
  cmd = hash.delete('cmd')
  if respond_to?(cmd)
    params = Hash[ hash.map{ |k,v| [k.to_sym, v } ]
    method(cmd).arity==0 ? send(cmd) : send(cmd,params)
  end
end

As shown above, some methods take no arguments, and some take keyword arguments. Under Ruby 2.1.0 (where I'm developing) the arity of both methods above is 0 . However, if I send(cmd,params) always, I get an error for methods that take no parameters.

How can I use send to correctly pass along the keyword arguments when desired, but omit them when not?

Using parameters instead of arity appears to work for my needs:

method(cmd).parameters.empty? ? send(cmd) : send(cmd,opts)

More insight into the richness of the parameters return values:

def foo; end
method(:foo).parameters
#=> [] 

def bar(a,b=nil); end
method(:bar).parameters
#=> [[:req, :a], [:opt, :b]] 

def jim(a:,b:nil); end
method(:jim).parameters
#=> [[:keyreq, :a], [:key, :b]] 

Here's a generic method that picks out only those named values that your method supports, in case you have extra keys in your hash that aren't part of the keyword arguments used by the method:

module Kernel
  def dispatch(name,args)
    keyargs = method(name).parameters.map do |type,name|
      [name,args[name]] if args.include?(name)
    end.compact.to_h
    keyargs.empty? ? send(name) : send(name,keyargs)
  end
end

h = {a:1, b:2, c:3}

def no_params
  p :yay
end

def few(a:,b:99)
  p a:a, b:b
end

def extra(a:,b:,c:,z:17)
  p a:a, b:b, c:c, z:z
end

dispatch(:no_params,h) #=> :yay
dispatch(:few,h)       #=> {:a=>1, :b=>2}
dispatch(:extra,h)     #=> {:a=>1, :b=>2, :c=>3, :z=>17}

At first, I thought params is supposed to become empty when the :cmd value is "items" , in which case Jesse Sielaff's answer would be correct. But since you seem to be claiming that it isn't, I think that it is your design flaw. Instead of trying to dispatch in that way, you should rather have those methods just gobble the arguments:

def items(name:nil, data:nil)
  ...
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM