はじめに
概要
この記事では、「リファクタリングと保守の向上」をテーマに、Ruby on Railsでの開発効率を向上させる技術について解説します。具体的には、resources
の効果的な活用、ルーティングの簡潔化、名前付きルートとヘルパーの利用、部分テンプレートの導入、そしてbefore_action
を使用したコントローラの整理方法を学びます。これらのテクニックを通じて、コードの可読性の向上と保守性の強化を目指します。
目標
- ルーティングの設定を簡素化し、名前付きルートなどを活用する方法を学ぶ。
- 部分テンプレートで、ビューのコードを整理し再利用性を高める技術を習得する。
before_action
で、コントローラ内の共通処理を管理する方法を理解する。
必要な前提条件・事前準備
以前、部分テンプレートの基本的な使用方法については触れましたが、今回は特に変数を活用する応用技術を学びます。そのため、事前に「部分テンプレートの基礎」について復習しておくことで、本章をより深く理解できるでしょう。
- 「部分テンプレートの基礎」の復習(任意)
resourcesの活用
以前「RESTful開発の基礎」で学んだように、Railsが提供するresources
メソッドを使用すると、RESTfulルーティングに必要な標準的なルートが自動的に生成され、ルーティングの設定が簡素化されます。
1
2
3
Rails.application.routes.draw do
resources :リソース名
end
例えば、これまでの「RailsでRESTfulなCRUDアプリケーションを作成しよう1(基礎)」では、学習のためにemployees
リソースのルーティングを設定する際に、resources
メソッドを使用せずにRESTfulなルーティングを手動で定義してきました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Rails.application.routes.draw do
# Index: 社員の一覧を表示
get 'employees', to: 'employees#index'
# New: 新しい社員を追加するためのフォームを表示
get 'employees/new', to: 'employees#new'
# Create: 新しい社員を追加
post 'employees', to: 'employees#create'
# Show: 特定の社員の詳細を表示
get 'employees/:id', to: 'employees#show'
# Edit: 既存の社員を編集するためのフォームを表示
get 'employees/:id/edit', to: 'employees#edit'
# Update: 既存の社員の情報を更新
patch 'employees/:id', to: 'employees#update', as: :employee
# Destroy: 特定の社員を削除
delete 'employees/:id', to: 'employees#destroy'
end
しかし、resources
メソッドを使用すると、以下のようにわずか1行で設定することができます。
1
2
3
Rails.application.routes.draw do
resources :employees # 説明用
end
さらに、resources
メソッドを使用することで、「名前付きルート」と「名前付きヘルパー」が自動的に生成されます。これらを活用することで、コードの可読性を向上させ、保守性を高めることができます。
名前付きルートの基礎
resources
メソッドは、各アクションに対する名前付きルートを自動的に生成します。名前付きルートは、特定のルートに対してわかりやすく意味のある名前(識別子)を割り当てることにより、アプリケーション内でそのルートを容易に参照できるようにします。
自動生成された名前付きルートは、ターミナルから確認することができます。アプリケーションのルートディレクトリで以下のコマンドを使用します。
1
rails routes
このコマンドは、アプリケーションで定義されているすべてのルートの一覧を表示し、それぞれのルートに対して設定されているパス、HTTPメソッド、名前付きルート、対応するコントローラーとアクションを表示します。
表示されるリストのPrefix
の項目が名前付きルートに相当します。
例えば、GET /articles/new(.:format)
というルートが articles#new
アクションに対応付けされている場合、このルートには new_article
という名前が割り当てられます。これが「名前付きルート」の一例です。
名前付きルートとは、特定のルートにわかりやすい名前を割り当てる概念なんだね!
自動生成される名前付きルートの比較
resources
メソッドを使用すると、new
, edit
, show
, およびindex
アクションに対応する名前付きルートが自動的に生成されます。
例えば、「articles」というリソースに対して resources
メソッドを適用した場合には、new_article
、edit_article
、article
、articles
という名前付きルートが自動的に生成されます。
1
2
3
Rails.application.routes.draw do
resources :articles
end
一方、resources
メソッドを使用せずに手動でRESTfulルーティングを定義する場合、通常は自動的に名前付きルートは生成されません。しかし、特定の条件(※補足説明)に該当する場合、new
や index
のような一部のアクションに対して自動的に名前付きルートが生成されることがあります。
例えば、「articles」というリソースに対して手動でRESTfulルーティングを設定した場合、new
とindex
アクションに対応する名前付きルートのみが自動的に生成され、他のアクションに対する名前付きルートは生成されません。
また、resources
メソッドを利用した場合、new
アクションに付けられる名前付きルートは通常new_article
となりますが、手動でルーティングを定義した場合はarticle_new
のように命名規則が異なることがあります。
この違いは、resources
メソッドが一貫した命名規則に従って名前付きルートを生成するのに対し、手動でルーティングを定義する際には特定の命名規則に従わないためです。
今回は学習目的で手動でルーティングを定義したけれど、ルーティングの一貫性と名前付きルートの整合性を維持するため、通常は手動ではなくresources
メソッドの使用が推奨されるよ!
名前付きルートを手動で設定する方法
resources
メソッドを使用すると、名前付きルートが自動的に設定されます。しかし、状況によっては、手動で名前付きルートを設定する必要がある場合もあります。ルーティング定義に as: :名前
を追加することで、ルートに特定の名前を付けることができます。
1
2
3
Rails.application.routes.draw do
HTTPリクエストメソッド 'パス', to: 'コントローラ名#アクション名', as: :名前
end
例えば、resources
メソッドを使用せずに手動でルーティングを定義した場合、article_new
という名前付きルートが自動生成されます。
しかし、以下のように as
オプションを指定することで、new_article
という名前付きルートに変更することができます。
1
2
3
4
Rails.application.routes.draw do
get 'articles/new', to: 'articles#new', as: :new_article
# その他のルーティング定義...
end
この方法を用いることで、resources
メソッドを使用した場合と同等のルーティングを手動で設定することもできます。また、form_with
などのヘルパーメソッドとの互換性も確保できます。
form_with と命名規則の整合性
以前、編集フォームを実装した際にform_with
メソッドを使用しましたが、その時には更新用ルートで as
オプションを使用して employee
という名前付きルートを手動で設定しました。
1
2
3
4
Rails.application.routes.draw do
# ... [その他のルート定義] ...
patch 'employees/:id', to: 'employees#update', as: :employee
end
ここでは、なぜ上記のような設定が必要だったのかを詳しく見ていきましょう。
form_withメソッドは、model
引数を通じて与えられたインスタンス変数の状態(新規レコードか既存レコードか)を確認し、それに基づいて適切なURLとHTTPメソッドを推測します。
この挙動は、Railsのresources
メソッドで定義される標準的なRESTfulルーティングの命名規則に従っています。そのため、form_with
が正確なURLを推測できるように、ルーティング定義でもresourcesメソッドの命名規則に従うことが重要です。
新規レコードの場合は通常問題ありませんが、既存レコードの場合、form_with
は更新アクションのURLを生成するために resources
メソッドで定義される名前付きルート(employee
)を期待します。
しかし、手動でルーティングを設定した場合、update
アクションに対して名前付きルートが自動的に生成されないため、form_with
による正確なURL推測ができません。
このため、 as
オプションを使用して明示的に名前付きルートを設定することで、form_with
が期待する名前付きルートを提供し、適切な更新アクションのURLを生成できるようになります。
1
2
3
4
Rails.application.routes.draw do
# ... [その他のルート定義] ...
patch 'employees/:id', to: 'employees#update', as: :employee
end
この名前付きルートの設定により、form_with
との整合性が保たれ、モデルインスタンスの状態が既存レコードである場合にも、正しい更新アクションのURLが自動的に推測されます。
更新ルートに as
を使って名前付きルートを手動で設定した理由は、form_with
がモデルインスタンスの状態を判断して適切なアクションのURLを推測するときに、resources
メソッドで自動生成される名前付きルートの命名規則に基づいているからなんだね!
その通りだよ。「employees」リソースに対して resources
メソッドを使用すれば、これらの命名規則の整合性に関する問題を考慮する必要がなくなるよ!
resourcesメソッドを使用してルーティングを再設定しよう
手動で設定したルーティングからresources
メソッドを使用したルーティングへと変更します。この変更により、employees リソースに対する標準的なRESTfulルーティングが自動的に設定され、アプリケーションのルーティングがより整理されます。
まず、アプリケーションのルートディレクトリに移動して、rails routes
コマンドを実行しましょう。
1
rails routes
実行すると、以下のように設定されたルーティングだけでなく、デフォルトで設定されている多くのルートも表示されます。
以下のように、手動で設定したルーティングであっても、employees
や employees_new
といった名前付きルートが自動生成されていること、また as
オプションを使用して設定した employee
という名前付きルートが存在することが確認できれば問題ありません。
次に、手動で設定したルーティングから resources
メソッドを使った設定へと変更しましょう。config/routes.rb
ファイルを開き、以下のように編集してください。
1
2
3
Rails.application.routes.draw do
resources :employees
end
この変更により、employees
リソースに対する標準的なRESTfulルーティングが自動的に設定されます。
最後に、resources
メソッドによって自動生成された名前付きルートを確認するために、再度 rails routes
コマンドを実行しましょう。
1
rails routes
実行すると、アプリケーションで定義されているすべてのルートの一覧が表示されます。
Prefix
の項目に注目してみましょう。手動でルーティングを設定した際にはnew
アクションの名前付きルートは employees_new
でしたが、resources
メソッドを適用すると、以下のように new_employee
として生成されていることが確認できます。
また、index
、edit
、show
アクションなどに対しても自動で名前付きルートが生成されていることがわかります。
今まではconfig/routes.rb
ファイルで、どのリクエストがどのアクションに対応しているか確認できたけど、resources
メソッドを適用すると、直接は分からなくなっちゃうよね。これからルートをどうやって確認すればいいのかな?
その場合はターミナルでrails routes
コマンドを実行するといいよ!このコマンドは、名前付きルートの確認に加えて、アプリケーションで定義されている全てのルートと、それぞれが対応するコントローラとアクションの一覧を表示してくれるから、とっても便利だよ。
名前付きヘルパーの基礎
Railsでは、名前付きルートに基づいて対応するヘルパーメソッドが自動的に生成されます。これらのヘルパーメソッドは「名前付きヘルパー」と呼ばれます。
名前付きヘルパーは、名前付きルートを利用してURLやパスを生成するためのヘルパーメソッドです。主に *_path
と *_url
の二種類があります。*
の部分には対応する名前付きルートが入ります。
種類 | 生成 | 用途 |
---|---|---|
*_path |
相対パス | 同一アプリケーション内でのページ遷移やリンク生成に使用 |
*_url |
絶対URL | ドメインを含む完全なURLが必要な場合に使用 外部リンクやメール内のリンクなど |
*_path
は相対パスを生成し、*_url
は絶対URLを生成します。主にlink_toメソッドなどのビューヘルパー内で使用され、リンクを生成する際に活用されます。
link_toメソッドでは、リンク先のパス
に*_path
や*_url
を指定します。
1
<%= link_to 'テキスト', 'リンク先のパス', method: :HTTPメソッド %>
1
<%= link_to 'テキスト', 'リンク先のパス' %>
Railsの基礎で*_path
について学習したことがありますが、ここで復習としておさらいしてみましょう。さらに引数にインスタンス変数を活用する方法も学習します。
*_pathメソッド
*_path
メソッドを使用すると、名前付きルートに基づいて相対パスを生成することができます。link_toメソッドなどを使うことで、名前付きルートに基づいてリンクを生成し、アプリケーション内の異なるページを簡単にリンクすることができます。
例えば、config/routes.rb
に以下のようなルート設定があるとします。
1
2
3
Rails.application.routes.draw do
resources :articles
end
この設定により、以下のように名前付きルートが自動で生成されます。
このとき、上記の名前付きルートに対応するヘルパーメソッド、つまり名前付きヘルパーが生成されます。名前付きルートに _path
を付けるだけで使用することができます。
例えば、indexアクションに対応する名前付きルートのarticles
を使用してみます。
名前付きルート articles
に _path
を付けて、以下のように link_to
メソッドで指定します。また、articles_path
はクォーテーションで囲いません。クォーテーションで囲んでしまうと、その文字列自体がリンク先として認識されてしまいます。
1
2
<%= link_to 'テキスト', 'リンク先のパス' %>
<%# HTTPメソッドを指定しない場合、デフォルトでGETが使用されます。 %>
1
<%= link_to '記事一覧', articles_path %>
上記のarticles_path
は、/articles
というパスを生成します。このコードはビューで以下のHTMLに変換されます。
1
<a href="/articles">記事一覧</a>
articles_path
は、以下のようにindex
アクションのルートパスが生成されていることがわかります。
もしnew_article_path
を指定する場合は、/articles/new
になります。
インスタンスを利用する場合
*_path
メソッドは、引数として受け取った値を使って、対応するルートの URL パスを生成することができます。
例えば、特定の記事の詳細を表示するshow
アクションを考えてみましょう。このアクションに対応する名前付きルートはarticle
です。そのため、article_path(id)
と記述することで/articles/:id
というパスが生成されます。
article_path
の引数id
には、具体的な数値を渡します。例えば、id
に3
を渡した場合、生成されるURLは/articles/3
となります。
1
<%= link_to '詳細を表示', article_path(3) %>
1
<a href="/articles/3">詳細を表示</a>
また、数値のid
の代わりにモデルのインスタンス自体を直接渡すこともできます。
例えば、@article
に特定の記事のインスタンス(id
が2
)が代入されている場合、以下のようにarticle_path
の引数にそのインスタンス変数を渡すだけで、Railsはインスタンスからidを自動的に取り出し、適切なURLを生成します。
1
<%= link_to '詳細を表示', article_path(@article) %>
この場合、以下のリンクが生成されます。
1
<a href="/articles/2">詳細を表示</a>
さらに、記事一覧ページで各記事の詳細ページへのリンクを生成する例も確認してみます。
これまでに学習した方法では、まず@articles
(全記事のインスタンスが格納されている)をeach
メソッドでループし、各記事をarticle
変数に代入します。次に、リンクを生成する際にarticle.id
で各記事インスタンスのid
属性を取得し、それをURLに動的に組み込んでいました。
1
2
3
def index
@articles = Article.all # 全ての記事を取得し @article に代入
end
1
2
3
<% @articles.each do |article| %>
<a href="/articles/<%= article.id %>">詳細情報</a>
<% end %>
しかし、*_path
ヘルパーを使用することで、以下のような記述が可能になります。特定の記事のインスタンスがarticle
に格納されている場合、*_path
は自動的にそのインスタンスのid
を抽出し、適切なURLを生成します。
1
2
3
<% @articles.each do |article| %>
<%= link_to '詳細', article_path(article) %>
<% end %>
*_urlメソッド
*_url
メソッドを使用すると、名前付きルートに基づいて、絶対URL(プロトコル、ドメイン名、パスを含む)を生成することができます。これは外部リンクや特にメール内でリンクを送る場合に適しています。メールでは、受信者が異なるドメインやサーバーからアクセスするため、相対パスではなく絶対URLを使用する方が一般的です。
相対パスは、ある特定のウェブサイト内のみ有効なパスです。メールはウェブサイト上にあるわけではないため、その相対パスだけでは何のページか分かりません。そのため、メール内のリンクには絶対URLを使います。
1
/articles/3
1
http://yourdomain.com/articles/3
例として、config/routes.rb
に設定された名前付きルートを利用します。
1
2
3
Rails.application.routes.draw do
resources :articles
end
この設定により、以下のように名前付きルートが自動で生成されます。
例えば、new_article_url
を指定する場合、生成されるURLは http://yourdomain.com/articles/new
になります。これは、メールで新しい記事を作成するためのリンクを送る際に便利です。
1
<%= link_to '新しい記事を作成', new_article_url %>
生成されるメール内のHTMLは以下のようになります。
1
<a href="http://yourdomain.com/articles/new">新しい記事を作成</a>
また、*_url
メソッドも _path
メソッドと同様に、引数としてモデルのインスタンスやIDを受け取ることができます。
このように、*_url
メソッドは、外部からのアクセスやメール内でのリンク作成に適しているため、Webアプリケーションでメール通知機能を実装する際に特に有用です。
名前付きヘルパーの確認方法
名前付きヘルパーは、rails routes
コマンドで名前付きルートを確認し、それに_path
や_url
を付け加えることで利用できます。
さらに、開発環境ではRailsサーバーを起動させて/rails/info/routes
にアクセスすることで、アプリケーションのルート情報をブラウザで直接確認することも可能です。
この機能は、ターミナルでrails routes
コマンドを実行した際に得られる情報と同じルーティング情報を提供します。ただし、セキュリティ上の理由から、このページはデフォルトでは開発環境でのみアクセス可能です。
また、rails routes
コマンドとは異なり、このページでは名前付きヘルパーも直接表示されます。
名前付きヘルパーを使ったリファクタリングしよう
アプリケーション内のリンクなので、_urlではなく、_pathを利用します。またリンク生成はHTMLのaタグを利用しているので、そちらもlink_toメソッドに変更します。
Railsサーバーを起動してない方は、アプリケーションのルートディレクトリでrails s
コマンドを実行しましょう。
Webブラウザで/rails/info/routes
にアクセスして、以下のように名前付きヘルパーが表示されることを確認しましょう。
まず、社員情報一覧画面以外の画面にある「社員情報一覧に戻る」のリンクをlink_to
メソッドと名前付きヘルパーの*_path
メソッドを使用してリファクタリングしましょう。社員の詳細情報画面では、以下の部分にこのリンクが配置されています。
社員情報一覧の画面を表示するリクエストGET /employees
は、index
アクションで処理されます。Webブラウザで確認すると、以下のようにindex
アクションへのパス生成は、employees_path
メソッドが使用できることがわかります。
現在、「社員情報一覧に戻る」というリンクは、HTMLのa
タグを使って直接URLをhref属性に記述することで生成されています。
1
<a href="/employees">社員情報一覧に戻る</a>
このコードをlink_to
メソッドとemployees_path
メソッドを使ってリファクタリングすると、以下のように書き換えることができます。
1
<%= link_to '社員情報一覧に戻る', employees_path %>
app/views/employees/
ディレクトリ内の以下のビューファイルの該当箇所を上記のコードのように変更しましょう。
create.html.erb
destroy.html.erb
edit.html.erb
new.html.erb
show.html.erb
update.html.erb
例えば、new.html.erb
の<a href="/employees">社員情報一覧に戻る</a>
の箇所は、以下の画像のように変更します。
index.html.erb
以外のビューファイルを変更した後は、社員の詳細画面などで「社員情報一覧に戻る」というリンクをクリックし、リファクタリング後もリンクが正常に機能しているか確認しましょう。
社員情報一覧画面の「新規作成」のリンクをlink_to
メソッドと名前付きヘルパーの*_path
メソッドを使用してリファクタリングしましょう。
新規社員登録の画面を表示するリクエストGET /employees/new
は、new
アクションで処理されます。Webブラウザで確認すると、以下のようにnew
アクションへのパス生成は、new_employee_path
メソッドが使用できることがわかります。
現在、「新規作成」というリンクは、HTMLのaタグを使って直接URLをhref属性に記述することで生成されています。
1
<a href="/employees/new">新規作成</a>
このコードをlink_to
メソッドとnew_employee_path
メソッドを使ってリファクタリングすると、以下のように書き換えることができます。
1
<%= link_to '新規作成', new_employee_path %>
それでは、index.html.erb
ファイルの該当箇所を上記のコードのように変更しましょう。
index.html.erb
ファイルを変更した後は、社員情報一覧画面の「新規作成」のリンクをクリックし、リファクタリング後もリンクが正常に機能しているか確認しましょう。
「社員情報一覧画面」の「詳細情報」と「編集」のリンクをリファクタリングしましょう。これら2つのリンクについては、*_path
メソッドの引数としてインスタンスを渡し、動的にパスを生成します。
特定の社員の詳細情報を編集するためのリクエストGET /employees/:id/edit
は、edit
アクションで処理されます。Webブラウザで確認すると、以下のようにedit
アクションへのパス生成は、edit_employee_path
メソッドを使用することで実現できることがわかります。
同様に、特定の社員の詳細情報を表示するリクエストGET /employees/:id
は、show
アクションで処理されます。Webブラウザで確認すると、show
アクションへのパス生成は employee_path
メソッドを使用します。
これらの*_path
メソッドはどちらも、特定の社員のid
を引数として受け取り、その社員の詳細画面や編集画面へのパスを生成する役割を担います。
現在、「詳細情報」と「編集」というリンク生成では、@employees(全社員のインスタンスが格納されている)をeachメソッドでループし、各社員をarticle変数に代入します。次に、リンクを生成する際にemployee.idで各社員インスタンスのid属性を取得し、それをURLに動的に組み込んでいました。
1
2
3
4
5
<% @employees.each do |employee| %>
<%# 他コードを省略してます。 %>
<a href="/employees/<%= employee.id %>">詳細情報</a>
<a href="/employees/<%= employee.id %>/edit">編集</a>
<% end %>
このコードをlink_to
メソッドと各*_path
メソッドを使ってリファクタリングすると、以下のように書き換えることができます。
1
2
3
4
5
<% @employees.each do |employee| %>
<%# 他コードを省略してます。 %>
<%= link_to '詳細情報', employee_path(employee) %>
<%= link_to '編集', edit_employee_path(employee) %>
<% end %>
*_path
メソッドの引数に特定の社員のインスタンスemployee
を渡すだけで、Railsはインスタンスからid
を自動的に取り出し、適切なURLを生成します。
index.html.erb
ファイルの該当箇所を上記のコードのように変更しましょう。
index.html.erb
ファイルを変更した後は、社員情報一覧画面の「詳細情報」と「編集」のリンクをクリックし、リファクタリング後もリンクが正常に機能しているか確認しましょう。
削除操作に関しては、method
オプションでdelete
を指定し、ユーザーに確認を促すダイアログは、data
属性の{ confirm: 'メッセージ' }
で指定します。
1
<%= link_to 'テキスト', 'リンク先のパス', method: :delete, data: { confirm: '確認ダイアログのメッセージ' } %>
先ほどの詳細情報と編集のリンクと同様に、削除のリンクも*_pathメソッドの引数としてインスタンスを渡し、動的にパスを生成します。
show.html.erb
の削除ボタンを以下のように変更しましょう。
1
2
3
4
5
6
7
<%# ...[中略]... %>
<%= link_to '社員情報一覧に戻る', employees_path %>
<div class='employee-button'>
<a href="/employees/<%= @employee.id %>" class="delete-button" data-method="delete" data-confirm="本当に削除してよろしいですか?">削除</a>
</div>
1
2
3
4
5
6
7
<%# ...[中略]... %>
<%= link_to '社員情報一覧に戻る', employees_path %>
<div class='employee-button'>
<%= link_to '削除', employee_path(@employee), method: :delete, data: { confirm: '本当に削除してよろしいですか?' }, class: 'delete-button' %>
</div>
show.html.erb
ファイルの変更後、新規社員を作成し、その削除機能が正しく動作するかチェックしてみましょう。
部分テンプレートの利用
部分テンプレートとは、ビューの中で繰り返し使用されるコードを一か所にまとめ、異なる箇所から何度も呼び出すことができる小さなテンプレートファイルのことです。
複数のビューで共有したい箇所を切り出し、別ファイルにしてファイル名の先頭にアンダースコア(_)を付けて保存すると、それが部分テンプレートとなります。
1
2
3
4
<footer>
<p>© <%= Time.now.year %> My Website</p>
</footer>
<%# Time.now.yearは、Ruby言語で現在の年を取得するためのコードです %>
さらに、部分テンプレートはrender
メソッドで呼び出すことができます。
例えば、articles
ビューディレクトリがある場合、new.html.erb
から同じディレクトリ内の部分テンプレートをrender '部分テンプレート名'
と記述することで呼び出せます。ファイル名の先頭にあるアンダースコアと拡張子は省略できます。
1
2
<%= render 'form' %>
<%# 同じディレクトリ内の _form.html.erb 部分テンプレートを呼び出す -->
異なるディレクトリにある部分テンプレートを呼び出す場合は、app/views
ディレクトリからの相対パスを文字列として引数に指定します。
例えば、articles
ビューディレクトリ内のビューファイルからshard
ビューディレクトリ内の部分テンプレートを呼び出す場合、render 'shard/部分テンプレート名'
と記述することで呼び出せます。
1
2
<%= render 'shared/header' %>
<%# app/views/sharedディレクトリ内の _header.html.erb 部分テンプレートを呼び出す -->
ここまでの部分テンプレートの基礎については、Railsの基礎で学習しました。このセクションでは、インスタンスを利用した部分テンプレートの使い方について学習します。
部分テンプレートに変数を渡す方法
render
メソッドで部分テンプレートを呼び出す際に、locals
オプションを利用することで部分テンプレート内でのみ使用できる変数(ローカル変数)を渡すことができます。
基本的な構文
部分テンプレートに変数を渡す際にはlocals
オプションを使用します。通常、部分テンプレートはrender '部分テンプレート名'
で呼び出せますが、locals
オプションを使う場合は、部分テンプレート名を明示的に指定するpartial
オプションを使用する必要があります。
locals
オプションは、render
メソッドの引数としてハッシュ形式で指定します。キーは部分テンプレート内で使用する変数名、値はその変数に設定したいオブジェクトです。
1
<%= render partial: '部分テンプレート名', locals: { 変数名: 値 } %>
例えばarticles
ビューディレクトリ内にあるedit.html.erb
から、同じディレクトリにある部分テンプレート_form.html.erb
を呼び出す場合で確認してみます。
同じディレクトリ内にある部分テンプレートを呼び出す場合は、ファイル名の先頭の_
と拡張子を省略し、以下のように記述することで呼び出せることを既に学習しています。
1
<%= render 'form' %>
このとき、以下のように記述して部分テンプレートの_form.html.erb
内で使用する変数を渡すことができます。
1
<%= render partial: 'form', locals: { name: '山田' } %>
_form.html.erb
内では、以下のようにname
という変数を使用することができます。
1
<p>私の名前は、<%= name %>です。</p>
上記のコードにより、_form.html.erb
内でname
という変数を使って'山田'
の値を表示することができます。
実際にブラウザ上には、以下のように表示されます。
複数の変数を渡す方法
部分テンプレートに複数の変数を渡す場合、カンマを用いて複数のキーと値を指定します。
1
<%= render partial: 'form', locals: { name: '山田', address: '東京都渋谷区', age: 20 } %>
呼び出された部分テンプレートの_form.html.erb
では、以下のようにローカル変数を使用することができます。
1
2
3
<p>私の名前は、<%= name %>です。</p>
<p>私は、<%= address %>に住んでいます。</p>
<p>私の年齢は、<%= age %>歳です。</p>
実際にブラウザ上には、以下のように表示されます。
インスタンスを使用する場合
locals
オプションでは、コントローラで定義したインスタンス変数もローカル変数として部分テンプレートに渡すことができます。
例えば、以下のようにedit
アクションでは、特定のユーザー情報が入る@user
が定義されているとします。
1
2
3
def edit
@user = User.find(params[:id])
end
部分テンプレートにインスタンス変数@user
をuser
という名前のローカル変数として部分テンプレートの_form.html.erb
に渡す場合、以下のように記述します。
1
<%= render partial: 'form', locals: { user: @user } %>
部分テンプレートの_form.html.erb
内では、以下のように渡された変数user
を利用して、ユーザーの名前を表示することができます。
1
<p>私の名前は、<%= user.name %>です。</p>
部分テンプレート内では、locals
オプションで指定した名前(この場合はuser
)を使って、コントローラで設定したインスタンス変数(@user
)の内容にアクセスできるんだね。
省略形による構文
以下のように、locals:
を省略して直接ハッシュのキーと値を render
メソッドに渡す書き方も可能です。locals:
を省略した場合、partial:
も省略できます。
1
<%= render partial: 'form', locals: { user: @user } %>
1
<%= render 'form', user: @user %>
部分テンプレートを使用してビューを整理しよう
それでは、実際に部分テンプレートを使用してビューの中で繰り返し使用されるコードを整理してメンテナンスしやすくしましょう。
現在、アプリケーション内のapp/views/employees/
ディレクトリにあるnew.html.erb
とedit.html.erb
ファイルで、以下の黄色い枠線で囲まれた部分が「登録する」と「更新する」ボタン以外で重複しています。
上記の箇所を部分テンプレートを使用して、整理していきます。
app/views/employees/
ディレクトリ内に_form.html.erb
という部分テンプレートのファイルを作成しましょう。
部分テンプレートの_form.html.erb
ファイルには、以下のようにnew.html.erb
ファイルからform_with
以降のコードを移しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%= form_with model: @employee, local: true, class: 'employee-form' do |f| %>
<div class="form-group">
<%= f.label :name, '名前:' %>
<%= f.text_field :name %>
</div>
<div class="form-group">
<%= f.label :birthday, '生年月日:' %>
<%= f.date_field :birthday %>
</div>
<div class="form-group">
<%= f.label :department_id, '部署:' %>
<%= f.collection_select :department_id, @departments, :id, :name %>
</div>
<div class="form-submit">
<%= f.submit '登録する' %>
</div>
<% end %>
<%= link_to '社員情報一覧に戻る', employees_path %>
new.html.erb
ファイルは、以下のようになります。
1
<h2>新規社員登録</h2>
edit.html.erb
ファイルも、以下のようにh2
部分以外を削除しておきましょう。
1
<h2>社員情報の編集</h2>
new.html.erb
ファイルから移動したコードを含む_form.html.erb
ファイル内では、以下の箇所でインスタンス変数が利用されています。
部分テンプレートを呼び出す際に、これらのインスタンス変数をローカル変数として渡せるようにするため、@employee
をemployee
に、@departments
をdepartments
に修正しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%= form_with model: employee, local: true, class: 'employee-form' do |f| %>
<div class="form-group">
<%= f.label :name, '名前:' %>
<%= f.text_field :name %>
</div>
<div class="form-group">
<%= f.label :birthday, '生年月日:' %>
<%= f.date_field :birthday %>
</div>
<div class="form-group">
<%= f.label :department_id, '部署:' %>
<%= f.collection_select :department_id, departments, :id, :name %>
</div>
<div class="form-submit">
<%= f.submit '登録する' %>
</div>
<% end %>
<%= link_to '社員情報一覧に戻る', employees_path %>
さらに、ボタンのテキストは現在「登録する」と固定されていますが、更新フォームでは「更新する」が適切です。
1
2
3
<div class="form-submit">
<%= f.submit '登録する' %>
</div>
1
2
3
<div class="form-submit">
<%= f.submit '更新する' %>
</div>
このままでは、更新フォームで部分テンプレートを使用する際にも「登録する」が表示されてしまいます。これを柔軟に対応するために、ボタンテキストも部分テンプレートの呼び出し時に変数で渡せるように'登録する'
の部分をbutton_text
変数に修正しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%= form_with model: employee, local: true, class: 'employee-form' do |f| %>
<div class="form-group">
<%= f.label :name, '名前:' %>
<%= f.text_field :name %>
</div>
<div class="form-group">
<%= f.label :birthday, '生年月日:' %>
<%= f.date_field :birthday %>
</div>
<div class="form-group">
<%= f.label :department_id, '部署:' %>
<%= f.collection_select :department_id, departments, :id, :name %>
</div>
<div class="form-submit">
<%= f.submit button_text %>
</div>
<% end %>
<%= link_to '社員情報一覧に戻る', employees_path %>
new.html.erb
とedit.html.erb
に部分テンプレートの呼び出しコードを以下のように追加しましょう。
ボタンテキストを適切に設定するため、new.html.erb
ではbutton_text
に'登録する'
を、edit.html.erb
では'更新する'
をそれぞれ渡します。
1
2
3
<h2>新規社員登録</h2>
<%= render partial: 'form', locals: { employee: @employee, departments: @departments, button_text: '登録する' } %>
1
2
3
<h2>社員情報の編集</h2>
<%= render partial: 'form', locals: { employee: @employee, departments: @departments, button_text: '更新する' } %>
今回、locals
オプションで複数のローカル変数を渡すため、locals:
を明示的に使用し、どの変数がテンプレートに渡されるのかを明確にします。また、locals
オプションを省略していないため、partial
オプションも指定します。
ここまでの変更により、登録フォームと更新フォームが正しく表示されるか確認しましょう。サーバーを起動し、Webブラウザで表示を確認します。
以下のように登録フォームと更新フォームが正しく表示されれば、問題ありません。
before_actionの活用
before_action
を使うと、特定のコントローラのアクションが実行される前に、あらかじめ定められたメソッドを実行するよう設定できます。
1
2
3
4
5
6
7
8
9
10
11
class コントローラ名 < ApplicationController
before_action :メソッド名
# ...
private
def メソッド名
#アクションの前に実行したい処理を記述する
end
end
これにより、いくつかのアクションで共通の処理を一度に設定でき、コードの重複を減らし、整理されたコードを保つことができます。
特定のコントローラのアクションが実行される前、自動的に別の処理をしてほしいときに使うんだね!
たとえば、ユーザーがログインしているかチェックしたいときや、ある情報をデータベースから取得してくるときに便利だよ!
before_actionの基本的な使い方
before_action
では、only
オプションやexcept
オプションを使用して、特定のアクションにのみbefore_action
を適用するか、または特定のアクションを除外して適用するかを設定できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class コントローラ名 < ApplicationController
# 特定のアクションにのみbefore_actionを適用する場合
before_action :メソッド名, only: [:アクション名1, :アクション名2]
# 特定のアクションを除外してbefore_actionを適用する場合
# before_action :メソッド名, except: [:アクション名3, :アクション名4]
# その他のアクションメソッド...
# ...
private
def メソッド名
# 特定のアクションの前に実行したい処理を記述する
end
end
また、before_action
で使用されるメソッドは、通常、そのコントローラ内でのみ使用されるべきものであり、他のコントローラやクラスから直接呼び出されることはないため、private
として定義します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class コントローラ名 < ApplicationController
# 特定のアクションにのみbefore_actionを適用する場合
before_action :メソッド名, only: [:アクション名1, :アクション名2]
# 特定のアクションを除外してbefore_actionを適用する場合
# before_action :メソッド名, except: [:アクション名3, :アクション名4]
# その他のアクションメソッド...
# ...
private
def メソッド名
# 特定のアクションの前に実行したい処理を記述する
end
end
onlyオプションによるアクションの限定
例えば、以下のようにonly
オプションで[:show, :edit]
と指定すると、show
とedit
アクションが呼び出される前にset_article
メソッドが実行されます。
index
アクションでは、set_article
メソッドは実行されません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit]
def show
# showアクションの前にset_articleが実行されます
end
def edit
# editアクションの前にset_articleが実行されます
end
def index
# ここではset_articleは実行されません
end
private
def set_article
#特定のアクションの前に実行したい処理を記述する
end
end
exceptオプションによるアクションの除外
次に、以下のようにexcept
オプションで[:show, :edit]
を指定すると、show
とedit
アクションが実行される前にはset_article
メソッドが実行されませんが、それ以外のアクションが呼び出される前にはset_article
メソッドが実行されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ArticlesController < ApplicationController
before_action :set_article, except: [:show, :edit]
def show
# ここではset_articleは実行されません
end
def edit
# ここではset_articleは実行されません
end
def index
# showアクションの前にset_articleが実行されます
end
def new
# showアクションの前にset_articleが実行されます
end
private
def set_article
#特定のアクションの前に実行したい処理を記述する
end
end
before_actionの利点と活用シーン
before_action
は、特定の条件下で重複する処理をまとめるのに非常に便利です。コントローラ内のアクション間で共通の処理を実行する場合、このコールバックを使用すると、コードの重複を避け、一貫性を保ちながらコードを整理・簡潔に保つことができます。
例えば、Articlesコントローラ内で複数のアクションが同じArticle
モデルから特定の記事を取得しているとします。以下のコードでは、show
、edit
、update
、destroy
アクション内で同じ行を繰り返し書いています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ArticlesController < ApplicationController
# ...
def show
@article = Article.find(params[:id])
# その他の処理
end
def edit
@article = Article.find(params[:id])
# その他の処理
end
def update
@article = Article.find(params[:id])
# その他の処理
end
def destroy
@article = Article.find(params[:id])
# その他の処理
end
# ...
end
このような場合、@article = Article.find(params[:id])
の行を以下のようにset_article
メソッドにまとめ、before_action
を用いて特定のアクション実行前にこのメソッドを呼び出すよう設定します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
def show
# @articleはset_articleで設定されています
# その他の処理
end
def edit
# @articleはset_articleで設定されています
# その他の処理
end
def update
# @articleはset_articleで設定されています
# その他の処理
end
def destroy
# @articleはset_articleで設定されています
# その他の処理
end
# ...
private
def set_article
@article = Article.find(params[:id])
end
end
このようにすることで、コードの重複を減らし、読みやすさとメンテナンス性を向上させることができます。
それぞれのアクションで同じ処理を書かずに済むため、バグの発生リスクも減らすことができます。また、将来的にコードを変更する必要が生じた場合にも、一箇所の修正で済むため、効率的です。
before_actionを使用して共通処理をまとめよう
それでは、実際にbefore_action
を使用して、複数のアクションで共通の処理を1箇所にまとめ、コードの整理と管理を簡単にしましょう。
app/controllers/
ディレクトリのemployees_controller.rb
ファイルでは、以下の緑の枠で示された部分が複数のアクションで重複しています。
また、@departments = Department.all
も複数の箇所で使用されています。
上記の箇所をbefore_action
を使用して、整理していきます。
複数のアクションで共通して使用される処理をメソッドとして定義します。
以下のようにemployees_controller.rb
ファイルにset_employee
メソッドとset_departments
メソッドを追加して定義しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class EmployeesController < ApplicationController
# ...中略
private
def employee_params
params.require(:employee).permit(:name, :birthday, :department_id)
end
def set_employee
@employee = Employee.find(params[:id])
end
def set_departments
@departments = Department.all
end
end
before_action
を活用し、特定のアクションが実行される前に、先に定義したprivate
メソッドを自動で呼び出すよう設定しましょう。
以下のように@employee = Employee.find(params[:id])
が必要なアクションはshow
, edit
, update
, destroy
です。
これらのアクションが実行される前にset_employee
メソッドが実行されるように設定します。employees_controller.rb
ファイルに以下のようにbefore_action
を追加しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
# ...中略
private
def employee_params
params.require(:employee).permit(:name, :birthday, :department_id)
end
def set_employee
@employee = Employee.find(params[:id])
end
def set_departments
@departments = Department.all
end
end
また、以下のように@departments = Department.all
を使用するアクションはnew
とedit
です。
こちらもbefore_action
で設定し、new
とedit
アクションが実行される前にset_departments
メソッドが呼び出されるようにします。
employees_controller.rb
に以下の1行を追加してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
before_action :set_departments, only: [:new, :edit]
# ...中略
private
def employee_params
params.require(:employee).permit(:name, :birthday, :department_id)
end
def set_employee
@employee = Employee.find(params[:id])
end
def set_departments
@departments = Department.all
end
end
before_action
を設定したことで、各アクションの実行前に必要な処理が自動で行われるようになりました。
以下の重複するコード@employee = Employee.find(params[:id])
(show、edit、update、destroyアクションで使用)および@departments = Department.all
(new、editアクションで使用)を削除して、コードをすっきりさせましょう。
最後にemployees_controller.rb
のコードが整理されたことを確認しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class EmployeesController < ApplicationController
before_action :set_employee, only: [:show, :edit, :update, :destroy]
before_action :set_departments, only: [:new, :edit]
def index
# 社員の一覧を取得する処理
@employees = Employee.all
end
def show
# 特定の社員の詳細情報を取得する処理
end
def new
# 新しい社員の登録フォームを表示する処理
@employee = Employee.new
end
def create
# 新しい社員を登録する処理
@employee = Employee.create(employee_params)
end
def edit
# 既存の社員情報を編集するためのフォームを表示する処理
end
def update
# 既存の社員情報を更新する処理
@employee.update(employee_params)
end
def destroy
# 特定の社員を削除する処理
@employee.destroy
end
private
def employee_params
params.require(:employee).permit(:name, :birthday, :department_id)
end
def set_employee
@employee = Employee.find(params[:id])
end
def set_departments
@departments = Department.all
end
end
リファクタリングでコントローラがスッキリしたね!before_actionを見れば、どんな共通処理が行われているか一目で分かるね。
その通りだね!before_actionはアクションが実行される前に共通の処理を行うので、アクション内のコードがどう動くかを理解するための重要な手がかりになるよ。
さいごに
この記事を通じて、Railsアプリケーションのリファクタリングと保守性の向上についての理解を深めることができたでしょう。resources
の活用、名前付きルートとヘルパーの適切な利用、部分テンプレートの導入、そしてbefore_action
の効果的な活用は、Railsアプリケーションの開発効率を大幅に向上させることができます。
この記事のまとめ
- 名前付きヘルパーは、名前付きルートを利用してURLやパスを生成するためのヘルパーメソッドのこと
- before_actionを使うと、特定のコントローラのアクションが実行される前に、あらかじめ定められたメソッドを実行するよう設定できる
この記事で学んだことをTwitterに投稿して、アウトプットしよう!
Twitterの投稿画面に遷移します