• 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

これまでの流れ

  1. Method#to_source を作るには Ruby を改造するしかない
  2. それは敷居が高すぎる
  3. MIME定義部分は block で妥協しよう

   1      def show
   2        @user = User.find(params[:id])
   3      end
   4  
   5      show.xml {
   6        render :text => @user.to_xml
   7      }

def と block の併用は、respond_to と同程度にキモイと気付いた

結論

そうだ!アクション定義も block にしよう!

   1      show {
   2        @user = User.find(params[:id])
   3      }
   4  
   5      show.xml {
   6        render :text => @user.to_xml
   7      }

SexyActions plugin

http://github.com/maiha/sexy_actions

動作要件

  • Rails2.x

Rails2.x の scaffold が生成する不細工なコード

   1  class UsersController < ApplicationController
   2    def index
   3      @users = User.find(:all)
   4  
   5      respond_to do |format|
   6        format.html # index.html.erb
   7        format.xml  { render :xml => @users }
   8      end
   9    end
  10  
  11    def show
  12      @user = User.find(params[:id])
  13  
  14      respond_to do |format|
  15        format.html # show.html.erb
  16        format.xml  { render :xml => @user }
  17      end
  18    end
  19  
  20    ...

俺達のセクシーなコード

   1  class UsersController < ApplicationController
   2    include SexyActions
   3  
   4    index      { @users = User.find(:all) }
   5    index.html # index.html.erb
   6    index.xml  { render :xml => @users }
   7  
   8    show      { @user = User.find(params[:id]) }
   9    show.html # show.html.erb
  10    show.xml  { render :xml => @user }
  11  
  12    ...

動作

  • 各アクションの定義は method_missing 経由で得られるproxyオブジェクトが行う
  • 表記の制約により、定義時のコンテキストはコントローラクラスになる
  • 従ってinstanceだけでなくclassに対してもアクション名と同名のメソッドが定義される

まとめ

  • 可読性が上がった (定義内容が1行の場合の美しさは半端ない)
  • 再利用性が上がった (あるMIMEの動作だけ再定義することができる)

致命的な問題

  • new アクションが定義できない事に気付いた><
posted by Png maiha on Tue 29 Jul 2008 at 17:30 with 1 comment

ActionController::Base#respond_to

ActionController の respond_to

  • MIMEタイプに応じて出力を切り替える素晴らしい手段を提供しつつも
  • 使う気をなくさせる表記法(実装)を採用する

というセンスがあるのかないのかわからない機能になっている。

   1    def show
   2      @person = Person.find(params[:id])
   3  
   4      respond_to do |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

可読性は一目瞭然であり、 さきほどのサンプルと比べて以下のメリットを有する。

  1. アクションロジックと描画ロジックの峻別
  2. MIME単位での定義による可読性と再利用性
  3. 特異メソッド定義と拡張子の直感的親和性

特に3番目の、 "show.xml" のレスポンスを直感的に定義(記述)できる点は Rubyならではの機能と言える。

実装方針

   1  class ApplicationController < ActionController::Base
   2    class << self
   3      def method_missing(action, *arguments)
   4        if action_methods.include?(action.to_s)
   5          mime_respond_proxy_for(action)
   6        else
   7          super
   8        end
   9      end
  10  
  11      def mime_respond_proxy_for(action)
  12        @mime_responds[action] ||= MimeRespondProxy.new
  13      end
  14    end

  1. コントローラクラスの method_missing で、"def show.xml" の "show" 部分の未定義変数への参照をトラップ
  2. 定義済みのアクションであれば、Proxyオブジェクトを返す
  3. 以下、"def show.xml" 等の定義は Proxy オブジェクトへの(特異)メソッド追加となる
  4. default_render で、現在のアクションに応じた Proxy オブジェクトを取得し、指定されたmimeタイプの描画処理を行う
  5. コンテキストを合わせるために、aProxy.xml のメソッド定義の内容を実行中のコントローラに定義する
  6. 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 Png maiha on Mon 28 Jul 2008 at 06:48 with 3 comments
Contents
SexyActions plugin
新しい MimeResponds 表記の提案
Comments
maiha: 追記)対応は約1営業日でした。チームの忙しさに応じて変動するとは思いますが、大体の目安情報とい... '10-4
maiha: なるほど。rails のログから実行したクエリを抜き出したいと思うことがよくありまして。 あ... '10-3
winebarrel: レポートありがとうございます。 削除できない件は修正しました。 SQLのparserはTC/... '10-2
maiha: なるほど。ありがとうございます! '10-2
ursm: Haml 2.2 以降はどこでも式展開が使えるようになったので、== は意味がなくなりました。... '10-2
Services from s21g
twpro(ツイプロ)
Twitterプロフィールを快適検索
地価2009
土地の値段を調べてみよう
MyRestaurant
自分だけのレストラン手帳
Formula
ブログに数式を埋め込める数式コミュニティ