instance_eval with custom definee context

678 Views Asked by At

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
0

There are 0 best solutions below