【Rails】resourcesメソッドを徹底解説!

Rails

resourcesメソッドとは

railsで定義されている7つのアクションのルーティングを自動で作成するメソッドです。 resourcesメソッドを使うことにより、簡単にルーティングを作成することができます。

railsで定義されている7つのアクション
リンクをコピーしました

resourcesメソッドの説明の前にrailsで定義されている7つのアクションの確認をしてみましょう。

アクション名 役割
index リソースの一覧を表示させる
show リソースの詳細を表示させる
new 投稿フォームを表示させる
create リソースを追加させる
edit 更新フォームを表示させる
update リソースを更新させる
destroy リソースを削除する

こちらのルーティングをresourcesメソッドを使わず1つずつ定義した一例は下記のようになります。

ruby
1
2
3
4
5
6
7
8
9
Rails.application.routes.draw do
    get 'tweets'     => 'tweets#index'
    get 'tweets/:id' => 'tweets#show'
    get 'tweets/new' => 'tweets#new'
    post 'tweets' => 'tweets#create'
    get 'tweets/:id/edit' => 'tweets#edit'
    patch 'tweets/:id'  => 'tweets#update'
    delete 'tweets/:id' => 'tweets#destroy'
end

このようにそれぞれのアクションはもともと与えられた主な役割というのがあります。
アプリを作る際、何か機能を追加したい時は、まずは上の7つのアクションに当てはまるかを考えましょう。
できる限り、上の7つのアクションを利用することが大切です。

RESTとは
リンクをコピーしました

REpresentational State Transferの略です。
ユーザーが自分の好き勝手に機能を追加していくのではなく、あらかじめ決められたリソースを定義し、それに沿ってアクションとhttpメソッドを関連づけるという考え方です。

RESTを意識しながらアプリを作成していくとどのアプリも統一された書き方でコードが書かれるので、他人が書いたコードも非常に可読性が上がります。
railsはこのRESTという概念を強く意識したフレームワークです。
resourcesメソッドはRESTに基づくルーティングを自動的に作成してくれます。

resourcesの使い方
リンクをコピーしました

resourcesメソッドはルーティングを記述するroutes.rbの中で使用します。

ruby
1
2
3
Rails.application.routes.draw do
  resources :コントローラー名
end

上のように記述します。
例を見てみましょう。

ruby
1
2
3
Rails.application.routes.draw do
  resources :tweets
end

このように記述することによりtweetsコントローラーに上記の7つのアクションが自動で定義されました。
rake routesコマンドで確認すると下記のようになります。

shell
1
2
3
4
5
6
7
8
9
             Prefix  Verb       URI Pattern                            Controller#Action
            tweets GET         /tweets(.:format)                tweets#index
                         POST      /tweets(.:format)                 tweets#create
    new_tweet GET         /tweets/new(.:format)        tweets#new
    edit_tweet GET         /tweets/:id/edit(.:format)   tweets#edit
             tweet GET         /tweets/:id(.:format)            tweets#show
                        PATCH    /tweets/:id(.:format)            tweets#update
                        PUT         /tweets/:id(.:format)            tweets#update
                        DELETE   /tweets/:id(.:format)            tweets#destroy

onlyとexcept
リンクをコピーしました

resourcesメソッドを使用した際、indexアクションとshowアクションだけを指定したいときには下記のように記述します。

ruby
1
2
3
Rails.application.routes.draw do
  resources :tweets, only: [:index, :snow]
end

もしくは下記のように記述しても同じです。

ruby
1
2
3
Rails.application.routes.draw do
  resources :tweets, except: [:new, :create, :edit, :update, :destroy]
end

「only」はホワイトリスト系で、「except」はブラックリスト系になります。
どちらを使っても構いませんが、「only」を使ったほうが使用するアクションを明示的にできるので可読性が上がります。

このときに「rake routes」コマンドで確認すると下記のようになります。

shell
1
2
3
   Prefix      Verb   URI Pattern                      Controller#Action
   tweets   GET    /tweets(.:format)            tweets#index
   tweet     GET    /tweets/:id(.:format)      tweets#show

このように非常にルーティングがスッキリしました。
特定のアクションしか使用しない場合はこのように「only」や「except」を使った方がルーティングの可読性が上がるので積極的に使いましょう。
※ルーティングがスッキリするだけで「only」や「except」を絶対に使わなければいけないわけではありません。

