我试图在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)中是不可能的?
答案 0 :(得分:6)
这是一个(有点脆弱)的黑客攻击:
# 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
答案 1 :(得分:3)
如果使用块调用该方法,则可以通过block.binding
获取块的绑定(关闭调用者的绑定)。但是,如果没有阻止,这是行不通的。
你无法直接获得调用者的绑定(好吧,除非你当然明确地传递它)。
编辑:我应该补充说,有一个Binding.of_caller方法浮动,但这不再适用于任何最近的ruby版本(最近包括1.8.6)
答案 2 :(得分:2)
这可能比你想要的有点麻烦,但这是我能够做到的一种方式。
#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.new
和define_method
调用替换类和方法定义只是工作的一半。不幸的是,丑陋的部分是它只有在事先定义x
时才有效,所以你并没有真正抓住调用者的绑定(而是被调用者在不同的范围内修改变量)。
这可能等同于将所有变量定义为全局变量,但这可能对您有用,具体取决于您的情况。也许有了这个,你就可以在手中找到最后一块拼图(如果这对你不起作用)。
编辑:您可以按照以下方式获取任何方法的绑定,但即使使用它,我也无法eval
成功(请务必将其放在最佳)。这将填充@@binding
some_method
和method_missing
的绑定(按此顺序),所以这可能会有所帮助。
@@binding = []
class Class
alias real_def define_method
def define_method(method_name, &block)
real_def method_name, &block
@@binding << block.binding
end
end