美しいコードとは

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: ,

モデルが必ず必要とする値はどう渡せばいいのだろうか?

悩む.....


普通の場合は、初期化の際に値を渡したりするのだろうけど、
Railsのモデルの場合、クラスメソッドを使ってしまっては初期化なんてことができない。
すべてのメソッドの引数に渡すのだろうか?
それとも、クラス変数を設定する??

どーしたらいいのかなぁ。

例えば、
SNSなんかの場合、何を検索するにしても条件にユーザーIDが必要となってくると思う。
この場合、いちいちユーザーIDを渡すのも面倒なんでクラス変数にあらかじめ渡しておいてそれを使いまわすっていうやり方はどうなんだろう?

邪道なんだろうか。

ドッグフードの"ペディグリー"のCM犬が可愛い

最近TVCM「犬の視点」に登場した犬がかわゆいっす。
ちょっと犬種がわからなかったんだけど、どーやら「ビーグル」みたい。

ほふく前進する姿もいい!!
いつか飼ってみたいなー


動画リンク 犬の視点

with_scopeってあんまり使えない気がする・・・

ActiveRecord::Base.with_scope(method_scoping = {}) {|| ...}


必ず指定したい条件をうまーくまとめてくれるのがすばらしくいいと思うんだけど
なぜにjoin条件はwith_scopeで指定したものしかだめなんだろう。ブロックの中で個々の検索条件に書いたjoinは無視されてしまうようになってるんだよねぇ。

複数joinしたいときは多いと思うんだけどなあ