Rails Wayを読む(3)

3. ルーティング

Railsのルーティングシステムは、受信したリクエストから、「どのコントローラでどのアクションを実行するか」を決定するシステムです。
とにかく実践あるのみ。ルーティングルールは、config/route.rbに書いていきます。

ActionController::Routing::Routes.draw do |map|
  # 上から順にマッチングをはかります

  # ここにルーティングルールを定義していきます

  # デフォルトルートです(なんにもひっかからなかったら
  # これが適用されます)
  map.connect ':controller/:action:id.:format'
  map.connect ':controller/:action:id'
end

下が基本的なサンプル。

  map.connect 'myrecipes/:ingredient',
    :controller => 'recipes',
    :action => 'show'

ルーティングシステムの目的は、「リクエストURLを解釈すること」と「URLを生成すること」の2つです。上の例の場合だと、

  • /myrecipes/apples というURLからコントローラ(myrecipes)、アクション(show)、パラメータ(apples)を判断します
  • link_to メソッドでコントローラ(:controller => 'recipes')、アクション(:action => 'show')、パラメータ(:ingredient => 'apples')を指定することによって、/myrecipes/applesというURLを生成します。

ちなみに、:ingredient(レセプトと言うらしい)に入ってきた値は、params[:ingredient]で参照することができます。
次の例は、デフォルトルートです。

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

:formatは出力形式による振り分け用です。振り分けをやっているのは、controllerの、下の部分です。

  # scaffoldが生成したコード
  def show
    # 略

    respond_to do |format|
      format.html   # <--- :format => 'html'
      format.xml { render :xml => @model.to_xml } # <--- :format => 'xml'
    end
  end

なるほど。:formatが指定されなかった場合は、一番したのデフォルトルートにマッチングするわけですが、HTTP-Acceptヘッダをみて適切なところに振り分けられます。うまいことできてますね。
空のルートの設定は、こんな感じ。

  map.root :controller => "welcome"

link_toの話。ルーティングルールで検出されないパラメータがあったらクエリ文字列扱いとなります

  # /〜?some_other_thing=blah
  link_to 〜, :some_other_thing => 'blah' 

ルーティングルールには正規表現を使えます!

  # 1) 数字だったらこっち
  map.connect 'hoge/:action/:id',
    :action => "show", :id => /\d+/
  # 2) 数字以外のはこっちに落ちてきます
  map.connect 'hoge/:action/:id',
    :action => "alt_show"
  # 1') こう書くとコードが明確になるよね
  map.connect 'hoge/:action/:id',
    :action => 'show', :requirements => { :id => /\d+/ }

これすごいですね。でも 2) を書かずにルーティングエラーにしちゃってもいいような(だってルールにマッチしないんだし)。
link_toメソッドはurl_forメソッドのラッパーです。性質としては真空を嫌い、空きがあったら現在のリクエストの内容を引き継ぎます。注意としては、URL文字列を左から見て、デフォルト値以外で埋め立てられた場所以降はデフォルト値を使用しません
可変長のパラメータも解釈できます。

  # /list/first/second/...
  map.connect 'list/*array',
    :controller => 'hoge', :action => 'show'

  # 書いてなかったけど、こうやりゃいいのかな?
  array = params[:array] # <= ["first", "second", ...]

最後に、名前つきルートも設定できます(次のRESTへの布石かな?)

  map.hoge, 'hoge/:id', :controller => 'hoge', :action => 'show'

ってやるとhoge_url、hoge_pathという2つのメソッドが生成されます。違いは下のとおり

こんな感じで書けるのがrubyのすばらしいところでしょうか。

  # ぜんぶおなじ
  hoge_path(:id => hoge.id)
  hoge_path(hoge.id)
  hoge_path(hoge)

Rails Way (Professional Ruby Series)

Rails Way (Professional Ruby Series)