web-dev-qa-db-fra.com

Y a-t-il un moyen d'accéder aux arguments de méthode dans Ruby?

Nouveau sur Ruby et ROR et l’aimer tous les jours), alors voici ma question car je ne sais pas comment le rechercher sur Google (et j’ai essayé :))

nous avons 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 recherche un moyen d’obtenir tous les arguments passés à la méthode, sans les lister tous. Puisqu'il s'agit de Ruby, je suppose qu'il existe un moyen :) s'il était Java, je les énumérerais simplement :)

La sortie serait:

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

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 paramètres de cette méthode. Cela renverra une liste de paires indiquant le nom. du paramètre et s'il est requis.

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 en cours. Ainsi, dans une méthode, les noms de ses paramètres 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(', ')

Note: puisque cette réponse a été écrite à l'origine, dans les versions actuelles de Ruby eval ne peut plus être appelé avec un symbole. Pour y remédier, un explicite to_s a été ajouté lors de la construction de la liste des noms de paramètres, à savoir parameters.map { |arg| arg[1].to_s }

148
mikej

Depuis Ruby 2.1, vous pouvez utiliser binding.local_variable_get pour lire la valeur de toute variable locale, y compris les paramètres de méthode (arguments). Grâce à cela, vous pouvez améliorer la réponse acceptée à é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
47
Jakub Jirutka

Une façon de gérer cela est:

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
17
Arun Kumar Arjunan

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

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
7
Raffaele

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
5
Jon Jagger

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 utilisez-le dans votre code comme:

args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)
2
Al Johri

Il semble que ce que cette question tente d'accomplir puisse être réalisé 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) des exceptions sauvées. Ça vaut peut-être le coup d'oeil ...

2
ebeland

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

Vous pouvez utiliser un argument "splat". Il met tout dans un tableau. Cela ressemblerait à:

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

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, interpolée args ou opts.values sera un tableau, mais vous pouvez join si vous utilisez une virgule. À votre santé

2
Simon Bagreev

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 de paramètres, 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
1
greenback

Si la fonction est dans une classe, vous pouvez faire quelque chose comme ceci:

class Car
  def drive(speed)
  end
end

car = Car.new
method = car.method(:drive)

p method.parameters #=> [[:req, :speed]] 
0
Nikhil Wagh