gem binding_of_caller
有一个有关如何在父作用域中设置变量的示例:
(这只是从他们的自述文件中粘贴的)
def a
var = 10
b
puts var
end
def b
c
end
def c
binding.of_caller(2).eval('var = :hello')
end
a()
# OUTPUT
# => hello
这很有用,但它受到需要在字符串中进行所有变量初始化的限制。
我想了一下,意识到我可以使用 YAML 来序列化/反序列化对象。
以下面的例子为例:
def c
value = YAML.dump [ { a: "b" } ]
binding.of_caller(2).eval("var = YAML.load('#{value}')")
end
a()
# => {a: "b"}
这很酷,但是如果我可以完全避免序列化并只编写适当的 do; 那就更好了end;
像这样阻塞:
# doesnt work
def c
binding.of_caller(2).eval do
# ideally this would set the variable named "var" in the scope of method "a"
var = [ { a: "b" } ]
end
end
如何实现最后一个示例的功能?如果有其他方法,我不需要使用 binding_of_caller
。
最佳答案
这是我能做的最好的事情,而且我怀疑(尽管我真的很想被证明是错误的),如果你不编写自己的 C 扩展,那么你会发现最好的事情是:
require 'binding_of_caller'
module BindingExtensionEvalBlock
def eval_block(&block)
eval("ObjectSpace._id2ref(%d).call(binding)" % block.object_id)
end
end
class ::Binding
include BindingExtensionEvalBlock
end
当然,魔法就在这里:
eval("ObjectSpace._id2ref(%d).call(binding)" % block.object_id)
我们获取 Proc 的对象 ID,然后在 Binding#eval
中使用 ObjectSpace#_id2ref
从内存中的任何位置检索它并调用它,传入本地绑定(bind)
。
这是在行动:
def a
var = 10
b
puts var
end
def b
c
end
def c
binding.of_caller(2).eval_block do |bnd|
bnd.local_variable_set(:var, [ { a: "b" } ])
end
end
a # => {:a=>"b"}
如您所见,我们必须在 block 中执行 bnd.local_variable_set(:var, [ { a: "b"),而不是
。 Ruby 中无法更改 block 的绑定(bind),因此我们必须传入绑定(bind) (var = [ { a: "b"} ]
“} ])bnd
) 并调用 Binding#local_variable_set
.
关于ruby - 使用 block 在父作用域中设置变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37125272/