这里有一个(比较脆弱的)技巧:
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}
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
A.new.nonexistent_method
当然,如果绑定没有定义您要评估的变量,则此方法将无法运行,但这是与绑定相关的一般问题。如果未定义变量,则不知道它是什么。
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
show_x(binding)
end
def ex2
x = 123
y
show_x(binding)
end
ex1
ex2
为了解决这个问题,您需要在评估的字符串内部进行一些错误处理:
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
show_x(binding)
end
def ex2
x = 123
y
show_x(binding)
end
ex1
ex2
eval "puts x", binding()
一样。请参见我的编辑以获取更多信息。 - rampioncaller_binding
实现不需要任何特殊的适应措施。但是,为了让它能够与Ruby 1.8.7一起使用,需要将"1.8.7" => -3
添加到其偏移哈希中。 - Chuck