すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

リファクタリングと保守の向上

この記事で出来るようになること

はじめに

概要

この記事では、「リファクタリングと保守の向上」をテーマに、Ruby on Railsでの開発効率を向上させる技術について解説します。具体的には、resourcesの効果的な活用、ルーティングの簡潔化、名前付きルートとヘルパーの利用、部分テンプレートの導入、そしてbefore_actionを使用したコントローラの整理方法を学びます。これらのテクニックを通じて、コードの可読性の向上と保守性の強化を目指します。

目標

この章の目標
  • ルーティングの設定を簡素化し、名前付きルートなどを活用する方法を学ぶ。
  • 部分テンプレートで、ビューのコードを整理し再利用性を高める技術を習得する。
  • before_actionで、コントローラ内の共通処理を管理する方法を理解する。

必要な前提条件・事前準備

以前、部分テンプレートの基本的な使用方法については触れましたが、今回は特に変数を活用する応用技術を学びます。そのため、事前に「部分テンプレートの基礎」について復習しておくことで、本章をより深く理解できるでしょう。

必要なもの・知識

resourcesの活用

以前「RESTful開発の基礎」で学んだように、Railsが提供するresourcesメソッドを使用すると、RESTfulルーティングに必要な標準的なルートが自動的に生成され、ルーティングの設定が簡素化されます。

resourcesメソッドを使用する場合
1
2
3
Rails.application.routes.draw do
resources :リソース名
end

例えば、これまでの「RailsでRESTfulなCRUDアプリケーションを作成しよう1(基礎)」では、学習のためにemployeesリソースのルーティングを設定する際に、resourcesメソッドを使用せずにRESTfulなルーティングを手動で定義してきました。

config/routes.rb | 現在のルーティング設定
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行で設定することができます。

例:resourcesメソッドを使用した場合
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_articleedit_articlearticlearticlesという名前付きルートが自動的に生成されます。

例:resourcesメソッドを使用した場合
1
2
3
Rails.application.routes.draw do
  resources :articles
end

一方、resources メソッドを使用せずに手動でRESTfulルーティングを定義する場合、通常は自動的に名前付きルートは生成されません。しかし、特定の条件(※補足説明)に該当する場合、newindex のような一部のアクションに対して自動的に名前付きルートが生成されることがあります。

例えば、「articles」というリソースに対して手動でRESTfulルーティングを設定した場合、newindexアクションに対応する名前付きルートのみが自動的に生成され、他のアクションに対する名前付きルートは生成されません。

また、resourcesメソッドを利用した場合、newアクションに付けられる名前付きルートは通常new_articleとなりますが、手動でルーティングを定義した場合はarticle_newのように命名規則が異なることがあります。

この違いは、resourcesメソッドが一貫した命名規則に従って名前付きルートを生成するのに対し、手動でルーティングを定義する際には特定の命名規則に従わないためです。

今回は学習目的で手動でルーティングを定義したけれど、ルーティングの一貫性と名前付きルートの整合性を維持するため、通常は手動ではなくresourcesメソッドの使用が推奨されるよ!

ぴかわかさん

名前付きルートを手動で設定する方法

resources メソッドを使用すると、名前付きルートが自動的に設定されます。しかし、状況によっては、手動で名前付きルートを設定する必要がある場合もあります。ルーティング定義に as: :名前 を追加することで、ルートに特定の名前を付けることができます。

config/routes.rb
1
2
3
Rails.application.routes.draw do
  HTTPリクエストメソッド 'パス', to: 'コントローラ名#アクション名', as: :名前
end

例えば、resources メソッドを使用せずに手動でルーティングを定義した場合、article_new という名前付きルートが自動生成されます。

しかし、以下のように as オプションを指定することで、new_article という名前付きルートに変更することができます。

例:config/routes.rb
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 という名前付きルートを手動で設定しました。

config/routes.rb | form_withを使用して編集フォームを実装した際の設定例
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を生成できるようになります。

config/routes.rb | form_withを使用して編集フォームを実装した際の設定例
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

実行すると、以下のように設定されたルーティングだけでなく、デフォルトで設定されている多くのルートも表示されます。

