這裏的(有些脆弱)黑客:
# 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
要解決這個問題,你需要做一些錯誤的評估字符串內處理:
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
我試過了在我的盒子和喜歡我的答案,它依靠x也在不同的範圍內聲明(這裏如果x = 456沒有說明,它不起作用)。 – 2009-08-22 18:18:41
如果x在調用者的綁定中沒有定義,它不會找到它,只不過是'eval「放x」,binding()'可以在調用者的上下文中工作。看到我的編輯更多。 – rampion 2009-08-23 09:17:53
@Chris:Rampion的'caller_binding'實現沒有特別的適應性。儘管爲了與Ruby 1.8.7一起工作,它需要將「1.8.7」=> -3'添加到它的偏移散列中。 – Chuck 2009-08-23 09:26:08