Edge Rails における Controller#respond_to

Rails 1.1.2における Controller#respond_to の機能は くまくまーの人がまとめているのを参考にすればよいのですが,Edge RailsではRubyKaigi2006のDHHの講演でも触れられていたようにさらに機能が追加されています.
ところで Controller#respond_to の機能をおさらいすると,「同じ処理を入力/出力で異なる形式のデータを介して行う」ためのメソッドで,1つのアクションの中で簡潔にこの出力の切り替えを行うことができるメソッドです.
というわけで全体的にくまくまの人の記事をパクり^h^h^hリスペクトしつつ,主に追加部分について書いてみます.

概要

  1. params[:format] を見る
    1. でなければHTTP1.1 リクエストヘッダ中の Accept フィールドを見る
  2. Content-type に応じてリクエストのデータ形式を自動変換してくれる
  3. 従って,コントローラは同じロジック(action)で対応できる
  4. MIMEタイプに応じて実行する描画処理を指定できる
  5. "*/*" が指定された場合は respond_to の最初の定義を実行する

要するに params[:format] を見る部分だけが増えてます.(あとは対応するMIMEタイプが増えてるとか後付が簡単になったとか)

書式

Controller
  respond_to do |format|
    format.html
    format.js
    format.xml  { render :action => "#{action_name}.rxml" }
    format.yaml { render :text => @obj.to_yaml }
  end

指定しなければ html ならば { render }, js ならば { render :action => "#{action_name}.rjs" }, xml ならば { render :action => "#{action_name}.rxml" } を指定したのと同等です.(そのほかの形式では今のところ省略できません.)

config/routes.rb

GETで ?format=xml を付けるのもいいんですが,せっかくなので routes.rb で拡張子っぽく書けるようにしましょう.

  map.connect ':controller/:action/:id.:format'
  map.connect ':controller/:action/:id'

えっと何でもいいんですが,Perfumeのメンバー情報を見ることにします.何でもいいんですが.ええ.

MemberController
  def show
    @member = Member.find(params[:id])
    respond_to do |format|
      format.html
      format.js
      format.xml  { render :text => @member.to_xml }
      format.atom { redirect_to :action => :atom }
      format.yaml { render :text => @member.to_yaml }
    end
  end
show.rhtml
  <h1><%= @member.nickname %></h1>
  <h2><%= @member.birthday %></h2>
show.rjs
  page.replace_html 'member_nickname', @member.nickname

実行

すべてブラウザからでも問題ありません(最初の例以外はAcceptヘッダの値を考慮する必要はありません)

http://localhost:3000/member/show/2
  <h1>のっち</h1>
  <h2>1988-09-20</h2>

(この場合はAcceptヘッダが考慮され,その値に結果は左右されます.)

http://localhost:3000/member/show/2.html
  <h1>のっち</h1>
  <h2>1988/09/20</h2>
http://localhost:3000/member/show/2.js
  try {
  Element.update("member_nickname", "\u306e\u3063\u3061");
  } catch (e) { alert('RJS error:\n\n' + e.toString()); alert('Element.update(\"member_nickname\", \"\u306e\u3063\u3061\");'); throw e }

http://localhost:3000/member/show/2.xml
  <?xml version="1.0" encoding="UTF-8"?>
  <member>
    ...
    <nickname>のっち</nickname>
    <birthday type="date">1988-09-20</birthday>
    ...
  </member>
http://localhost:3000/member/show/2.atom
  HTTP/1.x 302 Moved Temporarily
  ..
  Location: http://localhost:3000/member/atom
http://localhost:3000/member/show/2.yaml
  --- !ruby/object:Member 
  attributes:
    nickname: !binary |
      44Gu44Gj44Gh

    birthday: "1988-09-20"

もはやyaml(application/x-yaml もしくは text/yaml)は未定義のMIMEタイプではありません.

http://localhost:3000/member/show/2.json

未定義のMIMEタイプでアクセス

  HTTP/1.x 406 Not Acceptable

入力変換機能

標準では XML 形式を入力に使用できます.

XML の例
  % telnet localhost 3000
  POST /member/search HTTP/1.1
  Host: localhost
  Accept: application/xml
  Content-Type: application/xml

  <query>
    <birthday>1988-12-23</birthday>
  </query>

Controller からは params に

  {"action"=>"search", "controller"=>"member", "query"=>{"birthday"=>#<Date: 4895037/2.0,2299161>}}

という要素が追加されたように見えます.すなわち,FORMからPOSTされたデータと同じように扱えます.

YAMLの例

標準では使用できませんが,YAML形式に関しては envirionment.rb などで以下のようにすることで使用できます.

  ActionController::Base.param_parsers[Mime::Type::lookup('application/x-yaml')] = :yaml
  % telnet localhost 3000
  POST /member/search HTTP/1.1
  Host: localhost
  Accept: application/xml
  Content-Type: application/x-yaml
  
  ---
  :query:
    :birthday: 1988-12-23

params

  {"action"=>"search", "controller"=>"member", "query"=>{"birthday"=>#<Date: 4895037/2.0,2299161>}}

結果

  <?xml version="1.0" encoding="UTF-8"?>
  <member>
    ...
    <nickname>かしゆか</nickname>
    <birthday type="date">1988-12-23</birthday>
    ...
  </member>
そのほかの例

その他の形式でも,Mime::Type に登録されているものであれば

  ActionController::Base.param_parsers[Mime::Type::lookup('text/html')] = Proc.new do |rawdata|
    # ...
  end # => Hash

とHashオブジェクトを返すProcオブジェクトを指定することによって任意の解析結果を与えることができます.

Mime::Type に登録されていないものは登録します.(次項)

Mime::Type へ登録

environment.rb で以下のようにして登録できます.

  Mime::Type.register "text/x-mobile", :mobile

この辺DHHのRubyKaigiのスライドとは引数の順序が違ったりするのでいろいろと注意です.

参考