以下のように、手動で設定したルーティングであっても、employeesemployees_new といった名前付きルートが自動生成されていること、また as オプションを使用して設定した employee という名前付きルートが存在することが確認できれば問題ありません。

resourcesメソッドを使用してルーティングを再設定しましょう

次に、手動で設定したルーティングから resources メソッドを使った設定へと変更しましょう。config/routes.rb ファイルを開き、以下のように編集してください。

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 として生成されていることが確認できます。

また、indexeditshow アクションなどに対しても自動で名前付きルートが生成されていることがわかります。

ぴっかちゃん

今まではconfig/routes.rbファイルで、どのリクエストがどのアクションに対応しているか確認できたけど、resourcesメソッドを適用すると、直接は分からなくなっちゃうよね。これからルートをどうやって確認すればいいのかな?

その場合はターミナルでrails routesコマンドを実行するといいよ!このコマンドは、名前付きルートの確認に加えて、アプリケーションで定義されている全てのルートと、それぞれが対応するコントローラとアクションの一覧を表示してくれるから、とっても便利だよ。

ぴかわかさん

名前付きヘルパーの基礎

Railsでは、名前付きルートに基づいて対応するヘルパーメソッドが自動的に生成されます。これらのヘルパーメソッドは「名前付きヘルパー」と呼ばれます。

名前付きヘルパーは、名前付きルートを利用してURLやパスを生成するためのヘルパーメソッドです。主に *_path*_url の二種類があります。* の部分には対応する名前付きルートが入ります。

種類 生成 用途
*_path 相対パス 同一アプリケーション内でのページ遷移やリンク生成に使用
*_url 絶対URL ドメインを含む完全なURLが必要な場合に使用
外部リンクやメール内のリンクなど

*_path相対パスを生成し、*_urlは絶対URLを生成します。主にlink_toメソッドなどのビューヘルパー内で使用され、リンクを生成する際に活用されます。

link_toメソッドでは、リンク先のパス*_path*_urlを指定します。

link_toメソッドの基本構文
1
<%= link_to 'テキスト', 'リンク先のパス', method: :HTTPメソッド %>
例: HTTPメソッドを指定しない場合(デフォルトでGETが使用される)
1
<%= link_to 'テキスト', 'リンク先のパス' %>

Railsの基礎で*_pathについて学習したことがありますが、ここで復習としておさらいしてみましょう。さらに引数にインスタンス変数を活用する方法も学習します。

*_pathメソッド

*_path メソッドを使用すると、名前付きルートに基づいて相対パスを生成することができます。link_toメソッドなどを使うことで、名前付きルートに基づいてリンクを生成し、アプリケーション内の異なるページを簡単にリンクすることができます。

例えば、config/routes.rb に以下のようなルート設定があるとします。

config/routes.rb | 例
1
2
3
Rails.application.routes.draw do
  resources :articles
end

この設定により、以下のように名前付きルートが自動で生成されます。

このとき、上記の名前付きルートに対応するヘルパーメソッド、つまり名前付きヘルパーが生成されます。名前付きルートに _path を付けるだけで使用することができます。

例えば、indexアクションに対応する名前付きルートのarticlesを使用してみます。

名前付きルート articles_path を付けて、以下のように link_to メソッドで指定します。また、articles_pathクォーテーションで囲いません。クォーテーションで囲んでしまうと、その文字列自体がリンク先として認識されてしまいます。

link_toメソッドの使い方
1
2
<%= link_to 'テキスト', 'リンク先のパス' %>
<%# HTTPメソッドを指定しない場合、デフォルトでGETが使用されます。 %>
app/views/some_view.html.erb | 例
1
<%= link_to '記事一覧', articles_path %>

上記のarticles_pathは、/articles というパスを生成します。このコードはビューで以下のHTMLに変換されます。

生成される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には、具体的な数値を渡します。例えば、id3を渡した場合、生成されるURLは/articles/3となります。

引数のidに3を渡した場合
1
<%= link_to '詳細を表示', article_path(3) %>
生成されるHTMLの例
1
<a href="/articles/3">詳細を表示</a>

また、数値のidの代わりにモデルのインスタンス自体を直接渡すこともできます。