Prefixとは?
リンクをコピーしました

一番左にPrefixとあります。
このPrefixはパスが代入されている変数のようなものです。
Prefixを使うときは末尾に「_path」と追記します。

prefixを確認するとルートパスを表す「/」というパスは「root」になっています。
なのでlink_toのパスを指定する場所には「_path」をつけた「root_path」を書いてあげれば「/」で指定した記述と同じになります。

パスにidが入っているときはidの情報が入っているインスタンスを引数として渡してあげることにより指定ができます。
例えばusersコントローラーのshowアクションを動かすときは通常のパスだと「users/"ユーザーのid"」になりますが、Prefixを使って書くと「user_path(@user)」のような記述になります。
上の「@user」はコントローラーで「@user = User.find(params[:id])」などで記述してあげれば@userの中にはそのユーザーのidも含まれているのでuser_pathの引数として指定できます。

ネストの定義の仕方
リンクをコピーしました

ルーティングがネストするときもresourcesメソッドを使うことができます。
その際の記述は下記のようになります。

ruby
1
2
3
  resources :tweets do
    resources :comments
  end

このように記述した場合のルーティングは下記のようになります。

shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
                        Prefix   Verb                URI Pattern                  Controller#Action
  tweet_comments    GET         /tweets/:tweet_id/comments(.:format)            comments#index
                                     POST       /tweets/:tweet_id/comments(.:format)          comments#create
 new_tweet_review   GET         /tweets/:tweet_id/comments/new(.:format)      comments#new
edit_tweet_review    GET         /tweets/:tweet_id/comments/:id/edit(.:format) comments#edit
     tweet_review        GET         /tweets/:tweet_id/comments/:id(.:format)      comments#show
                                     PATCH    /tweets/:tweet_id/comments/:id(.:format)      comments#update
                                     PUT         /tweets/:tweet_id/comments/:id(.:format)      comments#update
                                     DELETE   /tweets/:tweet_id/comments/:id(.:format)      comments#destroy
                      tweets   GET         /tweets(.:format)                                                 tweets#index
                                     POST      /tweets(.:format)                                                 tweets#create
              new_tweet   GET         /tweets/new(.:format)                                          tweets#new
              edit_tweet   GET         /tweets/:id/edit(.:format)                                      tweets#edit
                       tweet   GET         /tweets/:id(.:format)                                             tweets#show
                                    PATCH    /tweets/:id(.:format)                                             tweets#update
                                    PUT         /tweets/:id(.:format)                                             tweets#update
                                    DELETE   /tweets/:id(.:format)                                            tweets#destroy

7つのアクション以外のルーティング設計
リンクをコピーしました

resoucesメソッドでrailsで定義されている7つのアクションを自動で定義できますが、それ以外のアクションを追加する場合はどうしたら良いでしょうか?

memberとcollection
リンクをコピーしました

7つのアクション以外のアクションを追加する場合はルーティングの中でmemberとcollectionを使用します。

memberの使い方
リンクをコピーしました

memberはidで指定した個々のリソースに対するアクションを定義できます。
ユーザーのレビューが見れるreviewアクションを追加したいとき、レビューは必ず1人のユーザーがしているものです。
なのでパスは下記のようにならなければなりません。

ruby
1
/users/ユーザーのid/review

このようなルーティングを作りたいときは下記のようにmemberを使って記述します。

ruby
1
2
3
4
5
  resources :tweets do
    member do
      get 'review'
    end
  end

rake routesコマンドでルーティングを確認すると下記のようになります。

shell
1
2
             Prefix  Verb   URI Pattern                                  Controller#Action
review_tweet  GET    /tweets/:id/review(.:format)   tweets#review

このようにパスの中にidが入った状態でルーティングが作成されます。

collectionの使い方
リンクをコピーしました

collectionはリソース全体に対するアクションを定義します。
今回は「search」アクションを定義してみます。

ruby
1
2
3
4
5
  resources :tweets do
    collection do
      get 'search'
    end
  end

rake routesコマンドでルーティングを確認すると下記のようになります。

shell
1
2
               Prefix   Verb    URI Pattern                            Controller#Action
search_tweets   GET    /tweets/search(.:format)    tweets#search

このように全てのリソースに対してのアクションが定義できました。

