ActionCont roller::Ba se#respond_to
ActionCont
- MIMEタイプに応じて出力を切り替える素晴らしい手段を提供しつつも
- 使う気をなくさせる表記法(実装)を採用する
というセンスがあるのかないのかわからない機能になっている。
1 def show 2 @person = Person.find(params[:id]) 3 4 respond_todo |format| 5 format.html 6 format.xml { render :xml => @person.to_xml } 7 end 8 end
誰がこんな面倒な記述を各アクションに書いていきたいと思うだろうか。
問題
具体的には以下の問題点が挙げられる。
- やりたい事に対して無駄にコード量が多い
- アクションが肥大化し可読性が低い
- case文と同じで再利用性が低い
提案
上記の問題点を解消するために、 以下のような新しい表記法(実装)を提案する。
1 def show 2 @person = Person.find(params[:id]) 3 end 4 5 def show.xml 6 render :xml => @person.to_xml 7 end
可読性は一目瞭然であり、 さきほどのサンプルと比べて以下のメリットを有する。
- アクションロジックと描画ロジックの峻別
- MIME単位での定義による可読性と再利用性
- 特異メソッド定義と拡張子の直感的親和性
特に3番目の、 "show.xml" のレスポンスを直感的に定義(記述)できる点は Rubyならではの機能と言える。
実装方針
1 class ApplicationControlle r < ActionCont roller::Ba se 2 class << self 3 def method_mis sing(action, *arguments) 4 if action_met hods.include?(action.to_s) 5 mime_respo nd_proxy_f or(action) 6 else 7 super 8 end 9 end 10 11 def mime_respo nd_proxy_f or(action) 12 @mime_respo nds[action] ||= MimeRespon dProxy.new 13 end 14 end
- コントローラクラスの method_mis
sing で、"def show.xml" の "show" 部分の未定義変数への参照をトラップ - 定義済みのアクションであれば、Proxyオブジェクトを返す
- 以下、"def show.xml" 等の定義は Proxy オブジェクトへの(特異)メソッド追加となる
- default_re
nder で、現在のアクションに応じた Proxy オブジェクトを取得し、指定されたmimeタイプの描画処理を行う - コンテキストを合わせるために、aProxy.xml
のメソッド定義の内容を実行中のコントローラに定義する - singleton method bound for a different object (imkk)
反省会
- singleton 以前に、他のメソッド定義を違うクラスに再定義できない
今後の選択肢
- ブロック定義にすれば引き回せるよ (→ "def show.xml" と書ける心地よさが重要)
- UnboundMet
hodを任意のクラス(Object)にbindさせてYO! - Method#to_sourceが欲しい
- Method#modulizeでもいい
- JavaScript
Proxyみたいにする(helperとmethodとcontextをパピコ)
posted by
maiha
on Mon 28 Jul 2008
at 06:48
with
3 comments
一応、Proc#sourceを取り出すのであれば、 この辺なんかが使えそうな気がしますね。
Procだとそれが便利だねぇ。 でもMethodには使えないみたいだ。(to_procするとnilになる)
なるほど、確かにMethodだとだめですね。うーむ。