例えば、@articleに特定の記事のインスタンス(id2)が代入されている場合、以下のようにarticle_pathの引数にそのインスタンス変数を渡すだけで、Railsはインスタンスからidを自動的に取り出し、適切なURLを生成します。

特定の記事のインスタンスが@articleに代入されている場合
1
<%= link_to '詳細を表示', article_path(@article) %>

この場合、以下のリンクが生成されます。

生成されるHTMLの例
1
<a href="/articles/2">詳細を表示</a>

さらに、記事一覧ページで各記事の詳細ページへのリンクを生成する例も確認してみます。

これまでに学習した方法では、まず@articles(全記事のインスタンスが格納されている)をeachメソッドでループし、各記事をarticle変数に代入します。次に、リンクを生成する際にarticle.idで各記事インスタンスのid属性を取得し、それをURLに動的に組み込んでいました。

例 | Articlesコントローラ
1
2
3
def index
  @articles = Article.all  # 全ての記事を取得し @article に代入
end
indexアクションに対応するビュー
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
絶対URLの例
1
http://yourdomain.com/articles/3

例として、config/routes.rbに設定された名前付きルートを利用します。

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は以下のようになります。

生成される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メソッドに変更します。

Webブラウザで名前付きヘルパーを確認しましょう

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: 'メッセージ' }で指定します。

link_toメソッドの基本構文(削除の場合)
1
<%= link_to 'テキスト', 'リンク先のパス', method: :delete, data: { confirm: '確認ダイアログのメッセージ' } %>

先ほどの詳細情報と編集のリンクと同様に、削除のリンクも*_pathメソッドの引数としてインスタンスを渡し、動的にパスを生成します。

show.html.erbの削除ボタンを以下のように変更しましょう。

app/views/employees/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>
app/views/employees/show.html.erb | 変更後
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ファイルの変更後、新規社員を作成し、その削除機能が正しく動作するかチェックしてみましょう。

部分テンプレートの利用

部分テンプレートとは、ビューの中で繰り返し使用されるコードを一か所にまとめ、異なる箇所から何度も呼び出すことができる小さなテンプレートファイルのことです。

複数のビューで共有したい箇所を切り出し、別ファイルにしてファイル名の先頭にアンダースコア(_)を付けて保存すると、それが部分テンプレートとなります。

_footer.html.erb | 部分テンプレートの例
1
2
3
4
<footer>
  <p>&copy; <%= Time.now.year %> My Website</p>
</footer>
<%# Time.now.yearは、Ruby言語で現在の年を取得するためのコードです %>

さらに、部分テンプレートはrenderメソッドで呼び出すことができます。

例えば、articlesビューディレクトリがある場合、new.html.erbから同じディレクトリ内の部分テンプレートをrender '部分テンプレート名'と記述することで呼び出せます。ファイル名の先頭にあるアンダースコアと拡張子は省略できます。

new.html.erb | 同じディレクトリ内の部分テンプレートを呼び出す例
1
2
<%= render 'form' %>
<%# 同じディレクトリ内の _form.html.erb 部分テンプレートを呼び出す -->

異なるディレクトリにある部分テンプレートを呼び出す場合は、app/viewsディレクトリからの相対パスを文字列として引数に指定します。

例えば、articlesビューディレクトリ内のビューファイルからshardビューディレクトリ内の部分テンプレートを呼び出す場合、render 'shard/部分テンプレート名'と記述することで呼び出せます。

app/views/articles/index.html.erb | 異なるディレクトリ内の部分テンプレートを呼び出す例
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を呼び出す場合で確認してみます。

同じディレクトリ内にある部分テンプレートを呼び出す場合は、ファイル名の先頭の_と拡張子を省略し、以下のように記述することで呼び出せることを既に学習しています。

edit.html.erb | 同じディレクトリ内の部分テンプレートを呼び出す例
1
<%= render 'form' %>

このとき、以下のように記述して部分テンプレートの_form.html.erb内で使用する変数を渡すことができます。

edit.html.erb | 部分テンプレートに変数を渡す例
1
<%= render partial: 'form', locals: { name: '山田' }  %>

_form.html.erb内では、以下のようにnameという変数を使用することができます。

