1

我试图为已经存在的库创建 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,默认定义保持不变

当我们在实例上调用 instance_eval

N.new.instance_eval &some_block
# => #<N:0x007fc0422294f8>
#    true
#    true
#    implicit calling context is N instance

some_block 中的 self 上下文切换到 N 个实例,但默认定义者变为 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
4

0 回答 0