Определить арность метода с помощью ключевых слов

Я разрабатываю приложение 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
Другие вопросы по тегам