我试图为现有的lib创建DSL自定义,我对Ruby块上下文有一些误解。
假设我们将一个块保存为proc
some_block = Proc.new do
def testing; end
puts self.inspect
if self.kind_of? Class
puts self.instance_methods.include? :testing
else
puts self.methods.include? :testing
end
puts self.singleton_class.instance_methods.include? :testing
implicit_calling_context
end
def implicit_calling_context
"implicit calling context is definition context"
end
当我们简单地产生一个块时,这个块的自我上下文不会改变
class N
def some_method
yield
end
def self.implicit_calling_context
puts "implicit calling context is N"
end
def implicit_calling_context
puts "implicit calling context is N instance"
end
end
N.new.some_method &some_block
# => main # block self context stays definition one (closure)
# false
# false
# implicit calling context is definition context
当我们打电话时
N.class_eval &some_block
# => N # block self context changed to N
# true # block definition context became N
# false
# implicit calling context is N
此块中的self变为N,默认的definee保持不变
当我们在实例
上调用instance_eval时N.new.instance_eval &some_block
# => #<N:0x007fc0422294f8>
# true
# true
# implicit calling context is N instance
some_block中的自身上下文切换到N实例,但默认的definee变为N个实例元类
有没有方便的方法在实例和代理定义上下文的上下文中产生块?
例如,我有一个Delegator实例,其中包含一些类,我想代理它的定义上下文:
class Definee
end
class MyDelegator < SimpleDelegator
def work *args
puts "some additional work"
__getobj__.work *args
end
end
MyDelegator.new(Definee).instance_eval do
work "some base work"
def test_my_work
"should be defined on Definee"
end
end
# I expecting that test_my_work should be defined as Definee instance method
# and :work should be called on MyDelegator.new(Definee) instance
# with "some base work" arg.
所以Definee已经实现了DSL,我用instance_eval覆盖它,但定义上下文不正确。 Class_eval将被委托给Definee,并且不会调用MyDelegator的任何方法,因此它也无法解决问题。
也许有更优雅的方式来做这样的事情。有什么想法吗?
编辑:
解决了我使用继承自Module的类作为委托者来定义上下文切换的问题。
class Definee
end
class MyDelegator < Module
def initialize definee, &block
@definee = definee
self.class_eval &block
@definee.include self
end
def work *args
puts "some additional work"
@definee.work *args
end
def method_missing *args, &block
@definee.send *args, &block
end
end
MyDelegator.new(Definee) do
work "some base work"
def test_my_work
"should be defined on Definee"
end
end