Определить арность метода с помощью ключевых слов
Я разрабатываю приложение Ruby, в котором я динамически вызываю методы, основанные на данных JSON. Неплотно:
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
Как показано выше, некоторые методы не принимают аргументов, а некоторые принимают ключевые аргументы. Под Ruby 2.1.0 (где я разрабатываю) arity
обоих методов выше 0
, Однако если я send(cmd,params)
всегда получаю ошибку для методов, которые не принимают параметров.
Как я могу использовать send
правильно передавать аргументы ключевого слова при желании, но опускать их, когда нет?
2 ответа
С помощью parameters
вместо arity
кажется, работает для моих нужд:
method(cmd).parameters.empty? ? send(cmd) : send(cmd,opts)
Более глубокое понимание богатства parameters
возвращаемые значения:
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]]
Вот универсальный метод, который выбирает только те именованные значения, которые поддерживает ваш метод, в случае, если у вас есть дополнительные ключи в вашем хэше, которые не являются частью аргументов ключевого слова, используемых методом:
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}
Сначала я подумал params
должен стать пустым, когда :cmd
значение "items"
В этом случае ответ Джесси Силаффа будет правильным. Но так как вы, кажется, утверждаете, что это не так, я думаю, что это ваш недостаток дизайна. Вместо того, чтобы пытаться выполнить диспетчеризацию таким способом, вы должны использовать эти методы, просто сожрав аргументы:
def items(name:nil, data:nil)
...
end