_form.html.erb | 渡された変数を使用する例
1
<p>私の名前は、<%= name %>です。</p>

上記のコードにより、_form.html.erb内でnameという変数を使って'山田'の値を表示することができます。

実際にブラウザ上には、以下のように表示されます。

ポイント
  1. localsオプションで部分テンプレートにローカル変数を渡すことができる
  2. localsオプションを使用する際には、partialオプションが必要になる
  3. 変数は部分テンプレート内でのみ使える(例:呼び出し元のnew.html.erbではnameの変数は使えないが、部分テンプレートの_form.html.erbでは使える)

複数の変数を渡す方法

部分テンプレートに複数の変数を渡す場合、カンマを用いて複数のキーと値を指定します。

edit.html.erb | 部分テンプレートに複数の変数を渡す例
1
<%= render partial: 'form', locals: { name: '山田', address: '東京都渋谷区', age: 20  }  %>

呼び出された部分テンプレートの_form.html.erbでは、以下のようにローカル変数を使用することができます。

_form.html.erb | 渡されたローカル変数を使用する例
1
2
3
<p>私の名前は、<%= name %>です。</p>
<p>私は、<%= address %>に住んでいます。</p>
<p>私の年齢は、<%= age %>歳です。</p>

実際にブラウザ上には、以下のように表示されます。

インスタンスを使用する場合

localsオプションでは、コントローラで定義したインスタンス変数もローカル変数として部分テンプレートに渡すことができます。

例えば、以下のようにeditアクションでは、特定のユーザー情報が入る@userが定義されているとします。

users_controller.rb | コントローラの例
1
2
3
def edit
  @user = User.find(params[:id])
end

部分テンプレートにインスタンス変数@useruserという名前のローカル変数として部分テンプレートの_form.html.erbに渡す場合、以下のように記述します。

edit.html.erb | インスタンス変数を部分テンプレートに渡す例
1
<%= render partial: 'form', locals: { user: @user } %>

部分テンプレートの_form.html.erb内では、以下のように渡された変数userを利用して、ユーザーの名前を表示することができます。

_form.html.erb | 渡されたローカル変数を使用する
1
<p>私の名前は、<%= user.name %>です。</p>

ぴっかちゃん

部分テンプレート内では、localsオプションで指定した名前(この場合はuser)を使って、コントローラで設定したインスタンス変数(@user)の内容にアクセスできるんだね。

省略形による構文

以下のように、locals: を省略して直接ハッシュのキーと値を render メソッドに渡す書き方も可能です。locals: を省略した場合、partial: も省略できます。

new.html.erb | 通常の構文
1
<%= render partial: 'form', locals: { user: @user } %>
new.html.erb | 省略形の構文
1
<%= render 'form', user: @user  %>

部分テンプレートを使用してビューを整理しよう

それでは、実際に部分テンプレートを使用してビューの中で繰り返し使用されるコードを整理してメンテナンスしやすくしましょう。

現在、アプリケーション内のapp/views/employees/ディレクトリにあるnew.html.erbedit.html.erbファイルで、以下の黄色い枠線で囲まれた部分が「登録する」と「更新する」ボタン以外で重複しています。

上記の箇所を部分テンプレートを使用して、整理していきます。

部分テンプレートファイルを作成しましょう

app/views/employees/ディレクトリ内に_form.html.erbという部分テンプレートのファイルを作成しましょう。

重複コードを部分テンプレートに移行しましょう

部分テンプレートの_form.html.erbファイルには、以下のようにnew.html.erbファイルからform_with以降のコードを移しましょう。

app/views/employees/_form.html.erb
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ファイルは、以下のようになります。

app/views/employees/new.html.erb
1
<h2>新規社員登録</h2>

edit.html.erbファイルも、以下のようにh2部分以外を削除しておきましょう。

app/views/employees/edit.html.erb
1
<h2>社員情報の編集</h2>
部分テンプレート内のコードを再利用しやすく修正しましょう

new.html.erbファイルから移動したコードを含む_form.html.erbファイル内では、以下の箇所でインスタンス変数が利用されています。

