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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9211813/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 02:47:53  来源:igfitidea点击:

Is there a way to access method arguments in Ruby?

ruby-on-railsruby

提问by Haris Krajina

New to Ruby and ROR and loving it each day, so here is my question since I have not idea how to google it (and I have tried :) )

Ruby 和 ROR 的新手并且每天都喜欢它,所以这是我的问题,因为我不知道如何搜索它(我已经尝试过 :))

we have method

我们有方法

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

So what I am looking for way to get all arguments passed to method, without listing each one. Since this is Ruby I assume there is a way :) if it was java I would just list them :)

所以我正在寻找将所有参数传递给方法的方法,而不列出每个参数。由于这是 Ruby,我认为有一种方法:) 如果是 Java,我会列出它们:)

Output would be:

输出将是:

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

回答by mikej

In Ruby 1.9.2 and later you can use the parametersmethod on a method to get the list of parameters for that method. This will return a list of pairs indicating the name of the parameter and whether it is required.

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

e.g.

例如

If you do

如果你这样做

def foo(x, y)
end

then

然后

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

You can use the special variable __method__to get the name of the current method. So within a method the names of its parameters can be obtained via

您可以使用特殊变量__method__来获取当前方法的名称。所以在一个方法中,其参数的名称可以通过

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

You could then display the name and value of each parameter with

然后,您可以使用以下命令显示每个参数的名称和值

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


Note:since this answer was originally written, in current versions of Ruby evalcan no longer be called with a symbol. To address this, an explicit to_shas been added when building the list of parameter names i.e. parameters.map { |arg| arg[1].to_s }

注意:由于这个答案最初是写的,在当前版本的 Rubyeval中不能再用符号调用。为了解决这个问题,to_s在构建参数名称列表时添加了一个显式,即parameters.map { |arg| arg[1].to_s }

回答by Jakub Jirutka

Since Ruby 2.1 you can use binding.local_variable_getto read value of any local variable, including method parameters (arguments). Thanks to that you can improve the accepted answerto avoid evileval.

从 Ruby 2.1 开始,您可以使用binding.local_variable_get读取任何局部变量的值,包括方法参数(参数)。因此,您可以改进已接受的答案以避免邪恶评估。

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

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

回答by Arun Kumar Arjunan

One way to handle this is:

处理这种情况的一种方法是:

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

回答by Raffaele

This is an interesting question. Maybe using local_variables? But there must be a way other than using eval. I'm looking in Kernel doc

这是个有趣的问题。也许使用local_variables?但是除了使用 eval 之外,肯定还有其他方法。我正在查看内核文档

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

回答by Jon Jagger

This may be helpful...

这可能会有所帮助...

  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

回答by Al Johri

You can define a constant such as:

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

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

And use it in your code like:

并在您的代码中使用它,例如:

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

回答by ebeland

It seems like what this question is trying to accomplish could be done with a gem I just released, https://github.com/ericbeland/exception_details. It will list local variables and vlaues (and instance variables) from rescued exceptions. Might be worth a look...

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

回答by Tom L

Before I go further, you're passing too many arguments into foo. It looks like all of those arguments are attributes on a Model, correct? You should really be passing the object itself. End of speech.

在我继续之前,您将太多参数传递给 foo。看起来所有这些参数都是模型的属性,对吗?你真的应该传递对象本身。演讲结束。

You could use a "splat" argument. It shoves everything into an array. It would look like:

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

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

回答by Simon Bagreev

If you would change the method signature, you can do something like this:

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

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

Or:

或者:

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

In this case, interpolated argsor opts.valueswill be an array, but you can joinif on comma. Cheers

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

回答by greenback

If you need arguments as a Hash, and you don't want to pollute method's body with tricky extraction of parameters, use this:

如果您需要将参数作为 Hash,并且您不想通过棘手的参数提取来污染方法的主体,请使用以下命令:

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

Just add this class to your project:

只需将此类添加到您的项目中:

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