Kagemushaを使って、スコープを汚さないDSLスタイルのinitialize
1 require 'rubygems' 2 require 'kagemusha' 3 4 class Foo 5 def initialize(&block) 6 bar = nil # default value 7 meta = eval('class << self; self end', block.binding) 8 Kagemusha.new(meta) do |kage| 9 kage.def(:bar){|bar|} # substituti on 10 end.swap(&block) 11 @bar = bar 12 end 13 end 14 15 def bar(x); puts x end 16 17 foo = Foo.new do 18 bar 'bar' 19 end 20 21 puts foo.inspect # => #<Foo:0xb7c0 3de0 @bar="bar"> 22 bar "hello!" # => hello!
bindingのコンテクストからメタクラスを取得し、
Kagemushaを使って限定的にbar
メソッドを置き換えています。
Foo.new
のブロック内から抜けると、
bar
の呼び出しはグローバルスコープのbar
を呼んで、
"hello!"が出力されます。
この方法は、initialize
以外にも、
ブロックを受けるインスタンスメソッド全般に利用できます。
例えば、以下のようなコードで、一時変数t
の存在が気になる場合などですね。
1 create_table do |t| 2 t.string :foo 3 end
別解
こっちの方がシンプルに記述できますが、
new
ブロック内で、隠蔽されていた無関係のプライベートメソッドが不用意に呼ばれてしまう可能性があるので、ちょっと微妙な感じですね。