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