Ruby 使用块在父范围中设置变量
gem提供了一个如何在父作用域中设置变量的示例: (这是刚从自述中粘贴的) 这很有用,但由于需要在字符串中进行所有变量初始化,因此受到限制 我仔细考虑了一下,意识到我可以使用YAML来序列化/反序列化对象 以下面的例子为例:Ruby 使用块在父范围中设置变量,ruby,metaprogramming,Ruby,Metaprogramming,gem提供了一个如何在父作用域中设置变量的示例: (这是刚从自述中粘贴的) 这很有用,但由于需要在字符串中进行所有变量初始化,因此受到限制 我仔细考虑了一下,意识到我可以使用YAML来序列化/反序列化对象 以下面的例子为例: def c value = YAML.dump [ { a: "b" } ] binding.of_caller(2).eval("var = YAML.load('#{value}')") end a() # => {a: "b"} 这很酷,但如果我可以
def c
value = YAML.dump [ { a: "b" } ]
binding.of_caller(2).eval("var = YAML.load('#{value}')")
end
a()
# => {a: "b"}
这很酷,但如果我可以完全避免序列化,只编写一个适当的do;结束代码>像这样的块:
# 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
如何实现最后一个示例的功能?如果有其他方法,我不需要使用调用者的绑定 这是我能做的最好的了,而且,我怀疑(尽管我真的很想被证明是错误的),除了编写自己的C扩展名a la binding_of_调用者之外,你能找到的最好的方法是:
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)
我们获取进程的对象ID,然后在我们的绑定#eval
中,使用从内存中的任何位置检索它并调用它,传入本地绑定
这就是它的作用:
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"}
如您所见,在我们的块中,我们必须执行bnd.local\u variable\u set(:var,[{a:b}])
,而不是var=[{a:b}]
。在Ruby中无法更改块的绑定,因此我们必须传入一个绑定(bnd
)并调用。你有问题吗?@Jordan I添加了一个明确的问题
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"}