如何使用reflection来获取参数名称

我想在Ruby中做一些相当重要的反思。 我想创build一个函数,它返callback用堆栈中各种调用函数参数的名称(只要一个更高就足够了,但为什么要停下来?)。 我可以使用Kernel.caller ,去文件和parsing参数列表,但这将是丑陋的,不可靠的。

我想要的function将以下列方式工作:

 module A def method1( tuti, fruity) foo end def method2(bim, bam, boom) foo end def foo print caller_args[1].join(",") #the "1" mean one step up the call stack end end A.method1 #prints "tuti,fruity" A.method2 #prints "bim, bam, boom" 

我不介意使用ParseTree或类似的工具来完成这个任务,但是看Parsetree,不知道如何使用它来达到这个目的。 像这样创build一个C扩展是另一种可能,但是如果有人已经为我做了这个,那将会很好。

我可以看到,我可能需要某种C扩展。 我想这意味着我的问题是C扩展的组合最容易工作。 我不认为调用者+ ParseTree会是足够的。

至于为什么我想这样做,而不是说“自动debugging”,也许我应该说,我想用这个function来自动检查函数的调用和返回条件:

 def add x, y check_positive return x + y end 

如果xy不正确, check_positive会抛出一个exception。 很显然,这将比这更多,但希望这给了足够的动力。

我build议你看一下Merb的action-args库。

 require 'rubygems' require 'merb' include GetArgs def foo(bar, zed=42) end method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]] 

如果你不想依赖Merb,你可以从github的源代码中select最好的部分。

在Ruby 1.9.2中,可以使用Proc#parameters平凡地获取任何Proc的参数列表(当然也包括任何MethodUnboundMethod ):

 A.instance_method(:method1).parameters # => [[:req, :tuti], [:req, :fruity]] 

格式是符号对的数组:types(必需,可选,rest,块)和名称。

对于你想要的格式,试试

 A.instance_method(:method1).parameters.map(&:last).map(&:to_s) # => ['tuti', 'fruity'] 

当然,这仍然不能让你访问调用者。

我有一个相当昂贵的方法,只有几乎工作。

  $shadow_stack = [] set_trace_func( lambda { |event, file, line, id, binding, classname| if event == "call" $shadow_stack.push( eval("local_variables", binding) ) elsif event == "return" $shadow_stack.pop end } ) def method1( tuti, fruity ) foo end def method2(bim, bam, boom) foo x = 10 y = 3 end def foo puts $shadow_stack[-2].join(", ") end method1(1,2) method2(3,4,4) 

输出

  tuti, fruity bim, bam, boom, x, y 

我很好奇你为什么要这样一个广义的方式这样的function。

我很好奇你怎么看这个function可以自动debugging? 您仍然需要将呼叫注入到您的“foo”function。 事实上,基于set_trace_func东西更能够自动执行,因为您不需要触摸现有的代码。 事实上,这就是debug.rb是如何实现的,就set_trace_func而言。

正如你所概述的那样,确切问题的解决scheme基本上是基本的。 使用调用者+ parsetree,或者打开文件并以这种方式获取数据。 没有任何反思能力,我知道这会让你得到参数的名字。 你可以通过抓取关联的方法对象并调用#arity来推断local_variables是什么参数,但是虽然看起来这个函数的结果是有序的,但我不确定依赖它是否安全。 如果你不介意我问,一旦你有了你所描述的数据和界面,你将如何处理? 自动debugging并不是当我想象使用这个function时最初想到的,虽然也许这是我的想象力的失败。


啊哈!

那么我会采取不同的方式。 现在有几个ruby库可以用来做合同devise,包括ruby-contract,rdbc等等。

另一个select是写一些类似的东西:

  def positive lambda { |x| x >= 0 } end def any lambda { |x| true } end class Module def define_checked_method(name, *checkers, &body) define_method(name) do |*args| unless checkers.zip(args).all? { |check, arg| check[arg] } raise "bad argument" end body.call(*args) end end end class A define_checked_method(:add, positive, any) do |x, y| x + y end end a = A.new p a.add(3, 2) p a.add(3, -1) p a.add(-4, 2) 

输出

  5 2 checked_rb.rb:13:in `add': bad argument (RuntimeError) from checked_rb.rb:29 

当然,这可以做得更加复杂,而且这也是我提到的一些图书馆所提供的,但也许这是一种让你想要去的地方,而不必采取你计划使用的path到达那里?

如果你想要的值的默认值,也有“参数”gem

 $ gem install rdp-arguments $ irb >> require 'arguments' >> require 'test.rb' # class A is defined here >> Arguments.names(A, :go) 

事实上,你所描述的方法显然无法区分参数与局部variables,同时也不能自动工作

那是因为你所要做的不是被支持的东西。 这是可能的(一切都可能在ruby),但没有文件或已知的方式来做到这一点。

要么你可以像logan所build议的那样评估回溯,要么你可以破解你的C编译器并且破解ruby的源代码。 我相当有信心没有其他方法可以做到这一点。