有没有办法在Ruby中访问方法参数?

时间:2012-02-09 13:34:12

标签: ruby-on-rails 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}

11 个答案:

答案 0 :(得分:144)

在Ruby 1.9.2及更高版本中,您可以在方法上使用parameters方法来获取该方法的参数列表。这将返回一个对列表,指示参数的名称以及是否需要它。

e.g。

如果你这样做

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 }

答案 1 :(得分:45)

从Ruby 2.1开始,您可以使用binding.local_variable_get来读取任何局部变量的值,包括方法参数(参数)。多亏了你,你可以改进accepted answer以避免 evil eval。

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

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

答案 2 :(得分:16)

处理此问题的一种方法是:

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

答案 3 :(得分:6)

这是一个有趣的问题。也许使用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

答案 4 :(得分: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

答案 5 :(得分:2)

在我走得更远之前,你向foo传递了太多的论据。看起来所有这些参数都是Model上的属性,对吗?你应该真正传递对象本身。演讲结束。

您可以使用“splat”参数。它把所有东西推到一个阵列中。它看起来像是:

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

答案 6 :(得分:2)

如果要更改方法签名,可以执行以下操作:

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

在这种情况下,插值argsopts.values将是一个数组,但如果使用逗号,则可以join。干杯

答案 7 :(得分:2)

似乎这个问题试图通过我刚刚发布的gem https://github.com/ericbeland/exception_details来完成。它将列出来自获救异常的局部变量和值(和实例变量)。可能值得一看......

答案 8 :(得分:2)

您可以定义一个常量,例如:

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)

答案 9 :(得分:1)

如果您需要参数作为哈希,并且您不想通过棘手的参数提取来污染方法体,请使用:

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

答案 10 :(得分:0)

如果函数在某个类中,则可以执行以下操作:

class Car
  def drive(speed)
  end
end

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

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