1

既存の 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 インスタンスに切り替わりますが、デフォルトの定義は 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 のメソッドは呼び出されないため、問題も解決しません。

たぶん、このようなことを行うためのよりエレガントな方法があります。何か案は?

編集:

モジュールから継承されたクラスをデリゲーターとして使用することで、定義コンテキストの切り替えの問題を解決しました。

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