map.resource(s)がURL設計をまとめてくれる

Railsのmap.resource(s)を使うと画面の構成をまとめて記述できます。

例えば、config/routes.rbに以下のように書くと、

ActionController::Routing::Routes.draw do |map|  
  map.resource :sessions
  map.resources :magazines do |magazine| # 雑誌
    magazine.resources :subscriptions # 購読
    magazine.resources :numbers do |number| # ナンバー
      number.resources :articles, 
        :new => {
          :draft => :post # 原稿登録
        },
        :member => {
          :correct => :post # 構成
        }
    end
  end
end

rake routesを実行すると、以下のようにヘルパーメソッド、HTTPメソッド、パス、paramsが出力されます。
パスに記述されているURLにそのHTTPメソッドでアクセスすると表にあるparams(と渡されたパラメータがマージされたもの)になります。


出力結果:

ヘルパーメソッド名 HTTPメソッド パス params
POST /sessions {:controller=>"sessions", :action=>"create"}
POST /sessions.:format {:controller=>"sessions", :action=>"create"}
new_sessions GET /sessions/new {:controller=>"sessions", :action=>"new"}
formatted_new_sessions GET /sessions/new.:format {:controller=>"sessions", :action=>"new"}
edit_sessions GET /sessions/edit {:controller=>"sessions", :action=>"edit"}
formatted_edit_sessions GET /sessions/edit.:format {:controller=>"sessions", :action=>"edit"}
sessions GET /sessions {:controller=>"sessions", :action=>"show"}
formatted_sessions GET /sessions.:format {:controller=>"sessions", :action=>"show"}
PUT /sessions {:controller=>"sessions", :action=>"update"}
PUT /sessions.:format {:controller=>"sessions", :action=>"update"}
DELETE /sessions {:controller=>"sessions", :action=>"destroy"}
DELETE /sessions.:format {:controller=>"sessions", :action=>"destroy"}
magazines GET /magazines {:controller=>"magazines", :action=>"index"}
formatted_magazines GET /magazines.:format {:controller=>"magazines", :action=>"index"}
POST /magazines {:controller=>"magazines", :action=>"create"}
POST /magazines.:format {:controller=>"magazines", :action=>"create"}
new_magazine GET /magazines/new {:controller=>"magazines", :action=>"new"}
formatted_new_magazine GET /magazines/new.:format {:controller=>"magazines", :action=>"new"}
edit_magazine GET /magazines/:id/edit {:controller=>"magazines", :action=>"edit"}
formatted_edit_magazine GET /magazines/:id/edit.:format {:controller=>"magazines", :action=>"edit"}
magazine GET /magazines/:id {:controller=>"magazines", :action=>"show"}
formatted_magazine GET /magazines/:id.:format {:controller=>"magazines", :action=>"show"}
PUT /magazines/:id {:controller=>"magazines", :action=>"update"}
PUT /magazines/:id.:format {:controller=>"magazines", :action=>"update"}
DELETE /magazines/:id {:controller=>"magazines", :action=>"destroy"}
DELETE /magazines/:id.:format {:controller=>"magazines", :action=>"destroy"}
magazine_subscriptions GET /magazines/:magazine_id/subscriptions {:controller=>"subscriptions", :action=>"index"}
formatted_magazine_subscriptions GET /magazines/:magazine_id/subscriptions.:format {:controller=>"subscriptions", :action=>"index"}
POST /magazines/:magazine_id/subscriptions {:controller=>"subscriptions", :action=>"create"}
POST /magazines/:magazine_id/subscriptions.:format {:controller=>"subscriptions", :action=>"create"}
new_magazine_subscription GET /magazines/:magazine_id/subscriptions/new {:controller=>"subscriptions", :action=>"new"}
formatted_new_magazine_subscription GET /magazines/:magazine_id/subscriptions/new.:format {:controller=>"subscriptions", :action=>"new"}
edit_magazine_subscription GET /magazines/:magazine_id/subscriptions/:id/edit {:controller=>"subscriptions", :action=>"edit"}
formatted_edit_magazine_subscription GET /magazines/:magazine_id/subscriptions/:id/edit.:format {:controller=>"subscriptions", :action=>"edit"}
magazine_subscription GET /magazines/:magazine_id/subscriptions/:id {:controller=>"subscriptions", :action=>"show"}
formatted_magazine_subscription GET /magazines/:magazine_id/subscriptions/:id.:format {:controller=>"subscriptions", :action=>"show"}
PUT /magazines/:magazine_id/subscriptions/:id {:controller=>"subscriptions", :action=>"update"}
PUT /magazines/:magazine_id/subscriptions/:id.:format {:controller=>"subscriptions", :action=>"update"}
DELETE /magazines/:magazine_id/subscriptions/:id {:controller=>"subscriptions", :action=>"destroy"}
DELETE /magazines/:magazine_id/subscriptions/:id.:format {:controller=>"subscriptions", :action=>"destroy"}
magazine_numbers GET /magazines/:magazine_id/numbers {:controller=>"numbers", :action=>"index"}
formatted_magazine_numbers GET /magazines/:magazine_id/numbers.:format {:controller=>"numbers", :action=>"index"}
POST /magazines/:magazine_id/numbers {:controller=>"numbers", :action=>"create"}
POST /magazines/:magazine_id/numbers.:format {:controller=>"numbers", :action=>"create"}
new_magazine_number GET /magazines/:magazine_id/numbers/new {:controller=>"numbers", :action=>"new"}
formatted_new_magazine_number GET /magazines/:magazine_id/numbers/new.:format {:controller=>"numbers", :action=>"new"}
edit_magazine_number GET /magazines/:magazine_id/numbers/:id/edit {:controller=>"numbers", :action=>"edit"}
formatted_edit_magazine_number GET /magazines/:magazine_id/numbers/:id/edit.:format {:controller=>"numbers", :action=>"edit"}
magazine_number GET /magazines/:magazine_id/numbers/:id {:controller=>"numbers", :action=>"show"}
formatted_magazine_number GET /magazines/:magazine_id/numbers/:id.:format {:controller=>"numbers", :action=>"show"}
PUT /magazines/:magazine_id/numbers/:id {:controller=>"numbers", :action=>"update"}
PUT /magazines/:magazine_id/numbers/:id.:format {:controller=>"numbers", :action=>"update"}
DELETE /magazines/:magazine_id/numbers/:id {:controller=>"numbers", :action=>"destroy"}
DELETE /magazines/:magazine_id/numbers/:id.:format {:controller=>"numbers", :action=>"destroy"}
magazine_number_articles GET /magazines/:magazine_id/numbers/:number_id/articles {:controller=>"articles", :action=>"index"}
formatted_magazine_number_articles GET /magazines/:magazine_id/numbers/:number_id/articles.:format {:controller=>"articles", :action=>"index"}
POST /magazines/:magazine_id/numbers/:number_id/articles {:controller=>"articles", :action=>"create"}
POST /magazines/:magazine_id/numbers/:number_id/articles.:format {:controller=>"articles", :action=>"create"}
draft_new_magazine_number_article POST /magazines/:magazine_id/numbers/:number_id/articles/new/draft {:controller=>"articles", :action=>"draft"}
formatted_draft_new_magazine_number_article POST /magazines/:magazine_id/numbers/:number_id/articles/new/draft.:format {:controller=>"articles", :action=>"draft"}
new_magazine_number_article GET /magazines/:magazine_id/numbers/:number_id/articles/new {:controller=>"articles", :action=>"new"}
formatted_new_magazine_number_article GET /magazines/:magazine_id/numbers/:number_id/articles/new.:format {:controller=>"articles", :action=>"new"}
correct_magazine_number_article POST /magazines/:magazine_id/numbers/:number_id/articles/:id/correct {:controller=>"articles", :action=>"correct"}
formatted_correct_magazine_number_article POST /magazines/:magazine_id/numbers/:number_id/articles/:id/correct.:format {:controller=>"articles", :action=>"correct"}
edit_magazine_number_article GET /magazines/:magazine_id/numbers/:number_id/articles/:id/edit {:controller=>"articles", :action=>"edit"}
formatted_edit_magazine_number_article GET /magazines/:magazine_id/numbers/:number_id/articles/:id/edit.:format {:controller=>"articles", :action=>"edit"}
magazine_number_article GET /magazines/:magazine_id/numbers/:number_id/articles/:id {:controller=>"articles", :action=>"show"}
formatted_magazine_number_article GET /magazines/:magazine_id/numbers/:number_id/articles/:id.:format {:controller=>"articles", :action=>"show"}
PUT /magazines/:magazine_id/numbers/:number_id/articles/:id {:controller=>"articles", :action=>"update"}
PUT /magazines/:magazine_id/numbers/:number_id/articles/:id.:format {:controller=>"articles", :action=>"update"}
DELETE /magazines/:magazine_id/numbers/:number_id/articles/:id {:controller=>"articles", :action=>"destroy"}
DELETE /magazines/:magazine_id/numbers/:number_id/articles/:id.:format {:controller=>"articles", :action=>"destroy"}


