Est-il possible d’accéder aux arguments de méthode dans Ruby?

Nouveau à Ruby et ROR et l’aimer chaque jour, alors voici ma question puisque je n’ai pas idée de comment google (et j’ai essayé :))

nous avons la méthode

def foo(first_name, last_name, age, sex, is_plumber) # some code # error happens here logger.error "Method has failed, here are all method arguments #{SOMETHING}" end 

Donc, ce que je cherche un moyen de faire passer tous les arguments à la méthode, sans énumérer chacun. Puisque c’est Ruby, je suppose qu’il y a un moyen 🙂 si c’était java, je les énumérerais simplement 🙂

La sortie serait:

 Method has failed, here are all method arguments {"Mario", "Super", 40, true, true} 

Dans Ruby 1.9.2 et versions ultérieures, vous pouvez utiliser la méthode parameters sur une méthode pour obtenir la liste des parameters de cette méthode. Cela renverra une liste de paires indiquant le nom du paramètre et si elle est requirejse.

par exemple

Si tu fais

 def foo(x, y) end 

puis

 method(:foo).parameters # => [[:req, :x], [:req, :y]] 

Vous pouvez utiliser la variable spéciale __method__ pour obtenir le nom de la méthode actuelle. Ainsi, dans une méthode, les noms de ses parameters peuvent être obtenus via

 args = method(__method__).parameters.map { |arg| arg[1].to_s } 

Vous pouvez ensuite afficher le nom et la valeur de chaque paramètre avec

 logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ') 

Remarque: cette réponse ayant été écrite à l’origine, les versions actuelles de Ruby ne peuvent plus être appelées avec un symbole. Pour y remédier, un to_s explicite a été ajouté lors de la construction de la liste des noms de parameters, à savoir parameters.map { |arg| arg[1].to_s } parameters.map { |arg| arg[1].to_s }

Depuis Ruby 2.1, vous pouvez utiliser binding.local_variable_get pour lire la valeur de toute variable locale, y compris les parameters de la méthode (arguments). Grâce à cela, vous pouvez améliorer la réponse acceptée pour éviter mal eval.

 def foo(x, y) method(__method__).parameters.map do |_, name| binding.local_variable_get(name) end end foo(1, 2) # => 1, 2 

Une façon de gérer cela est la suivante:

 def foo(*args) first_name, last_name, age, sex, is_plumber = *args # some code # error happens here logger.error "Method has failed, here are all method arguments #{args.inspect}" end 

C’est une question intéressante. Peut-être en utilisant local_variables ? Mais il doit y avoir un moyen autre que d’utiliser eval. Je cherche dans le doc du kernel

 class Test def method(first, last) local_variables.each do |var| puts eval var.to_s end end end Test.new().method("aaa", 1) # outputs "aaa", 1 

Cela peut être utile …

  def foo(x, y) args(binding) end def args(callers_binding) callers_name = caller[0][/`.*'/][1..-2] parameters = method(callers_name).parameters parameters.map { |_, arg_name| callers_binding.local_variable_get(arg_name) } end 

Avant d’aller plus loin, vous transmettez trop d’arguments à foo. Il semble que tous ces arguments soient des atsortingbuts sur un modèle, n’est-ce pas? Vous devriez vraiment passer l’object lui-même. Fin du discours

Vous pouvez utiliser un argument “splat”. Il pousse tout dans un tableau. Cela ressemblerait à:

 def foo(*bar) ... log.error "Error with arguments #{bar.joins(', ')}" end 

Si vous voulez changer la signature de la méthode, vous pouvez faire quelque chose comme ceci:

 def foo(*args) # some code # error happens here logger.error "Method has failed, here are all method arguments #{args}" end 

Ou:

 def foo(opts={}) # some code # error happens here logger.error "Method has failed, here are all method arguments #{opts.values}" end 

Dans ce cas, les arguments interpolés ou opts.values seront un tableau, mais vous pouvez vous join à une virgule. À votre santé

Il semble que ce que cette question essaie de faire pourrait être fait avec un joyau que je viens de publier, https://github.com/ericbeland/exception_details . Il listera les variables locales et vlaues (et les variables d’instance) à partir des exceptions récupérées. Peut-être vaut-il un coup d’oeil …

Vous pouvez définir une constante telle que:

 ARGS_TO_HASH = "method(__method__).parameters.map { |arg| arg[1].to_s }.map { |arg| { arg.to_sym => eval(arg) } }.reduce Hash.new, :merge" 

Et l’utiliser dans votre code comme:

 args = eval(ARGS_TO_HASH) another_method_that_takes_the_same_arguments(**args) 

Si vous avez besoin d’arguments en tant que hachage et que vous ne voulez pas polluer le corps de la méthode avec une extraction délicate des parameters, utilisez ceci:

 def mymethod(firstarg, kw_arg1:, kw_arg2: :default) args = MethodArguments.(binding) # All arguments are in `args` hash now ... end 

Ajoutez simplement cette classe à votre projet:

 class MethodArguments def self.call(ext_binding) raise ArgumentError, "Binding expected, #{ext_binding.class.name} given" unless ext_binding.is_a?(Binding) method_name = ext_binding.eval("__method__") ext_binding.receiver.method(method_name).parameters.map do |_, name| [name, ext_binding.local_variable_get(name)] end.to_h end end