如果有可能,任何人都知道如何让它发挥作用吗?
class Foo
def self.go(&block)
class << block
include Bar
end
puts "Within Foo#go: #{block.methods.include? 'baz'}"
block.call
end
end
module Bar
def baz
puts "I'm happily in my place!"
end
end
Foo.go {
puts "Within actual block: #{methods.include? 'baz'}"
baz
}
这得到输出:
Within Foo#go: true
Within actual block: false
NameError: undefined local variable or method ‘baz’ for main:Object
编辑:当我在Foo#中打印出该块的类时,它是Proc,但当我在Proc中打印它时,它就是Object。这可能是相关的吗?
答案 0 :(得分:3)
你不能这样做。你所看到的原因是这里有两种不同的背景。一个是块的上下文,closes over它定义的上下文。另一个是Proc对象包装器的上下文,它与任何其他对象上下文完全相同,并且与块本身的上下文完全无关。
我认为你最接近的是使用具有你想要的方法的上下文对象来instance_eval
块,但是该块将无法访问存在于哪里的self
它被定义了。是否对你想要写的方法有意义,取决于你。
另一种选择是将块传递给baz
方法的实际接收器。
答案 1 :(得分:3)
您可以将eval
与Proc#binding
:
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
eval('include Bar', block.binding)
block[]
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> "hi from baz!"
但除非你使用mixin / mixout框架(如mixico或mixology),否则你将把包含模块中的方法放入词法范围,这样一旦块返回它们仍然可以访问。
require 'rubygems'
require 'mixico'
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
Module.mix_eval(Bar, &block)
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> NameError
Here是关于在一个区块内使用DSL的不同方法的好文章。
答案 2 :(得分:2)
另一个替代方案是继续加速之后,在混合之前复制块的上下文,这样你就不会在完成后弄乱上下文。
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
dup_context = eval('self', block.binding).dup
dup_context.send(:include, Bar)
dup_context.instance_eval &block
end
注意,如果你没有在块
中运行任何mutator方法,这只对你有用