Kagemushaを使って、スコープを汚さないDSLスタイルのinitialize
メソッドを提供する方法を紹介します。
ruby>>
require 'rubygems'
require 'kagemusha'
class Foo
def initialize(&block)
bar = nil # default value
meta = eval('class << self; self end', block.binding)
Kagemusha.new(meta) do |kage|
kage.def(:bar){|bar|} # substitution
end.swap(&block)
@bar = bar
end
end
def bar(x); puts x end
foo = Foo.new do
bar 'bar'
end
puts foo.inspect # => #<Foo:0xb7c03de0 @bar="bar">
bar "hello!" # => hello!
<<--
bindingのコンテクストからメタクラスを取得し、
Kagemushaを使って限定的にbarメソッドを置き換えています。Foo.newのブロック内から抜けると、barの呼び出しはグローバルスコープのbarを呼んで、
"hello!"が出力されます。
この方法は、initialize以外にも、
ブロックを受けるインスタンスメソッド全般に利用できます。
例えば、以下のようなコードで、一時変数t
の存在が気になる場合などですね。
rails>>
create_table do |t|
t.string :foo
end
<<--
ruby>>
class Foo
def initialize(&block)
bar = nil
instance_eval(&block)
end
private
def bar(x) @bar = x end
end
def bar(x); puts x end
foo = Foo.new do
bar 'bar'
end
<<--
こっちの方がシンプルに記述できますが、
`new`ブロック内で、隠蔽されていた無関係のプライベートメソッドが不用意に呼ばれてしまう可能性があるので、ちょっと微妙な感じですね。