Hier ist eine (etwas zerbrechlich) hacken:
# 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)
Das mit Ihrem Beispiel funktioniert, aber ich habe es nicht mit viel mehr
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
Natürlich getestet, das gewonnen Es funktioniert nicht, wenn die Bindung die Variable, die Sie bewerten möchten, nicht definiert. Dies ist jedoch ein generelles Problem bei Bindungen. Wenn eine Variable nicht definiert ist, weiß sie nicht, was sie ist.
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
Um dies zu umgehen, müssen Sie innerhalb der ausgewerteten String Handling einige Fehler tun:
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
Ich habe es auf meiner Box ausprobiert und mag meine Antwort, darauf baut man auf x wird auch in einem anderen Bereich deklariert (hier, wenn x = 456 nicht angegeben ist, funktioniert es nicht). –
Wenn x in der Bindung des Aufrufers nicht definiert ist, wird es nicht gefunden, nicht mehr als "eval" puts x ", binding()" würde im Kontext des Aufrufers funktionieren. Siehe meine Bearbeitung für mehr. – rampion
@Chris: Rampions 'caller_binding' Implementierung funktioniert ohne spezielle Anpassungen. Allerdings muss "1.8.7" => -3' zu seinem Offset-Hash hinzugefügt werden, um mit Ruby 1.8.7 zu arbeiten. – Chuck