Есть ли способ доступа к аргументам метода в Ruby?

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

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

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}

Ответ 1

В 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 }

Ответ 2

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

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

foo(1, 2)  # => 1, 2

Ответ 3

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

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

Ответ 4

Это интересный вопрос. Возможно, используя local_variables? Но должен быть способ, кроме использования eval. Я смотрю в ядре 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

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

  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

Ответ 6

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

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

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

Ответ 7

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

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, если в запятой. Приветствия

Ответ 8

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

Ответ 9

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

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)

Ответ 10

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

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

Ответ 11

Если функция находится внутри какого-то класса, вы можете сделать что-то вроде этого:

class Car
  def drive(speed)
  end
end

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

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