部分テンプレートを呼び出す際に、これらのインスタンス変数をローカル変数として渡せるようにするため、@employeeemployeeに、@departmentsdepartmentsに修正しましょう。

app/views/employees/_form.html.erb | 修正後
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変数に修正しましょう。

app/views/employees/_form.html.erb | 修正後
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.erbedit.html.erbに部分テンプレートの呼び出しコードを以下のように追加しましょう。

ボタンテキストを適切に設定するため、new.html.erbではbutton_text'登録する'を、edit.html.erbでは'更新する'をそれぞれ渡します。

app/views/employees/new.html.erb
1
2
3
<h2>新規社員登録</h2>

<%= render partial: 'form', locals: { employee: @employee, departments: @departments, button_text: '登録する' } %>
app/views/employees/edit.html.erb
1
2
3
<h2>社員情報の編集</h2>

<%= render partial: 'form', locals: { employee: @employee, departments: @departments, button_text: '更新する' } %>

今回、locals オプションで複数のローカル変数を渡すため、locals: を明示的に使用し、どの変数がテンプレートに渡されるのかを明確にします。また、locals オプションを省略していないため、partial オプションも指定します。

Webブラウザで確認しましょう

ここまでの変更により、登録フォームと更新フォームが正しく表示されるか確認しましょう。サーバーを起動し、Webブラウザで表示を確認します。

以下のように登録フォームと更新フォームが正しく表示されれば、問題ありません。

before_actionの活用

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を適用するか、または特定のアクションを除外して適用するかを設定できます。

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として定義します。

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

onlyオプションによるアクションの限定

例えば、以下のようにonlyオプションで[:show, :edit]と指定すると、showeditアクションが呼び出される前にset_articleメソッドが実行されます。

indexアクションでは、set_articleメソッドは実行されません。

例 | onlyオプションの指定方法
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]を指定すると、showeditアクションが実行される前にはset_articleメソッドが実行されませんが、それ以外のアクションが呼び出される前にはset_articleメソッドが実行されます。

例 | exceptオプションの指定方法
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モデルから特定の記事を取得しているとします。以下のコードでは、showeditupdatedestroyアクション内で同じ行を繰り返し書いています。

例 | 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
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を用いて特定のアクション実行前にこのメソッドを呼び出すよう設定します。

例 | 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はRailsコントローラ内で利用され、特定のアクションが実行される前に定義したメソッドを実行する。これにより、共通の処理をアクションごとに書かずに済み、コードの整理と再利用を容易にする。

before_actionを使用して共通処理をまとめよう

それでは、実際にbefore_actionを使用して、複数のアクションで共通の処理を1箇所にまとめ、コードの整理と管理を簡単にしましょう。

app/controllers/ディレクトリのemployees_controller.rbファイルでは、以下の緑の枠で示された部分が複数のアクションで重複しています。

また、@departments = Department.allも複数の箇所で使用されています。

上記の箇所をbefore_actionを使用して、整理していきます。

共通処理をメソッドにまとめましょう

複数のアクションで共通して使用される処理をメソッドとして定義します。

以下のようにemployees_controller.rbファイルにset_employeeメソッドとset_departmentsメソッドを追加して定義しましょう。

app/controllers/employees_controller.rb
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を活用して共通処理を最適化しましょう

before_actionを活用し、特定のアクションが実行される前に、先に定義したprivateメソッドを自動で呼び出すよう設定しましょう。

以下のように@employee = Employee.find(params[:id])が必要なアクションはshow, edit, update, destroyです。

これらのアクションが実行される前にset_employeeメソッドが実行されるように設定します。employees_controller.rbファイルに以下のようにbefore_actionを追加しましょう。

app/controllers/employees_controller.rb
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を使用するアクションはneweditです。

こちらもbefore_actionで設定し、neweditアクションが実行される前にset_departmentsメソッドが呼び出されるようにします。

employees_controller.rbに以下の1行を追加してください。

app/controllers/employees_controller.rb
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のリファクタリング結果を確認しましょう

最後にemployees_controller.rbのコードが整理されたことを確認しましょう。

app/controllers/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を使うと、特定のコントローラのアクションが実行される前に、あらかじめ定められたメソッドを実行するよう設定できる