idでリソースが特定されるようなものについてのコントローラに着いては、map.resources(複数形)で、idで特定できないものについてはmap.resource(単数形)を使います。
これの何が便利なのかというと、基本的な動作を、index(GET), new(GET), create(POST), show(GET), edit(GET), update(PUT), destroy(DELETE)というCRUDに対応するようにある程度決めているところです。「ある程度」というのは、

      number.resources :articles, 
        :new => {
          :draft => :post # 原稿登録
        },
        :member => {
          :correct => :post # 構成
        }

と記述されているように、基本的なCRUDに対応するアクション以外にも自由に追加できるためです。また、

  map.resources :magazines do |magazine| # 雑誌
    magazine.resources :subscriptions # 購読
  end

という風に記述することで、ある特定の雑誌の購読についての動作であることがURL上でも表現でき、アクションではparamsに:magazine_idが設定されます。

これでアプリケーションの見通しがだいぶ違うはずです。このお約束を守ることが開発メンバー内で合意がとれていれば、大まかな画面遷移を決める人、画面の細かい動きを決める人、実装を行う人の間で誤解が生まれる可能性が少なくなります。URLをどんな感じにするのかについてもある程度決まっていることで、URLの設計に悩む時間も減ります。

という訳で、画面設計に関わる人はmap.resource(s)を押さえておくといいことあるはず、という話でした。