Railsルーティング:「namespaceとmodule」「resourceとresources」「memberとcollection」

# はじめに

この記事では、Rails（4.2系）のルーティングの「namespaceとmodule」と「resourceとresources」と「memberとcollection」に関して説明したいと思います。
これは、最近Railsのルーティングをがっつり触る機会があり、その際の知見を記事にしたものです。
rake routesの表示の違いを述べるだけでなく、どのような状況で使うのかという説明も可能な限り記載しました。

# 前提
adminとstaffがいるシステムを考えます。業務システムではよく出てくる登場人物だと思いますが、管理者(admin)がスタッフ(staff)を操作（CRUD）できるものとします。

# 本編
## namespaceとmodule
### namespace
adminがstaffを操作する機能に必要なルーティングは以下のようになります。

```lang:routes.rb
namespace :admin do
  resources :staffs
end
```
```
$ rake routes | grep admin
     admin_staffs  GET    /admin/staffs(.:format)                 admin/staffs#index
                   POST   /admin/staffs(.:format)                 admin/staffs#create
  new_admin_staff  GET    /admin/staffs/new(.:format)             admin/staffs#new
 edit_admin_staff  GET    /admin/staffs/:id/edit(.:format)        admin/staffs#edit
      admin_staff  GET    /admin/staffs/:id(.:format)             admin/staffs#show
                   PATCH  /admin/staffs/:id(.:format)             admin/staffs#update
                   PUT    /admin/staffs/:id(.:format)             admin/staffs#update
                   DELETE /admin/staffs/:id(.:format)             admin/staffs#destroy
```

### module
ここで、違いを把握するためnamespaceの代わりにmoduleを使ってみましょう。

```lang:routes.rb
resources :staffs, module: 'admin'
```
```
$ rake routes | grep admin
     staffs GET    /staffs(.:format)                    admin/staffs#index
            POST   /staffs(.:format)                    admin/staffs#create
  new_staff GET    /staffs/new(.:format)                admin/staffs#new
 edit_staff GET    /staffs/:id/edit(.:format)           admin/staffs#edit
            GET    /staffs/:id(.:format)                admin/staffs#show
            PATCH  /staffs/:id(.:format)                admin/staffs#update
            PUT    /staffs/:id(.:format)                admin/staffs#update
            DELETE /staffs/:id(.:format)                admin/staffs#destroy
```
見比べると、staffのindexページにアクセスするためのURIが、
namespaceを指定した場合は`/admin/staffs(.:format)`となっており、一方moduleを指定した場合は`/staffs(.:format)`となっています。

### コメント

moduleの用途としては以下の記事をご覧ください。
[Railsで多対多モデルを実装する場合のルーティング（moduleオプションの使い方）](http://qiita.com/ebihara99999/items/6e49830f8fd03a5a40dd)
また、調べてみるとnamespaceとmoduleの他にscopeがあるようです。詳しくは以下で詳細な説明が載っているのでご覧ください。
[routeのmoduleとnamespaceとscopeの違い](http://qiita.com/blueplanet/items/522cc8364f6cf189ecad)

## resourceとresources

今度は、「namespaceとmodule」で触れた仕様に加えて、adminがstaffのパスワードを変更できる仕様を考えます。

### resource
パスワード変更は通常のstaff#editとは別のフォームで実装することを考えます（なお、updateアクションに関してはstaff#updateを呼び出すべきですが、そちらに関してはviewに記述するため省略します）。
この場合、ユーザー1人に対してパスワードが1つ存在するので、resouceを使用します。

```lang:routes.rb
namespace :admin do
  resources :staffs do
    resource :password, only: [ :edit ]
  end
end
```
```
$ rake routes | grep admin
edit_admin_staff_password GET    /admin/staffs/:staff_id/password/edit(.:format)        admin/passwords#edit
~以下略~
```

### resources
一方、比較のために上記をresourcesを使用し記述すると以下のようになります。

```lang:routes.rb
namespace :admin do
  resources :staffs do
    resources :password, only: [ :edit ]
  end
end
```
```
$ rake routes | grep admin
edit_admin_staff_password GET    /admin/staffs/:staff_id/password/:id/edit(.:format)                                 admin/password#edit
~以下略~
```
URIが`/admin/staffs/:staff_id/password/:id/edit(.:format)`となり、staffにパスワードが複数紐付いているようなケースのURIとなってしまい不適です。

## memberとcollection

次は、単純なCRUD操作以外の処理を追加していきましょう。CRUD内に適切なメソッドがないケースではmemberもしくはcollectionを使います。
まず、管理者がスタッフページをカスタマイズするというケースを考えましょう（例えば、見やすくするために部ごとにスタッフページのレイアウトを変えたいというケースです）。

### member
スタッフではなくスタッフページへの操作なので、単純にstaffのeditやupdateを使用せずmemberにてpage_customメソッドを追加します。

```lang:routes.rb
namespace :admin do
  resources :staffs, module: 'staff_magagement' do
    member do
      get :page_custom
    end
  end
end
```
```
$ rake routes | grep page_custom
  page_custom_admin_staff GET    /admin/staffs/:id/page_custom(.:format)                  admin/staff_magagement/staffs#page_custom
             admin_staffs GET    /admin/staffs(.:format)                                  admin/staff_magagement/staffs#index
                          POST   /admin/staffs(.:format)                                  admin/staff_magagement/staffs#create
          new_admin_staff GET    /admin/staffs/new(.:format)                              admin/staff_magagement/staffs#new
         edit_admin_staff GET    /admin/staffs/:id/edit(.:format)                         admin/staff_magagement/staffs#edit
              admin_staff GET    /admin/staffs/:id(.:format)                              admin/staff_magagement/staffs#show
                          PATCH  /admin/staffs/:id(.:format)                              admin/staff_magagement/staffs#update
                          PUT    /admin/staffs/:id(.:format)                              admin/staff_magagement/staffs#update
                          DELETE /admin/staffs/:id(.:format)                              admin/staff_magagement/staffs#destroy
```

### collection
今度は、staffを検索する機能(search)を実現するルーティングです。searchアクションはstaff全体への操作でありidを渡す必要がないので、collectionを使用します。

```lang:routes.rb
namespace :admin do
  resources :staffs, module: 'staff_magagement' do
    collection do
      get :search
    end
  end
end
```
```
$ rake routes | grep search
search_admin_staffs GET    /admin/staffs/search(.:format)               admin/staff_magagement/staffs#search
```

# 最後に
誤りやご意見ありましたらご指摘いただけると嬉しいです。

##参考
http://qiita.com/ebihara99999/items/6e49830f8fd03a5a40dd
http://qiita.com/blueplanet/items/522cc8364f6cf189ecad