Categories
open all | close allTags
rake | Subversion | JustPosted | タグ | rubygems | Flash | ドキュメント | デュアル・コア | 認証 | Migration | テスト | スキンエンジン | Aptana | パソコン | 国際化 | RESTful | カテゴリ | CSRF | フォーム | 名称Search
クロージャを使うメソッドをかっこよくする
ツリー型カテゴリーの表示など,ロジックが必要な表示ルーチンで,ロジックと表示そのものの部分を分けるためにクロージャを使う話は以前に書きました。そのときのロジック側(Blogモデル)はこうなってました。
def category_tree
tempbuffer = ""
level = 0
self.categories.inject(nil) do |last_right, cat|
# cat is under the last leaf
if !last_right || (last_right > cat.cleft)
level += 1
tempbuffer += yield(:level_up, level)
#
elsif (last_right + 2 <= cat.cleft)
tempbuffer += yield(:item_close, level)
(last_right+2..cat.cleft).each do |i|
level -= 1
tempbuffer += yield(:level_down, level)
end
else
tempbuffer += yield(:item_close, level)
end
tempbuffer += yield(:item, level, cat)
last_right = cat.cright
end
if (level > 1)
tempbuffer += yield(:level_down, level)
end
(level..1).each do |i|
tempbuffer += yield(:item_close, level)
end
tempbuffer += yield(:level_down, level)
return tempbuffer
end
一方,呼び出し側(パーサ内のparse_categorylist)は
def parse_categorylist params
blog = params[2] ? Blog.find_by_bname(params[2]) : @controller.blog
template = _template(params[1])
bd = blog.basic_data
data = Hash.new
blog.category_tree do |type, level, cat|
data['level'] = level.to_s
case type
when :level_up
fill(template['CATLIST_HEADER'], bd.merge(data))
when :level_down
fill(template['CATLIST_FOOTER'], bd.merge(data))
when :item
data['catlink'] = url_for(:controller=>'categories', :catid=>cat.catid, :action=>'show')
fill(template['CATLIST_LISTITEM'], data.merge(cat.attrs))
when :item_close
fill(template['CATLIST_LISTITEM_END'], bd.merge(data))
end
end
end
これで実用上は不自由ないのですが,どうも呼び出し側があまりかっこよくない。case文で場合分けというのが「ださい」感じがします。yieldで呼び出されるたびにこのcase文を通るのが効率的にももう一つ。ここの部分を
blog.category_tree do |cat|
cat.level_up { |level|
data['level'] = level.to_s
fill(template['CATLIST_HEADER'], bd.merge(data))
}
cat.level_down { fill(template['CATLIST_FOOTER'], bd.merge(data)) }
cat.item { |cat|
data['catlink'] = url_for(:controller=>'categories', :catid=>cat.catid, :action=>'show')
fill(template['CATLIST_LISTITEM'], data.merge(cat.attrs))
}
cat.item_close { fill(template['CATLIST_LISTITEM_END'], bd.merge(data)) }
end
と書けたら,かなり「Railsっぽい」感じになります。で,やってみました。
こういう形で書くためには,何かオブジェクトを返してあげて,itemとかlevel_upというメソッドがあればいいわけです。そこでBlogクラスの中に
class Cattree
attr_reader :up_block, :down_block, :item_block, :close_block
def level_up(&b)
@up_block = b
end
def level_down(&b)
@down_block = b
end
def item(&b)
@item_block = b
end
def item_close(&b)
@close_block = b
end
end
とCattreeという内部クラスを作り,ここで与えられたブロックをインスタンス変数に保存しておきます。ブログのcategory_treeメソッド内で,これを呼び出せばいいわけです。
def category_tree2(&block)
tempbuffer = ""
level = 0
c = Cattree.new
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.up_block.call(level)
#
elsif (last_right + 2 <= cat.cleft)
tempbuffer += c.close_block.call
(last_right+2..cat.cleft).each do |i|
level -= 1
tempbuffer += c.down_block.call
end
else
tempbuffer += c.close_block.call
end
tempbuffer += c.item_block.call(cat)
last_right = cat.cright
end
if (level > 1)
tempbuffer += c.down_block.call
end
(level..1).each do |i|
tempbuffer += c.close_block.call
end
tempbuffer += c.down_block.call
return tempbuffer
end
ロジック部分の構成は同じですが,その前にcという名前でCattreeオブジェクトをつくり,parse_categorylistで与えられたブロックを実行しています。そして,それぞれ割り当てられたブロックを表示ロジックの中から呼んでいます。
これで動作としては,以前のものと同じになりました。
デザイン・パターンに詳しいと,こういうのもすぐに分かるんでしょうかねえ。
Comments
No comments yet. You can be the first!