Im trying to create DSL customization for already existing lib and i have some misunderstanding about Ruby block contexts.
Suppose we have a block saved as 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
When we simply yield a block self context of this block doesn't change
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
When we call
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 in this block becomes N, default definee stays the same
When we call instance_eval on instance
N.new.instance_eval &some_block
# => #<N:0x007fc0422294f8>
# true
# true
# implicit calling context is N instance
self context in some_block switches to N instance, but default definee becomes N instances metaclass
Is there any convenient way to yield block within context of instance and proxy definee context somewhere else?
For example i have Delegator instance with some class within and i want to proxy definee context to it:
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.
So Definee has already implemented DSL and i cover it with instance_eval, but definition context is incorrect. Class_eval will be delegated to Definee and no methods of MyDelegator will be called so it also doesn't solve problem.
Maybe there is some more elegant way to do something like this. Any ideas?
EDIT:
Solved my problem of definition context switching with using class inherited from Module as delegator.
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