美しいコードとは
Beautiful Code: The evolution of an iterator - (37signals)
DHHがコードを添削している。英語ぜんぜんだめだからいまいち理解できないなー。
def self.each_state STATES.each { |state| yield state } end
この部分を、
def self.states @states ||= [PENDING, PROCESSING, COMPLETED] end
をこう添削している。わざわざeachメソッドを定義していたのを配列を返すことによってeachでもcollectでもなんでもござれにしている。これは納得の添削だね。
しかし、もっと気になる部分はclass_evalを使ってモデルの状態を定義している部分。
class Export < ActiveRecord::Base PENDING = "pending" PROCESSING = "processing" COMPLETED = "completed" def self.states @states ||= [PENDING, PROCESSING, COMPLETED] end # ... end class ExportPresenter # ... Export.states.each do |state| class_eval "def #{state}?; @export && @export.#{state}?; end" end # ... end
Export.states.each do |state| class_eval "def #{state}?; @export && @export.#{state}?; end" end
class_evalを活用してより柔軟にコードを記述する。
プログラムを組む際に気をつけよう。
JavaScriptでTreemap
著名なサイト『百式』さんで検索結果をマップ形式で表示する検索サイトの紹介(リストからマップへ)がされていて、
なんか新しいなーって思った。TagCloudと同じで視覚で結果をうったえるってわかりやすくていいよね。
PHP でtreemapの実装をされている方(Treemap PHP Source Code - neurofuzzy)がいて、コードも公開されていたのでアルゴリズムはそのままに(真似してます)JavaScriptで実装してみた。
//■JavaScript //require Prototype.js function render_treemap(nodes) { var divTmp = new Template('<div class="node" style="width:#{width}px;height:#{height}">#{tag}</div>'); return (function(nodes, width, height, depth) { if (nodes.length == 1) { var textsize = Math.floor(width / nodes[0].label.length); textsize = Math.min(textsize, height); return '<a class="textnode" href="#" style=" font-size:'+textsize+'px">'+nodes[0].label+'</a>'; } var tag = ''; if (depth == 0) tag += '<div class="treemap" style="width:'+width+'px; height:'+height+'px;">'; var m = Math.ceil(nodes.length / 2); var a = nodes.slice(0, m); var b = nodes.slice(m, nodes.length); var aper = a.inject(0, function(ret, v) { return ret + v.size }) / nodes.inject(0, function(ret, v) { return ret + v.size }); var bper = 1 - aper; if (depth % 2 == 0) { var awidth = Math.ceil(width * aper); var bwidth = width - awidth; var aheight = height; var bheight = height; } else { var awidth = width; var bwidth = width; var aheight = Math.ceil(height * aper); var bheight = height - aheight; } tag += divTmp.evaluate({tag: arguments.callee.call(null, a, awidth, aheight, depth+1), width: awidth, height: aheight}); tag += divTmp.evaluate({tag: arguments.callee.call(null, b, bwidth, bheight, depth+1), width: bwidth, height: bheight}); if (depth == 0) tag += '</div>'; return tag; }).call(null, nodes, 300, 200, 0); } //■CSS div.treemap { margin-bottom: 4px; font-family: Lucida Grande, Lucida Sans Unicode, Tahoma, Arial, sans serif; } div.treemap div.node { float: left; clear: none; background-color: #CCCC00; } div.treemap a.textnode { display: block; font-size: 10px; text-align: center; width: 100%; height: 100%; display: block; float: left; color: #fff; overflow: hidden; background-color: #A4C088; text-decoration: none; } a.textnode { border: 1px solid black; }
使い方はこんな感じ
var n = [{label:'りんご', size: 20}, {label:'オレンジ', size: 30}, {label:'グレープ', size: 80}, {label:'いちご', size: 15}, {label:'キューイ', size: 40}, {label:'バナナ', size: 5}]; $('panel').innerHTML = render_treemap(n);
まだ、重要度によって色を変えたりだとかが実装されてないんだよね。
TagCloudみたいな感じでclassNameを振っていこうかなと思ってる。
Tags: ajax, javascript
with_scopeってあんまり使えない気がする・・・
ActiveRecord::Base.with_scope(method_scoping = {}) {|| ...}
必ず指定したい条件をうまーくまとめてくれるのがすばらしくいいと思うんだけど
なぜにjoin条件はwith_scopeで指定したものしかだめなんだろう。ブロックの中で個々の検索条件に書いたjoinは無視されてしまうようになってるんだよねぇ。
複数joinしたいときは多いと思うんだけどなあ