メタプログラミングで呼び出される側のプログラムをきれいにする

前のエントリーで書いた方法,確かに呼び出し側はきれいになるのですが,呼び出しを受ける側がちょっと気になってました。具体的に言うとCattreeというクラスを作って,そこでクロージャを保存するためのメソッド(すなわち呼び出しに使われるメソッド),保存するインスタンス変数,インスタンス変数を呼び出すためのattr_readerを書かなければいけません。

カテゴリーツリーだけの話ならいいのですが,同じような形を利用できるものは現在用意しているだけでもタグクラウド,ページスイッチとあります。このとき,それぞれについて上記のCattreeのようなクラスを作るのが結構面倒です。同じようなことばかり何度も書くので全然DRY(Don't Repeat Yourself)になっていません。

そこで,この受け皿の部分をDRY化する方法を考えました。

まず,次のようなモジュールBlocksContainer(ファイル名はblocks_container.rb)を作ります。
module BlocksContainer
  def method_missing(msg, *arg, &block)
    varname = ('@'+msg.to_s).to_sym
    if b = instance_variable_get(varname)
      b.call(*arg)
    elsif block
      instance_variable_set(varname, block)
    else
      raise NameError
    end
  end
end

ここではmethod_missingというメソッドを作っていますが,これはメソッドが見つからなかったときに自動的に呼び出されるメソッドです。ここでは,そのメソッド名と同じ名前のインスタンス変数があったらそれをブロックとみなして実行し,インスタンス変数がなくて,ブロックが与えられていたらメソッド名と同じインスタンス変数を作ってブロックを保存します。そうでない場合はメソッドが見つからないものとしてNameErrorを起こします。

これを使う側は次のようになります。
  def category_tree(&block)
      tempbuffer = ""
      level = 0
      c = Object.new.extend BlocksContainer
      block.call(c)
      
      self.categories.inject(nil) do |last_right, cat|
         
        # cat is under the last leaf
        if !last_right || (last_right > cat.cleft)
          level += 1
          tempbuffer += c.level_up(level)
        
        elsif (last_right + 2 <= cat.cleft)
          tempbuffer += c.item_close
          (last_right+2..cat.cleft).each do |i|
            level -= 1
            tempbuffer += c.level_down
          end
        else
          tempbuffer += c.item_close
        end
        tempbuffer += c.item(cat)
        last_right = cat.cright
      end
      
      if (level > 1)
        tempbuffer += c.level_down
      end
      (level..1).each do |i|
        tempbuffer += c.item_close
      end
      tempbuffer += c.level_down
      return tempbuffer
  end

前と違うのは
c = Object.new.extend BlocksContainer
として新しいオブジェクトに上記の機能を加えていること。
また,ブロックの呼び出しも,前は
down_block.call
といった形だったのが
level_down
だけで実行するようになります。これで,いちいち新しいクラスを作らなくても済むようになりました。


05 Sep, 2008 | Foodyn ( つぶやき ) , Rails | | Andy
« Prev item - Next Item »
---------------------------------------------

Comments


No comments yet. You can be the first!


Leave comment

© 2007 yoursite.com | Designed by DesignsByDarren
Ported to Nucleus CMS: Suvoroff