memberとcollectionを使えば自分でアクション名を定義できますが、RESTの概念に沿ってできる限り7つのアクションを使用しましょう。

実際のアプリでresourcesメソッドを使ってみよう
リンクをコピーしました

それでは実際にアプリの中でresourcesメソッドを使ってみましょう。
下記のコマンドを一つずつ実行していきましょう。
①git clone -b resources https://github.com/miyagit/programan_dojo.git

② cd programan_dojo

③ bundle install
→ rbenv: version ‘2.4.1’ is not installed と表示された場合は、ruby -v と実行してください。

ruby -vと実行し出てきたversion(例: 2.3.1)と出てきたら、

vim .ruby-versionとし、
ruby -vで出てきた値(例: 2.3.1)に書き換えてください。

続いてvim Gemfileとし、ruby 2.4.1と書いてある部分をruby -vで出てきた値(例: 2.3.1)に書き換えてください。

④ rails db:create && rails db:migrate && rails db:seed

環境構築が完了しました。と表示されると、
本当にrails applicationが動作するかrails sコマンドで起動しましょう。

rails sを起動し、ブラウザでlocalhost: 3000と入力して下記のような画面が出てくれば環境構築完了です!

環境構築

ルーティングファイルを確認してみよう
リンクをコピーしました

現在ルートパスを入力するとmainコントローラーのtopアクションが動いたのでルーティングではそういう記述があるはずです。
「config/routes.rb」を開いてみましょう。

ruby
1
2
3
4
Rails.application.routes.draw do
  root 'main#top'
  get 'main' => 'main#top'
end

現在はこういう記述になっていることが確認できます。
では実際に「rake routes」コマンドでルーティングを確認してみましょう。

shell
1
2
3
Prefix     Verb   URI Pattern           Controller#Action
   root     GET    /                             main#top
  main     GET    /main(.:format)    main#top

では次にresourcesメソッドを使ってルーティングを設定してみましょう。
「config/routes.rb」を下記のように編集してください。

ruby
1
2
3
4
Rails.application.routes.draw do
  root 'main#top'
  resources :main
end

では「rake routes」コマンドでルーティングを確認してみましょう。

shell
1
2
3
4
5
6
7
8
9
10
          Prefix    Verb         URI Pattern                        Controller#Action
             root    GET           /                                         main#top
 main_index    GET          /main(.:format)                main#index
                         POST       /main(.:format)                 main#create
   new_main     GET         /main/new(.:format)        main#new
   edit_main     GET         /main/:id/edit(.:format)   main#edit
            main     GET         /main/:id(.:format)            main#show
                         PATCH    /main/:id(.:format)            main#update
                         PUT         /main/:id(.:format)            main#update
                         DELETE   /main/:id(.:format)            main#destroy

このようにrailsで定義された7つのアクションが自動で定義されました。
ただ今回は7つのアクション以外のtopというアクションを使用しているため、今のままだとtopアクションへのルーティングが指定されていないため、エラーになってしまいます。
こういう場合はどうしたら良いのでしょうか。

railsの7つのアクションで定義されていないアクションを追加するにはmemberかcollectionを使うのでした。
今回は全てのリソースに対してアクションを追加するのでcollectionを使ってルーティングを定義します。

ruby
1
2
3
4
5
6
7
8
Rails.application.routes.draw do
  root 'main#top'
  resources :main do
    collection do
        get 'top'
    end
  end
end

「rake routes」コマンドで確認してみましょう。

shell
1
2
3
4
5
6
7
8
9
10
11
                   Prefix   Verb           URI Pattern                        Controller#Action
                     root    GET            /                                         main#top
top_main_index    GET            /main/top(.:format)        main#top
        main_index     GET           /main(.:format)                main#index
                                 POST        /main(.:format)                 main#create
          new_main      GET          /main/new(.:format)        main#new
          edit_main      GET          /main/:id/edit(.:format)   main#edit
                   main      GET          /main/:id(.:format)            main#show
                                  PATCH    /main/:id(.:format)            main#update
                                  PUT         /main/:id(.:format)            main#update
                                  DELETE   /main/:id(.:format)            main#destroy

このようにアクションが追加されました。
今回はルートでもmainコントローラーのtopアクションを指定してるので2つになっています。

まとめ

resourcesメソッドはrailsで定義されている7つのアクションのルーティングを簡単に定義できるメソッドです。