既存の 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