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|} # substitution
  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:0xb7c03de0 @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

posted by Png genki on Thu 17 Jul 2008 at 19:38 with 1 comment

Comments:

Png 瀧内元気 almost 16 years ago.

別解

   1  class Foo
   2    def initialize(&block)
   3      bar = nil
   4      instance_eval(&block)
   5    end
   6  
   7  private
   8    def bar(x) @bar = x end
   9  end
  10  
  11  def bar(x); puts x end
  12  
  13  foo = Foo.new do
  14    bar 'bar'
  15  end

こっちの方がシンプルに記述できますが、 newブロック内で、隠蔽されていた無関係のプライベートメソッドが不用意に呼ばれてしまう可能性があるので、ちょっと微妙な感じですね。

or Preview
Social Bookmarks
  • Delicious
  • B_entry670
  • Clip_16_12_w
Services from s21g
twpro(ツイプロ)
Twitterプロフィールを快適検索
地価2009
土地の値段を調べてみよう
MyRestaurant
自分だけのレストラン手帳
Formula
ブログに数式を埋め込める数式コミュニティ