如何从method_missing获取绑定?

我试图在Ruby(1.8)中的method_missing中找到一种从调用者获取绑定的方法,但我似乎无法找到一种方法来实现它。

希望以下代码解释我想要做的事情:

class A def some_method x = 123 nonexistent_method end def method_missing(method, *args, &block) b = caller_binding # <---- Is this possible? eval "puts x", b end end A.new.some_method # expected output: # 123 

那么…有没有办法获得调用者的绑定,或者这在Ruby(1.8)中是不可能的?

这是一个(有点脆弱)黑客攻击:

 # caller_binding.rb TRACE_STACK = [] VERSION_OFFSET = { "1.8.6" => -3, "1.9.1" => -2 }[RUBY_VERSION] def caller_binding(skip=1) TRACE_STACK[ VERSION_OFFSET - skip ][:binding] end set_trace_func(lambda do |event, file, line, id, binding, classname| item = {:event=>event,:file=>file,:line=>line,:id=>id,:binding=>binding,:classname=>classname} #p item case(event) when 'line' TRACE_STACK.push(item) if TRACE_STACK.empty? when /\b(?:(?:c-)?call|class)\b/ TRACE_STACK.push(item) when /\b(?:(?:c-)?return|end|raise)\b/ TRACE_STACK.pop end end) 

这适用于您的示例,但我还没有用其他方法测试它

 require 'caller_binding' class A def some_method x = 123 nonexistent_method end def method_missing( method, *args, &block ) b = caller_binding eval "puts x", b end end x = 456 A.new.some_method #=> prints 123 A.new.nonexistent_method #=> prints 456 

当然,如果绑定没有定义您要评估的变量,这将不起作用,但这是绑定的一般问题。 如果未定义变量,则不知道它是什么。

 require 'caller_binding' def show_x(b) begin eval <<-SCRIPT, b puts "x = \#{x}" SCRIPT rescue => e puts e end end def y show_x(caller_binding) end def ex1 y #=> prints "undefined local variable or method `x' for main:Object" show_x(binding) #=> prints "undefined local variable or method `x' for main:Object" end def ex2 x = 123 y #+> prints "x = 123" show_x(binding) #+> prints "x = 123" end ex1 ex2 

要解决这个问题,您需要在评估的字符串中进行一些error handling:

 require 'caller_binding' def show_x(b) begin eval <<-SCRIPT, b if defined? x puts "x = \#{x}" else puts "x not defined" end SCRIPT rescue => e puts e end end def y show_x(caller_binding) end def ex1 y #=> prints "x not defined" show_x(binding) #=> prints "x not defined" end def ex2 x = 123 y #+> prints "x = 123" show_x(binding) #+> prints "x = 123" end ex1 ex2 

如果使用块调用该方法,则可以通过执行block.binding来获取块的绑定(关闭调用者的绑定)。 但是,如果没有阻止,这不起作用。

你不能直接得到调用者的绑定(好吧,除非你当然明确地传递它)。

编辑:我应该补充一点,有一个Binding.of_caller方法浮动,但这不再适用于任何最近的ruby版本(最近包括1.8.6)

这可能比你想要的有点麻烦,但这是我能够做到的一种方式。

 #x = 1 # can uncomment out this and comment the other if you like A = Class.new do x = 1 define_method :some_method do x = 123 nonexistent_method end define_method :method_missing do |method, *args| puts x end end A.new.some_method 

但是,使用Class.newdefine_method调用替换类和方法定义只是工作的一半。 不幸的是,丑陋的部分是它只有在你事先已经定义了x之后才有效,所以你并没有真正抓住调用者的绑定(而是被调用者在不同的范围内修改变量)。

这可能等同于将所有变量定义为全局变量,但这可能对您有用,具体取决于您的情况。 也许有了这个,你将能够找到手头这一变化的最后一块拼图(如果这对你不起作用)。

编辑:你可以得到如下任何方法的绑定,但即使有它,我也无法成功eval (一定要把它放在顶部)。 这将填充@@bindingsome_methodmethod_missing的绑定( some_method顺序),所以也许这可以帮助某种方式。

 @@binding = [] class Class alias real_def define_method def define_method(method_name, &block) real_def method_name, &block @@binding << block.binding end end