Ruby 1.9でmodule_evalとブロック付きメソッド呼び出しの回避策
Ruby 1.9のmodule_evalとブロック付きメソッド呼び出しの件で、回避策を発見しました。
問題の挙動を詳しく見てみると、↓こういうことみたいです。
# メソッドを追加されるクラス class Test end # 直接module_evalをブロック付きメソッド呼び出し # => OK Test.module_eval { def test1 p :test1 end } Test.new.test1 #=> :test1 # メソッド付き呼び出しで作ったProcでmodule_evalをメソッド付き呼び出し # => NG!! def test2(&prc) Test.module_eval(&prc) end test2 { def test2 p :test2 end } Test.new.test2 #=> NoMethodErrorになる # Procオブジェクトをmodule_evalをブロック付きメソッド呼び出し # => OK prc = Proc.new { def test3 p :test3 end } Test.module_eval(&prc) Test.new.test3 #=> :test3 # Procオブジェクトをメソッドに渡してからmodule_evalをブロック付きメソッド呼び出し # => NG!! def test4(prc) Test.module_eval(&prc) end prc = Proc.new { def test4 p :test4 end } test4(prc) Test.new.test4 #=> NoMethodErrorになる # module_evalをProcのbindingで実行 # => OK def test5(prc) Kernel.const_set(:GLOBAL_ACCESSABLE, prc) prc.binding.eval "Test.module_eval(&::GLOBAL_ACCESSABLE)" Kernel.module_eval { remove_const(:GLOBAL_ACCESSABLE) } end prc = Proc.new { def test5 p :test5 end } test5(prc) Test.new.test5 #=> :test5 # ブロック付きメソッド呼び出しで作ったProcのbindingでmodule_eval # => OK def test6(&prc) Kernel.const_set(:GLOBAL_ACCESSABLE, prc) prc.binding.eval "Test.module_eval(&::GLOBAL_ACCESSABLE)" Kernel.module_eval { remove_const(:GLOBAL_ACCESSABLE) } end test6 { def test6 p :test6 end } Test.new.test6 #=> :test6
何が何だか良く分からないのですが、とりあえずtest6の方法で希望の動作になるようです。
ここでは::GLOBAL_ACCESSABLEという定数を使っていますが、これはどこからでもアクセスできる位置であればどこでもよくて、Objectのクラス変数でも、適当な名前空間(Module)を作ってそこに定義してもOKです。(要するにProc#bindingの中でそのprocを呼べればいい)