Есть ли способ получить доступ к аргументам метода в Ruby? - PullRequest
91 голосов
/ 09 февраля 2012

Новичок в Ruby и ROR и любовь к нему каждый день, поэтому вот мой вопрос, так как я понятия не имею, как Google (и я пытался :))

у нас есть метод

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

Так что я ищу способ передачи всех аргументов в метод, без перечисления каждого из них.Так как это Ruby, я предполагаю, что есть способ :) если бы это был java, я бы просто перечислил их:)

Вывод был бы:

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

Ответы [ 10 ]

144 голосов
/ 09 февраля 2012

В Ruby 1.9.2 и более поздних версиях вы можете использовать метод parameters для метода, чтобы получить список параметров для этого метода. Это вернет список пар с указанием имени параметра и его обязательности.

, например

Если вы делаете

def foo(x, y)
end

тогда

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

Вы можете использовать специальную переменную __method__, чтобы получить имя текущего метода. Таким образом, внутри метода имена его параметров могут быть получены с помощью

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

Затем вы можете отобразить имя и значение каждого параметра с помощью

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

Примечание: , поскольку этот ответ был изначально написан, в текущих версиях Ruby eval больше нельзя вызывать с символом. Для решения этой проблемы был добавлен явный to_s при построении списка имен параметров, т.е. parameters.map { |arg| arg[1].to_s }

47 голосов
/ 05 июля 2015

Начиная с Ruby 2.1, вы можете использовать binding.local_variable_get , чтобы прочитать значение любой локальной переменной, включая параметры метода (аргументы).Благодаря этому вы можете улучшить принятый ответ , чтобы избежать зла eval.

def foo(x, y)
  method(__method__).parameters.map do |_, name|
    binding.local_variable_get(name)
  end
end

foo(1, 2)  # => 1, 2
17 голосов
/ 09 февраля 2012

Один из способов справиться с этим:

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
7 голосов
/ 09 февраля 2012

Это интересный вопрос. Может быть, используя local_variables ? Но должен быть способ, отличный от использования eval. Я смотрю в Kernel 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
5 голосов
/ 31 июля 2017

Это может быть полезно ...

  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
2 голосов
/ 12 августа 2014

Вы можете определить константу, например:

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

И использовать ее в своем коде, например:

args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)
2 голосов
/ 26 августа 2013

Кажется, что то, что пытается решить этот вопрос, может быть сделано с использованием только что выпущенного гема https://github.com/ericbeland/exception_details. В нем будут перечислены локальные переменные и значения (и переменные экземпляра) из спасенных исключений.Может стоит посмотреть ...

2 голосов
/ 09 февраля 2012

Если вы измените сигнатуру метода, вы можете сделать что-то вроде этого:

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

Или:

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

В этом случае интерполированный args или opts.values будет массивом, но вы можете join, если запятая. Приветствия

2 голосов
/ 09 февраля 2012

Прежде чем я продолжу, вы передаете слишком много аргументов в foo.Похоже, что все эти аргументы являются атрибутами в модели, верно?Вы действительно должны передавать сам объект.Конец речи.

Вы можете использовать аргумент "splat".Это пихает все в массив.Это будет выглядеть так:

def foo(*bar)
  ...
  log.error "Error with arguments #{bar.joins(', ')}"
end
1 голос
/ 22 февраля 2018

Если вам нужны аргументы в качестве хэша, и вы не хотите загрязнять тело метода хитрым извлечением параметров, используйте это:

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

Просто добавьте этот класс в ваш проект:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...