PageSourceSearch

https://railsguides.jp/getting_started.html

html railsguides.jp collected 2026-09-24 08:41:39 UTC 291,396 bytes, 3,487 lines download raw bytes

1<!DOCTYPE html>
2<html lang="ja">
3  <head>
4      <!-- Google Tag Manager -->
5      
5<script>
vendor: 367 bytes, lines 5-9
5(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
6              new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
7          j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
8          'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
9      })(window,document,'script','dataLayer','
9GTM-KSKVTCG
vendor: 3 bytes, line 9
9');
9</script>
9
10      <!-- End Google Tag Manager -->
11    <title>
12  Rails をはじめよう - Railsガイド
13</title>
14
15<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
16<meta name="viewport"    content="width=device-width, initial-scale=1">
17<meta name="description" content="Railsのインストール方法と最初のRailsアプリケーションの作成に必要なすべてを解説します。">
18<meta name="keywords"    content="Ruby Rails ガイド 体系的 入門 無料">
19<meta name="author"      content="YassLab 株式会社">
20<link rel="author" type="text/plain" href="https://railsguides.jp/humans.txt">
21
22
23<meta property="fb:admins"      content="715330868">
24<meta property="fb:app_id"      content="614116885378153">
25<meta property="og:type"        content="article">
26<meta property="og:site_name"   content="Rails をはじめよう - Railsガイド">
27<meta property="og:title"       content="Rails をはじめよう - Railsガイド">
28<meta property="og:description" content="Railsのインストール方法と最初のRailsアプリケーションの作成に必要なすべてを解説します。">
29<meta property="og:image"       content="https://railsguides.jp/images/cover_for_facebook.png">
30<meta property="og:url"         content="https://railsguides.jp/getting_started.html">
31
32<meta name="twitter:card"        content="summary">
33<meta name="twitter:site"        content="@RailsGuidesJP">
34<meta name="twitter:title"       content="Rails をはじめよう - Railsガイド">
35<meta name="twitter:description" content="Railsのインストール方法と最初のRailsアプリケーションの作成に必要なすべてを解説します。">
36<meta name="twitter:url"         content="https://railsguides.jp/getting_started.html">
37<meta name="twitter:image"       content="https://railsguides.jp/images/cover_for_twitter.png">
38
39<meta name="apple-mobile-web-app-title" content="Railsガイド">
40<meta name="application-name" content="Railsガイド">
41
42<link href="/railsguides/images/favicon.ico" rel="shortcut icon" type="image/x-icon">
43
44<link rel="apple-touch-icon" sizes="57x57"   href="/railsguides/images/favicons/apple-icon-57x57.png">
45<link rel="apple-touch-icon" sizes="60x60"   href="/railsguides/images/favicons/apple-icon-60x60.png">
46<link rel="apple-touch-icon" sizes="72x72"   href="/railsguides/images/favicons/apple-icon-72x72.png">
47<link rel="apple-touch-icon" sizes="76x76"   href="/railsguides/images/favicons/apple-icon-76x76.png">
48<link rel="apple-touch-icon" sizes="114x114" href="/railsguides/images/favicons/apple-icon-114x114.png">
49<link rel="apple-touch-icon" sizes="120x120" href="/railsguides/images/favicons/apple-icon-120x120.png">
50<link rel="apple-touch-icon" sizes="144x144" href="/railsguides/images/favicons/apple-icon-144x144.png">
51<link rel="apple-touch-icon" sizes="152x152" href="/railsguides/images/favicons/apple-icon-152x152.png">
52<link rel="apple-touch-icon" sizes="180x180" href="/railsguides/images/favicons/apple-icon-180x180.png">
53<link rel="icon" type="image/png" sizes="16x16"   href="/railsguides/images/favicons/favicon-16x16.png">
54<link rel="icon" type="image/png" sizes="32x32"   href="/railsguides/images/favicons/favicon-32x32.png">
55<link rel="icon" type="image/png" sizes="96x96"   href="/railsguides/images/favicons/favicon-96x96.png">
56<link rel="icon" type="image/png" sizes="192x192" href="/railsguides/images/favicons/android-icon-192x192.png">
57
58<link rel="manifest" href="/manifest.json">
59<link rel="mask-icon" href="/railsguides/images/favicons/safari-pinned-tab.svg" color="#c94132">
60<meta name="msapplication-TileColor" content="#ffffff">
61<meta name="msapplication-TileImage" content="/railsguides/images/favicons/ms-icon-144x144.png">
62<meta name="theme-color" content="#ffffff">
63
64
65    <meta name="csrf-param" content="authenticity_token" />
66<meta name="csrf-token" content="cenngHxABlnLEXfJl36HUh1ht5c5krjHZlDySJyTlzqWRVaTESUk-WI3Yhs0caVfZapV8ER50IeaHbe0KeIhNw" />
67    
68
69    <link rel="stylesheet" href="/railsguides/stylesheets/style.css" media="screen" /> <!-- main.css 読んでいる部分 -->
70    <link rel="stylesheet" href="/railsguides/stylesheets/print.css" media="print" />
71    <link rel="stylesheet" href="/railsguides/stylesheets/rouge_highlight_github.css" media="screen" />
72    <link rel="stylesheet" href="/assets/application-8f12ae681c23165e8446f8779f3509c694016d7ae2a7a36b78a4164d24586436.css" />
73    
73<script src="/assets/application-4180822f2ab92d33017da20e0e2534adddf3ed219caea290056a6a1f25195c1d.js"></script>
73
74    
74<script src="/assets/version-b316c4f4762375f6eb16dadabcb4a189d55a99a421379bcd10102997d3b106d3.js"></script>
74
75      
75<script src="/assets/switch_disable_darkmode-0ce494430f9e27bb289c0fcaceba79fcc8c2362585160ae034fa5c5f3bf09c4b.js"></script>
75
76
77    <!--twemoji-->
78    
78<script src="/assets/twemoji-9ba82527eaa71e81e053641db99fee3d45605b0666c9495ce80fa260ab5b66d6.js"></script>
78
79
80    
80<script src="https://kit.fontawesome.com/82f32b9df5.js" crossorigin="anonymous"></script>
80
81  </head>
82
83  <body>
84      <!-- Google Tag Manager (noscript) -->
vendor: 74 bytes, lines 84-85
84
85      <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
85GTM-KSKVTCG
vendor: 97 bytes, lines 85-87
85"
86        height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
87      
87<!-- End Google Tag Manager (noscript) -->
88
89    <div id="fb-root"></div>
90<script>
91 (function(d, s, id) {
92   var js, fjs = d.getElementsByTagName(s)[0];
93   if (d.getElementById(id)) return;
94   js = d.createElement(s); js.id = id;
95   js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&appId=614116885378153&version=v2.0";
96   fjs.parentNode.insertBefore(js, fjs);
97 }(document, 'script', 'facebook-jssdk'));
98</script>
98
99
100
101
102<!--
103<div class="header_news">
104  <div class="header_news__container">
105    <a href="https://note.com/yasslab/n/n85fc874140d7?magazine_key=ma5df1164c798" target="_blank" rel="noopener">
106      【メンテナンスのお知らせ】2025年10月26日(日)4:30~5:00の間メンテナンスのためサイトにアクセスできなくなります。
107    </a>
108  </div>
109</div>
110-->
111
112<div id="header" class="fixed">
113  <div class="wrapper clearfix">
114    <h1><a href="/" title="ホームに戻る">Rails ガイド</a></h1>
115    <div class='version-select'>
116      <select name="verselect" id="verselect"><option selected="selected" value="8.1">v8.1</option>
117<option value="8.0">v8.0</option>
118<option value="7.2">v7.2</option>
119<option value="7.1">v7.1</option>
120<option value="7.0">v7.0</option>
121<option value="6.1">v6.1</option>
122<option value="6.0">v6.0</option>
123<option value="5.2">v5.2</option>
124<option value="5.1">v5.1</option>
125<option value="5.0">v5.0</option>
126<option value="4.2">v4.2</option></select>
127    </div>
128    <div id="nav">
129      <ul class="header-toc-pc ">
130        <li class="guides-index guides-index-large">
131            <a href="/" id="guidesMenu" class="guides-index-item nav-item">ガイド目次</a>
132            <div id="guides" class="clearfix" style="display: none;">
133              <hr>
134              <div class="guides-scroll">
135                <dl class="L">
136                    <dt>はじめに</dt>
137                        <dd><a href="/getting_started.html">Rails をはじめよう</a></dd>
138                        <dd><a href="/install_ruby_on_rails.html">Ruby on Rails インストールガイド</a></dd>
139                        <dd><a href="/getting_started_with_devcontainer.html">Dev Container での開発ガイド</a></dd>
140                        <dd><a href="/sign_up_and_settings.html">演習:ユーザー登録・設定機能の追加</a></dd>
141                        <dd><a href="/wishlists.html">演習:ウィッシュリスト機能の追加</a></dd>
142                    <dt>モデル</dt>
143                        <dd><a href="/active_record_basics.html">Active Record の基礎</a></dd>
144                        <dd><a href="/active_record_migrations.html">Active Record マイグレーション</a></dd>
145                        <dd><a href="/active_record_validations.html">Active Record バリデーション</a></dd>
146                        <dd><a href="/active_record_callbacks.html">Active Record コールバック</a></dd>
147                        <dd><a href="/association_basics.html">Active Record の関連付け</a></dd>
148                        <dd><a href="/active_record_querying.html">Active Record クエリインターフェイス</a></dd>
149                        <dd><a href="/active_model_basics.html">Active Model の基礎</a></dd>
150                    <dt>ビュー</dt>
151                        <dd><a href="/action_view_overview.html">Action View の概要</a></dd>
152                        <dd><a href="/layouts_and_rendering.html">レイアウトとレンダリング</a></dd>
153                        <dd><a href="/action_view_helpers.html">Action View ヘルパー</a></dd>
154                        <dd><a href="/form_helpers.html">Action View フォームヘルパー</a></dd>
155                    <dt>コントローラ</dt>
156                        <dd><a href="/action_controller_overview.html">Action Controller の概要</a></dd>
157                        <dd><a href="/action_controller_advanced_topics.html">Action Controller の高度なトピック</a></dd>
158                        <dd><a href="/routing.html">Rails のルーティング</a></dd>
159                    <dt>他の構成要素</dt>
160                        <dd><a href="/active_support_core_extensions.html">Active Support コア拡張機能</a></dd>
161                        <dd><a href="/action_mailer_basics.html">Action Mailer の基礎</a></dd>
162                        <dd><a href="/action_mailbox_basics.html">Action Mailbox の基礎</a></dd>
163                        <dd><a href="/action_text_overview.html">Action Text の概要</a></dd>
164                        <dd><a href="/active_job_basics.html">Active Job の基礎</a></dd>
165                        <dd><a href="/active_storage_overview.html">Active Storage の概要</a></dd>
166                        <dd><a href="/action_cable_overview.html">Action Cable の概要</a></dd>
167                        <dd><a href="/webpacker.html">Webpacker の概要</a></dd>
168                </dl>
169                <dl class="R">
170                    <dt>高度なトピック</dt>
171                        <dd><a href="/i18n.html">Rails 国際化(I18n)API</a></dd>
172                        <dd><a href="/testing.html">Rails テスティングガイド</a></dd>
173                        <dd><a href="/debugging_rails_applications.html">Rails アプリケーションのデバッグ</a></dd>
174                        <dd><a href="/configuring.html">Rails アプリケーションの設定項目</a></dd>
175                        <dd><a href="/command_line.html">コマンドラインツール</a></dd>
176                        <dd><a href="/asset_pipeline.html">アセットパイプライン</a></dd>
177                        <dd><a href="/working_with_javascript_in_rails.html">Rails で JavaScript を利用する</a></dd>
178                        <dd><a href="/initialization.html">Rails の初期化プロセス</a></dd>
179                        <dd><a href="/autoloading_and_reloading_constants.html">Rails の自動読み込みと再読み込み</a></dd>
180                        <dd><a href="/classic_to_zeitwerk_howto.html">Classic から Zeitwerk への移行</a></dd>
181                        <dd><a href="/active_support_instrumentation.html">Active Support Instrumentation で計測</a></dd>
182                        <dd><a href="/api_app.html">Rails による API 専用アプリケーション</a></dd>
183                        <dd><a href="/tuning_performance_for_deployment.html">デプロイ用パフォーマンスチューニング</a></dd>
184                        <dd><a href="/caching_with_rails.html">Rails のキャッシュ機構</a></dd>
185                        <dd><a href="/security.html">Rails セキュリティガイド</a></dd>
186                        <dd><a href="/error_reporting.html">Rails アプリケーションのエラー通知</a></dd>
187                    <dt>Active Recordの高度な話題</dt>
188                        <dd><a href="/active_record_postgresql.html">Active Record と PostgreSQL</a></dd>
189                        <dd><a href="/active_record_encryption.html">Active Record と暗号化</a></dd>
190                        <dd><a href="/active_record_multiple_databases.html">Active Record の複数データベース対応</a></dd>
191                        <dd><a href="/active_record_composite_primary_keys.html">Active Record の複合主キー</a></dd>
192                    <dt>Rails を拡張する</dt>
193                        <dd><a href="/plugins.html">Rails プラグイン作成入門</a></dd>
194                        <dd><a href="/rails_on_rack.html">Rails と Rack</a></dd>
195                        <dd><a href="/generators.html">Rails ジェネレータとテンプレート入門</a></dd>
196                        <dd><a href="/engines.html">Rails エンジン入門</a></dd>
197                        <dd><a href="/threading_and_code_execution.html">Rails のスレッドとコード実行</a></dd>
198                    <dt>Ruby on Rails に貢献する</dt>
199                        <dd><a href="/contributing_to_ruby_on_rails.html">Ruby on Rails に貢献する方法</a></dd>
200                        <dd><a href="/development_dependencies_install.html">Rails コア開発環境の構築方法</a></dd>
201                        <dd><a href="/api_documentation_guidelines.html">API ドキュメント作成ガイドライン</a></dd>
202                        <dd><a href="/ruby_on_rails_guides_guidelines.html">Rails ガイドのガイドライン</a></dd>
203                    <dt>メンテナンスポリシー</dt>
204                        <dd><a href="/maintenance_policy.html">Ruby on Rails のメンテナンスポリシー</a></dd>
205                    <dt>リリースノート</dt>
206                        <dd><a href="/upgrading_ruby_on_rails.html">Rails アップグレードガイド</a></dd>
207                        <dd><a href="/8_1_release_notes.html">Rails 8.1 - 2025/10</a></dd>
208                        <dd><a href="/8_0_release_notes.html">Rails 8.0 - 2024/11</a></dd>
209                        <dd><a href="/7_2_release_notes.html">Rails 7.2 - 2024/08</a></dd>
210                        <dd><a href="/7_1_release_notes.html">Rails 7.1 - 2023/10</a></dd>
211                        <dd><a href="/7_0_release_notes.html">Rails 7.0 - 2021/12</a></dd>
212                        <dd><a href="/6_1_release_notes.html">
212Rails 6.1 - 2020/12</a></dd>
213                        <dd><a href="/6_0_release_notes.html">Rails 6.0 - 2019/08</a></dd>
214                        <dd><a href="/5_2_release_notes.html">Rails 5.2 - 2018/04</a></dd>
215                        <dd><a href="/5_1_release_notes.html">Rails 5.1 - 2017/04</a></dd>
216                        <dd><a href="/5_0_release_notes.html">Rails 5.0 - 2016/06</a></dd>
217                        <dd><a href="/4_2_release_notes.html">Rails 4.2 - 2014/12</a></dd>
218                        <dd><a href="/4_1_release_notes.html">Rails 4.1 - 2014/04</a></dd>
219                        <dd><a href="/4_0_release_notes.html">Rails 4.0 - 2013/06</a></dd>
220                        <dd><a href="/3_2_release_notes.html">Rails 3.2 - 2012/01</a></dd>
221                        <dd><a href="/3_1_release_notes.html">Rails 3.1 - 2011/08</a></dd>
222                        <dd><a href="/3_0_release_notes.html">Rails 3.0 - 2010/08</a></dd>
223                        <dd><a href="/2_3_release_notes.html">Rails 2.3 - 2009/03</a></dd>
224                        <dd><a href="/2_2_release_notes.html">Rails 2.2 - 2008/11</a></dd>
225                </dl>
226              </div>
227            </div>
228          </li>
229         </ul>
230      <ul id="nav-sp">
231        <li class="nav-item-pc"><a class="nav-item" href="/contributing_to_ruby_on_rails.html">貢献する</a></li>
232        <li><a class="nav-item" href="/ebook">電子書籍</a></li>
233        <li><a class="nav-item" href="/pro">Proプラン</a></li>
234          <li class="guides-index guides-index-small">
235            <select class="guides-index-item nav-item">
236              <option value="/">ガイド目次</option>
237                <optgroup label="はじめに">
238                    <option value="/getting_started.html">Rails をはじめよう</option>
239                    <option value="/install_ruby_on_rails.html">Ruby on Rails インストールガイド</option>
240                    <option value="/getting_started_with_devcontainer.html">Dev Container での開発ガイド</option>
241                    <option value="/sign_up_and_settings.html">演習:ユーザー登録・設定機能の追加</option>
242                    <option value="/wishlists.html">演習:ウィッシュリスト機能の追加</option>
243                </optgroup>
244                <optgroup label="モデル">
245                    <option value="/active_record_basics.html">Active Record の基礎</option>
246                    <option value="/active_record_migrations.html">Active Record マイグレーション</option>
247                    <option value="/active_record_validations.html">Active Record バリデーション</option>
248                    <option value="/active_record_callbacks.html">Active Record コールバック</option>
249                    <option value="/association_basics.html">Active Record の関連付け</option>
250                    <option value="/active_record_querying.html">Active Record クエリインターフェイス</option>
251                    <option value="/active_model_basics.html">Active Model の基礎</option>
252                </optgroup>
253                <optgroup label="ビュー">
254                    <option value="/action_view_overview.html">Action View の概要</option>
255                    <option value="/layouts_and_rendering.html">レイアウトとレンダリング</option>
256                    <option value="/action_view_helpers.html">Action View ヘルパー</option>
257                    <option value="/form_helpers.html">Action View フォームヘルパー</option>
258                </optgroup>
259                <optgroup label="コントローラ">
260                    <option value="/action_controller_overview.html">Action Controller の概要</option>
261                    <option value="/action_controller_advanced_topics.html">Action Controller の高度なトピック</option>
262                    <option value="/routing.html">Rails のルーティング</option>
263                </optgroup>
264                <optgroup label="他の構成要素">
265                    <option value="/active_support_core_extensions.html">Active Support コア拡張機能</option>
266                    <option value="/action_mailer_basics.html">Action Mailer の基礎</option>
267                    <option value="/action_mailbox_basics.html">Action Mailbox の基礎</option>
268                    <option value="/action_text_overview.html">Action Text の概要</option>
269                    <option value="/active_job_basics.html">Active Job の基礎</option>
270                    <option value="/active_storage_overview.html">Active Storage の概要</option>
271                    <option value="/action_cable_overview.html">Action Cable の概要</option>
272                    <option value="/webpacker.html">Webpacker の概要</option>
273                </optgroup>
274                <optgroup label="高度なトピック">
275                    <option value="/i18n.html">Rails 国際化(I18n)API</option>
276                    <option value="/testing.html">Rails テスティングガイド</option>
277                    <option value="/debugging_rails_applications.html">Rails アプリケーションのデバッグ</option>
278                    <option value="/configuring.html">Rails アプリケーションの設定項目</option>
279                    <option value="/command_line.html">コマンドラインツール</option>
280                    <option value="/asset_pipeline.html">アセットパイプライン</option>
281                    <option value="/working_with_javascript_in_rails.html">Rails で JavaScript を利用する</option>
282                    <option value="/initialization.html">Rails の初期化プロセス</option>
283                    <option value="/autoloading_and_reloading_constants.html">Rails の自動読み込みと再読み込み</option>
284                    <option value="/classic_to_zeitwerk_howto.html">Classic から Zeitwerk への移行</option>
285                    <option value="/active_support_instrumentation.html">Active Support Instrumentation で計測</option>
286                    <option value="/api_app.html">Rails による API 専用アプリケーション</option>
287                    <option value="/tuning_performance_for_deployment.html">デプロイ用パフォーマンスチューニング</option>
288                    <option value="/caching_with_rails.html">Rails のキャッシュ機構</option>
289                    <option value="/security.html">Rails セキュリティガイド</option>
290                    <option value="/error_reporting.html">
290Rails アプリケーションのエラー通知</option>
291                </optgroup>
292                <optgroup label="Active Recordの高度な話題">
293                    <option value="/active_record_postgresql.html">Active Record と PostgreSQL</option>
294                    <option value="/active_record_encryption.html">Active Record と暗号化</option>
295                    <option value="/active_record_multiple_databases.html">Active Record の複数データベース対応</option>
296                    <option value="/active_record_composite_primary_keys.html">Active Record の複合主キー</option>
297                </optgroup>
298                <optgroup label="Rails を拡張する">
299                    <option value="/plugins.html">Rails プラグイン作成入門</option>
300                    <option value="/rails_on_rack.html">Rails と Rack</option>
301                    <option value="/generators.html">Rails ジェネレータとテンプレート入門</option>
302                    <option value="/engines.html">Rails エンジン入門</option>
303                    <option value="/threading_and_code_execution.html">Rails のスレッドとコード実行</option>
304                </optgroup>
305                <optgroup label="Ruby on Rails に貢献する">
306                    <option value="/contributing_to_ruby_on_rails.html">Ruby on Rails に貢献する方法</option>
307                    <option value="/development_dependencies_install.html">Rails コア開発環境の構築方法</option>
308                    <option value="/api_documentation_guidelines.html">API ドキュメント作成ガイドライン</option>
309                    <option value="/ruby_on_rails_guides_guidelines.html">Rails ガイドのガイドライン</option>
310                </optgroup>
311                <optgroup label="メンテナンスポリシー">
312                    <option value="/maintenance_policy.html">Ruby on Rails のメンテナンスポリシー</option>
313                </optgroup>
314                <optgroup label="リリースノート">
315                    <option value="/upgrading_ruby_on_rails.html">Rails アップグレードガイド</option>
316                    <option value="/8_1_release_notes.html">Rails 8.1 - 2025/10</option>
317                    <option value="/8_0_release_notes.html">Rails 8.0 - 2024/11</option>
318                    <option value="/7_2_release_notes.html">Rails 7.2 - 2024/08</option>
319                    <option value="/7_1_release_notes.html">Rails 7.1 - 2023/10</option>
320                    <option value="/7_0_release_notes.html">Rails 7.0 - 2021/12</option>
321                    <option value="/6_1_release_notes.html">Rails 6.1 - 2020/12</option>
322                    <option value="/6_0_release_notes.html">Rails 6.0 - 2019/08</option>
323                    <option value="/5_2_release_notes.html">Rails 5.2 - 2018/04</option>
324                    <option value="/5_1_release_notes.html">Rails 5.1 - 2017/04</option>
325                    <option value="/5_0_release_notes.html">Rails 5.0 - 2016/06</option>
326                    <option value="/4_2_release_notes.html">Rails 4.2 - 2014/12</option>
327                    <option value="/4_1_release_notes.html">Rails 4.1 - 2014/04</option>
328                    <option value="/4_0_release_notes.html">Rails 4.0 - 2013/06</option>
329                    <option value="/3_2_release_notes.html">Rails 3.2 - 2012/01</option>
330                    <option value="/3_1_release_notes.html">Rails 3.1 - 2011/08</option>
331                    <option value="/3_0_release_notes.html">Rails 3.0 - 2010/08</option>
332                    <option value="/2_3_release_notes.html">Rails 2.3 - 2009/03</option>
333                    <option value="/2_2_release_notes.html">Rails 2.2 - 2008/11</option>
334                </optgroup>
335            </select>
336          </li>
337            <ul class="nav-login-menu-list">
338      <li class="nav-login-menu-item"><a href="/users/login">ログイン</a></li>
339    </ul>
340
341      </ul>
342      <ul class="nav">
343        <li class="nav-item-pc"><a class="nav-item" href="/contributing_to_ruby_on_rails.html">貢献する</a></li>
344        <li><a class="nav-item" href="/ebook">電子書籍</a><a href="/ebook" class="nav-item-sp"><img alt="電子書籍" data-src="/assets/icon-book-5478e65ecf704b7d3a7103ab3263fd9577c578af09245a9f05397015ac52846b.png" loading="lazy" class="lazyload" src="/spinner.svg" /></a></li>
345        <li><a class="nav-item" href="/pro">Proプラン</a><a href="/pro" class="nav-item-sp"><img alt="プロプラン" data-src="/assets/icon-pro-51e8c7c42c2eeafe597df752a11539de35f82a6fc8a9d08b4178ac2bbc2c4767.png" loading="lazy" class="lazyload" src="/spinner.svg" /></a></li>
346      </ul>
347        <div class="header-icons">
348    <div class="notice-section header-icons__item">
349      <label class="notice-button" id="" for="notice-check">
350        <span class="notice-button__icon"><i class="far fa-bell"></i></span>
351      </label>
352      <input type="checkbox" id="notice-check"/>
353      <div class="notice-list-section">
354        <ul class="notice-list">
355          <li class="notice-list__header">
356            お知らせ
357          </li>
358            <li class="notice-list__item">
359              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/naa68e1ad293a?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>RailsTokyo#5 を支援</span><span class='notice-list__item__date'>2026-07-10</span></a>
360            </li>
361            <li class="notice-list__item">
362              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/n12805ed831a0?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>OSS Gate 10周年カンファレンス支援</span><span class='notice-list__item__date'>2026-06-25</span></a>
363            </li>
364            <li class="notice-list__item">
365              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/n604e1b092f5c?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>コミュニティ支援: OSS Gate</span><span class='notice-list__item__date'>2026-01-23</span></a>
366            </li>
367            <li class="notice-list__item">
368              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/nbbbbc52206ad?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>Railsガイドが Rails 8.1 に対応</span><span class='notice-list__item__date'>2025-10-24</span></a>
369            </li>
370            <li class="notice-list__item">
371              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/n85fc874140d7?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>️ Railsガイド改善に伴うメンテナンスのお知らせ</span><span class='notice-list__item__date'>2025-10-22</span></a>
372            </li>
373            <li class="notice-list__item">
374              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/n01f1e07eb35c?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>ProプランにAIサポートが登場</span><span class='notice-list__item__date'>2025-08-27</span></a>
375            </li>
376            <li class="notice-list__item">
377              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/n6856770ba7ff?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>️ Rails技術者認定試験の参考教材として採用!</span><span class='notice-list__item__date'>2025-06-27</span></a>
378            </li>
379            <li class="notice-list__item">
380              <a target="_blank" rel="noopener" class="notice-list__link" href="https://note.com/yasslab/n/n907fd73757af?magazine_key=ma5df1164c798"><span class='notice-list__item__title'>協賛プランでProプランが利用可能に!</span><span class='notice-list__item__date'>2025-06-06</span></a>
381            </li>
382        </ul>
383      </div>
384    </div>
385  </div>
386  <div class="nav-login-cover">
387      <!-- リソースでログインしてない状態 -->
388      <a class="nav-login-pc" id="guides-Logout" href="/users/login">ログイン</a>
389  </div>
390
391      <div id="mainNav__toggle" class="mainNav__toggle">
392        <a href="#" class="mainNav__toggle__button"><span class="hamburger-button__bars"><img alt="ハンバーガーメニュー" data-src="/assets/menu-891ed698fffbdef7d848319cad3962d008628a04650f5a53969c5db33d8b7659.png" loading="lazy" class="lazyload" src="/spinner.svg" /></span></a>
393      </div>
394    </div>
395    <div id="page_top"><a href="#"></a></div>
396  </div>
397</div>
398
399    <section>
400  <a href="#shortcut_help" class="shortcut_help" style='display: none;'></a>
401  <div id="shortcut_help" style="display:none;">
402    <h2>Keybord Shortcuts</h2>
403    <table class='shortcut-keys'>
404      <tr>
405        <td>
406          <span class='shortcut-key'>/</span>
407          <span class='key-description'>検索を開始</span>
408        </td>
409        <td>
410          <span class='shortcut-key'>ctrl + p </span>
411          <span class='key-description'>前の検索項目</span>
412        </td>
413        <td>
414          <span class='shortcut-key'>↑</span>
415          <span class='key-description'>前の検索項目</span>
416        </td>
417      </tr>
418      <tr>
419        <td>
420          <span class='shortcut-key'>esc</span>
421          <span class='key-description'>フォーカスを解除</span>
422        </td>
423        <td>
424          <span class='shortcut-key'>ctrl + n</span>
425          <span class='key-description'>次の検索項目</span>
426        </td>
427        <td>
428          <span class='shortcut-key'>↓</span>
429          <span class='key-description'>次の検索項目</span>
430        </td>
431      </tr>
432      <tr>
433        <td>
434          <span class='shortcut-key'>cmd + f</span>
435          <span class='key-description'>ページ内検索 (ブラウザ標準)</span>
436        </td>
437        <td>
438          <span class='shortcut-key'>?</span>
439          <span class='key-description'>ヘルプの表示</span>
440        </td>
441        <td>
442        </td>
443      </tr>
444    </table>
445  </div>
446</section>
447
448    <section>
449  <a href="#ai_request" class="modal-ai_request" style='display: none;'></a>
450  <div id="ai_request" style="display:none;">
451    <h2>AI に質問する</h2>
452    <div class='ai-request-form'>
453      <div class="form-center">
454        <form class="ai-request-form" action="/getting_started" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="j-CUgS3ggkmaVPwsBsjIzRBG_rk-tveocESCdiCB1bzqX4l34xbSn4zQabKM2MYIZ_N6w5YEdo3rR62GOuOfeQ" autocomplete="off" />
455          <div class="field">
456            <i class="fa-regular fa-circle-exclamation fa-xs"></i>
457            <span class="ai-request-tooltip tooltip" data-tooltip="AI の回答が必ず正しいわけではありません。あくまでヒントとしてお使いください。"></span>
458            <label style="margin-left: -23px" for="text">質問を送ってみよう</label><span id='text-count'>(0/500文字)</span><br>
459            <textarea placeholder="入力例:
460「A server is already running.」
461「ActiveRecord::PendingMigrationError」
462「Gemfile と Gemfile.lock の違い」
463「マイグレーションって何?」" name="text" id="text">
464</textarea>
465          </div>
466          <div class='ai-request-help'>
467            <a onclick="InputSample();" href="javascript:;"><i class='fad fa-vial-circle-check'></i> サンプル文を入力する</a>
468          </div>
469
470          <div class="actions">
471            <input type="submit" name="commit" value="質問する" class="card-btn btn-red" data-disable-with="質問する" />
472            <button type="button" id='content-clear' class="btn btn-secondary">リセットする</button>
473          </div>
474</form>        <div class='answer'></div>
475        <div class='ai-feedback' style="margin-top: 1rem;">
476          <div id="reaction-buttons" style="display: none;">
477            <span>この回答はお役に立ちましたか?</span>
478            <button class="reaction" id="positiveReaction" onclick="sendReaction('up')">👍</button>
479            <button class="reaction" id="negativeReaction" onclick="sendReaction('down')">👎</button>
480          </div>
481          <span class='thank-message' style='display: none'>フィードバックありがとうございました 🙏</span>
482        </div>
483      </div>
484     </div>
485    </div>
486  </div>
487</section>
488
489<script type='text/javascript'>
490  var currentIndex = 0; 
491  
492  function InputSample() {
493    var sampleArray = ["A server is already running.","ActiveRecord::PendingMigrationError","undefined method `id' for nil:NilClass","ActiveModel::ForbiddenAttributesError","モデル、ビュー、コントローラーの役割を教えて","マイグレーションって何?","Gemfile と Gemfile.lock の違い","HTTP リクエストが分からない"]
494    var currentSample = sampleArray[currentIndex];
495    currentIndex = (currentIndex + 1 < sampleArray.length) ? currentIndex + 1 : 0;
496
497    $('textarea[name="text"]').val(currentSample);
498
499    var count = $('textarea[name="text"]').val().length;
500    var maxinum = 500;
501    $('#text-count').html('(' + count + '/' + maxinum + '文字)');
502    return false;
503  }
504
505  function sendReaction(reaction) {
506    $('#reaction-buttons').hide('fast');
507    $.ajax({
508      url: "/reaction",
509      type: "POST",
510      cache: false,
511      data: { reaction: reaction },
512      headers: {
513        'X-CSRF-Token' : $('meta[name="csrf-token"]').attr('content')
514      },
515      success: function(data) {
516        $('.thank-message').show('slow')
517      }
518    });
519    return false;
520  }
521
522  $(function(){
523    $("#content-clear").click(function() {
524      $('.ai-request-form .answer').hide();
525      $('textarea[name="text"]').val('');
526      $('#text-count').css("color", "gray");
527      $('#text-count').html('(0/500文字)');
528      $('#reaction-buttons').hide();
529      $('.thank-message').hide();
530    });
531
532    $('textarea[name="text"]').on('keyup', function(){
533      var count = $('textarea[name="text"]').val().length;
534      var maxinum = 500;
535      $('#text-count').html('(' + count + '/' + maxinum + '文字)');
536      if (count > maxinum){
537        $('#text-count').css("color", "red");
538        $('.actions > input').prop('disabled', true);
539      }
540      else if(count > 0 && count <= maxinum){
541        $('#text-count').css("color", "grey");
542        $('.actions > input').prop('disabled', false);
543      }
544      else if(count == 0){
545        $('#text-count').css("color","gray");
546      }
547    })
548
549    $('.ai-request-form').submit(function(){
550      $('.actions > input').prop('disabled', true);
551      var content = $('textarea[name="text"]').val();
552      $('.ai-request-form .answer').html("<div class='loader'></div><span style='margin-left: 15px'>解析中…</span>")
553      $('.ai-request-form .answer').show('slow')
554      $('#reaction-buttons').hide();
555      $('.thank-message').hide();
556      $.ajax({
557        type: "POST",
558        url: "/question",
559        cache: false,
560        data: { content: content, path: location.pathname+location.search},
561        headers: {
562          'X-CSRF-Token' : $('meta[name="csrf-token"]').attr('content')
563        },
564        success: function(data) {
565          content = data.answer.replace(/\r?\n/g, '<br>')
566          content = content.replace(/<pre>[\s\S]*?<\/pre>|<br>\s*<br>/g, function(match) {
567                    return match.includes("<pre>") ? match : "<br>";
568          });
569          content = content.replace(/<br><html><body><br>/g, '<html><body>')
570          $('.ai-request-form .answer').html(content)
571          $('.actions > input').prop('disabled', false);
572          $('#reaction-buttons').show();
573        }
574      });
575      return false;
576    });
577  });
578</script>
578
579
580
581    <div style='margin-bottom: 100px;'>
582          
583          <body class="guide">
584    <div id="fb-root"></div>
585    
585<script>(function(d, s, id) {
586       var js, fjs = d.getElementsByTagName(s)[0];
587       if (d.getElementById(id)) return;
588       js = d.createElement(s); js.id = id;
589       js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&appId=614116885378153&version=v2.0";
590       fjs.parentNode.insertBefore(js, fjs);
591     }(document, 'script', 'facebook-jssdk'));
592    </script>
592
593
594    <!--
595    <div id="topNav">
596      <div class="wrapper">
597        <strong class="more-info-label">関連サイト: </strong>
598        <span class="red-button more-info-button">関連URL</span>
599        <ul class="more-info-links s-hidden">
600          <li class="more-info"><a href="http://guides.rubyonrails.org/">原著 (英語)</a></li>
601          <li class="more-info"><a href="https://github.com/yasslab/railsguides.jp">ソースコード (GitHub)</a></li>
602          <li class="more-info"><a href="https://railstutorial.jp/">Railsチュートリアル</a></li>
603        </ul>
604      </div>
605    </div>
606    -->
607
608    
609
610    <hr class="hide">
611
612    <div id="feature">
613
614      <div class="wrapper">
615        <h2>Rails をはじめよう</h2>
616<p>このガイドでは、Ruby on Rails(以下、Rails)を初めて設定して実行するまでを解説します。</p>
617<p>このガイドの内容:</p>
618<ul>
619<li>Railsのインストール方法、新しいRailsアプリケーションの作成方法、アプリケーションからデータベースへの接続方法</li>
620<li>Railsアプリケーションの一般的なレイアウト</li>
621<li>MVC(モデル・ビュー・コントローラ)およびRESTful設計の基礎</li>
622<li>Railsアプリケーションで使うパーツを手軽に生成する方法</li>
623<li>作成したアプリケーションをKamalでデプロイする方法</li>
624</ul>
625
626                <div id="subCol">
627          <h3 class="chapter">目次</h3>
628          <ol class="chapters">
629<li><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></li>
630<li><a href="#rails%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B">Railsとは何か</a></li>
631<li>
632<a href="#rails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E6%96%B0%E8%A6%8F%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">Railsアプリを新規作成する</a>
633
634<ul>
635<li><a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a></li>
636<li><a href="#%E6%9C%80%E5%88%9D%E3%81%AErails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">最初のRailsアプリケーションを作成する</a></li>
637<li><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></li>
638<li><a href="#mvc%E3%81%AE%E5%9F%BA%E7%A4%8E">MVCの基礎</a></li>
639</ul>
640</li>
641<li>
642<a href="#hello-rails-bang">Hello, Rails!</a>
643
644<ul>
645<li><a href="#%E9%96%8B%E7%99%BA%E4%B8%AD%E3%81%AE%E8%87%AA%E5%8B%95%E3%82%B3%E3%83%BC%E3%83%89%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF">開発中の自動コード読み込み</a></li>
646</ul>
647</li>
648<li>
649<a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">データベースモデルを作成する</a>
650
651<ul>
652<li><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">データベースのマイグレーション</a></li>
653<li><a href="#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">マイグレーションを実行する</a></li>
654</ul>
655</li>
656<li><a href="#rails%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB">Railsコンソール</a></li>
657<li>
658<a href="#active-record%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E5%9F%BA%E7%A4%8E">Active Recordモデルの基礎</a>
659
660<ul>
661<li><a href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">レコードを作成する</a></li>
662<li><a href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%A7%E5%8F%96%E3%82%8A%E5%87%BA%E3%81%99">レコードをクエリで取り出す</a></li>
663<li><a href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A8%E4%B8%A6%E3%81%B9%E6%9B%BF%E3%81%88">レコードのフィルタリングと並べ替え</a></li>
664<li><a href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B">レコードを検索する</a></li>
665<li><a href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B4%E6%96%B0%E3%81%99%E3%82%8B">レコードを更新する</a></li>
666<li><a href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B">レコードを削除する</a></li>
667<li><a href="#%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">バリデーション</a></li>
668</ul>
669</li>
670<li><a href="#rails%E3%81%AE%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%AE%E6%B5%81%E3%82%8C">Railsのリクエストの流れ</a></li>
671<li>
672<a href="#%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">ルーティング</a>
673
674<ul>
675<li><a href="#url%E3%81%AE%E6%A7%8B%E6%88%90%E8%A6%81%E7%B4%A0">URLの構成要素</a></li>
676<li><a href="#http%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%A8%E3%81%9D%E3%81%AE%E7%9B%AE%E7%9A%84">HTTPメソッドとその目的</a></li>
677<li><a href="#rails%E3%81%AE%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">Railsのルーティング</a></li>
678<li><a href="#%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89">ルーティングコマンド</a></li>
679</ul>
680</li>
681<li>
682<a href="#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%A8%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3">コントローラとアクション</a>
683
684<ul>
685<li><a href="#%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">リクエストを作成する</a></li>
686<li><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%A4%89%E6%95%B0">インスタンス変数</a></li>
687<li><a href="#crud%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3">CRUDアクション</a></li>
688<li><a href="#%E5%80%8B%E5%88%A5%E3%81%AE%E8%A3%BD%E5%93%81%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B">個別の製品を表示する</a></li>
689<li><a href="#%E8%A3%BD%E5%93%81%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">製品を作成する</a></li>
690<li><a href="#%E8%A3%BD%E5%93%81%E3%82%92%E7%B7%A8%E9%9B%86%E3%81%99%E3%82%8B">製品を編集する</a></li>
691<li><a href="#%E8%A3%BD%E5%93%81%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B">製品を削除する</a></li>
692</ul>
693</li>
694<li>
695<a href="#%E8%AA%8D%E8%A8%BC%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">認証機能を追加する</a>
696
697<ul>
698<li><a href="#%E3%83%AD%E3%82%B0%E3%82%A2%E3%82%A6%E3%83%88%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">ログアウト機能を追加する</a></li>
699<li><a href="#%E8%AA%8D%E8%A8%BC%E3%81%AA%E3%81%97%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%82%E8%A8%B1%E5%8F%AF%E3%81%99%E3%82%8B">認証なしのアクセスも許可する</a></li>
700<li><a href="#%E8%AA%8D%E8%A8%BC%E6%B8%88%E3%81%BF%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AB%E3%81%A0%E3%81%91%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B">認証済みユーザーにだけリンクを表示する</a></li>
701</ul>
702</li>
703<li><a href="#%E8%A3%BD%E5%93%81%E3%82%92%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AB%E4%B9%97%E3%81%9B%E3%82%8B">製品をキャッシュに乗せる</a></li>
704<li><a href="#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%82%92action-text%E3%81%A7%E3%83%AA%E3%83%83%E3%83%81%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AB%E3%81%99%E3%82%8B">フィールドをAction Textでリッチテキストにする</a></li>
705<li><a href="#active-storage%E3%81%A7%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">Active Storageでファイルアップロード機能を追加する</a></li>
706<li><a href="#%E5%9B%BD%E9%9A%9B%E5%8C%96%EF%BC%88i18n%EF%BC%89">国際化(I18n)</a></li>
707<li>
708<a href="#action-mailer%E3%81%A8%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%9A%E7%9F%A5">Action Mailerとメール通知</a>
709
710<ul>
711<li><a href="#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E5%9C%A8%E5%BA%AB%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0%E6%A9%9F%E8%83%BD">基本的な在庫トラッキング機能</a></li>
712<li><a href="#%E9%80%9A%E7%9F%A5%E3%81%AE%E8%B3%BC%E8%AA%AD%E8%80%85%E3%82%92%E8%A3%BD%E5%93%81%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">通知の購読者を製品に追加する</a></li>
713<li><a href="#%E3%80%8C%E5%9C%A8%E5%BA%AB%E3%81%82%E3%82%8A%E3%80%8D%E3%83%A1%E3%83%BC%E3%83%AB%E3%81%AB%E3%82%88%E3%82%8B%E9%80%9A%E7%9F%A5%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">「在庫あり」メールによる通知機能を追加する</a></li>
714<li><a href="#%E5%85%B1%E9%80%9A%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92concern%E3%81%AB%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B">共通コードをconcernに抽出する</a></li>
715<li><a href="#%E9%80%9A%E7%9F%A5%E8%B3%BC%E8%AA%AD%E3%81%AE%E8%A7%A3%E9%99%A4%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E3%83%A1%E3%83%BC%E3%83%AB%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">通知購読の解除リンクをメールに追加する</a></li>
716</ul>
717</li>
718<li>
719<a href="#css%E3%81%A8javascript%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">CSSとJavaScriptを追加する</a>
720
721<ul>
722<li><a href="#propshaft">Propshaft</a></li>
723<li><a href="#importmap">importmap</a></li>
724<li><a href="#hotwire">Hotwire</a></li>
725</ul>
726</li>
727<li>
728<a href="#rails%E3%81%A7%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E6%9B%B8%E3%81%8F">Railsでテストを書く</a>
729
730<ul>
731<li><a href="#%E3%83%95%E3%82%A3%E3%82%AF%E3%82%B9%E3%83%81%E3%83%A3">フィクスチャ</a></li>
732<li><a href="#%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%81%E4%BF%A1%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B">メール送信をテストする</a></li>
733</ul>
734</li>
735<li><a href="#rubocop%E3%81%A7%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E5%BD%A2%E5%BC%8F%E3%82%92%E7%B5%B1%E4%B8%80%E3%81%99%E3%82%8B">RuboCopでコードの形式を統一する</a></li>
736<li><a href="#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">セキュリティチェック</a></li>
737<li><a href="#github-actions%E3%81%A7ci%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">GitHub ActionsでCIを実行する</a></li>
738<li>
739<a href="#kamal%E3%81%A7production%E7%92%B0%E5%A2%83%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B">Kamalでproduction環境にデプロイする</a>
740
741<ul>
742<li><a href="#production%E7%92%B0%E5%A2%83%E3%81%A7%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">production環境でユーザーを作成する</a></li>
743<li><a href="#solid-queue%E3%81%A7%E3%83%90%E3%83%83%E3%82%AF%E3%82%B0%E3%83%A9%E3%82%A6%E3%83%B3%E3%83%89%E3%82%B8%E3%83%A7%E3%83%96%E3%82%92%E5%87%A6%E7%90%86%E3%81%99%E3%82%8B">Solid Queueでバックグラウンドジョブを処理する</a></li>
744</ul>
745</li>
746<li><a href="#%E4%BB%8A%E5%BE%8C%E3%81%AE%E3%82%B9%E3%83%86%E3%83%83%E3%83%97">今後のステップ</a></li>
747<li><a href="#%E9%96%A2%E9%80%A3%E3%83%AA%E3%83%B3%E3%82%AF">関連リンク</a></li>
748</ol>
749
750          <!--
751          <div id="sideDarkmodeSwitch">
752            <span><i class="far fa-moon"></i> ダークモード
753            <span class="label-beta"></span></span>
754            <input type="checkbox" id="darkmode_toggle_check" value="darkmode"/>
755            <label class="darkmode-toggle" for="darkmode_toggle_check" id="darkmode_status"></label>
756          </div>
757          -->
758          <div id="sponsor-pr" style="margin: 10px 0;text-align: center;">
759            \\ <a href="#supporters">協賛企業</a>からのお知らせ //
760            <!--協賛に興味あれば<a href="/sponsors">コチラ</a>へ-->
761          </div>
762          <aside id="toppage">
763            <div id="sponsor">
764              <!-- Carbon Ads や協賛企業の画像が表示される箇所  -->
765              
765<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7ITK7L&amp;placement=railsguidesjp" id="_carbonads_js"></script>
765
766            </div>
767            <ol class="snsb" style="margin-left: 0; margin-right: 0;">
768              <li><a class="twitter-share-button" href="https://twitter.com/intent/tweet" data-hashtags="Railsガイド" data-via="RailsGuidesJP" data-related="RailsGuidesJP,YassLab" data-size="large" lang="ja"></a></li>
769              <li style="margin-left: 6px; margin-right: 0;"><a class="github-button" href="https://github.com/yasslab/railsguides.jp" data-size="large" data-show-count="true" aria-label="Star yasslab/railsguides.jp on GitHub">Star</a></li>
770            </ol>
771          </aside>
772        </div>
773
774      </div>
775    </div>
776
777    <div id="container">
778      <div class="wrapper">
779        <div id="mainCol">
780          <h3 id="はじめに"><a class="anchorlink" href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">1 はじめに</a></h3>
781<p>Railsの世界へようこそ! 本ガイド「Railsをはじめよう」では、Railsを活用してWebアプリケーションを構築するときの中核となる概念について解説します。本ガイドを理解するために、Railsの経験は必要ありません。</p>
782<p>Railsは、Rubyプログラミング言語用に構築されたWebフレームワークです。RailsはRuby独自のさまざまな機能を活用しているため、このガイドで紹介する基本的な用語や語彙を理解できるように、事前にRubyの基礎を学習しておくことを<strong>強く</strong>オススメします。</p>
783<ul>
784<li><a href="https://www.ruby-lang.org/ja/documentation/">プログラミング言語Ruby公式Webサイト</a></li>
785<li><a href="https://github.com/EbookFoundation/free-programming-books/blob/main/books/free-programming-books-ja.md#ruby">プログラミング学習コンテンツまとめ</a></li>
786</ul>
787<div class="info"><p>訳注:Railsガイドでは開発経験者が早くキャッチアップできるよう、多くの用語説明を省略しています。読んでいて「難しい」と感じた場合は<a href="https://railstutorial.jp/">Railsチュートリアル</a>からお読みください。</p></div>
788<h3 id="railsとは何か"><a class="anchorlink" href="#rails%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B">2 Railsとは何か</a></h3>
789<p>Railsとは、プログラミング言語「Ruby」で書かれたWebアプリケーションフレームワークです。Railsは、あらゆる開発者がWebアプリケーション開発で必要となる作業やリソースを事前に想定することで、Webアプリケーションをより手軽に開発できるように設計されています。</p>
790<p>Railsは、他の多くのWebアプリケーションフレームワークと比較して、アプリケーションを開発する際のコード量がより少なくて済むにもかかわらず、より多くの機能を実現できます。ベテラン開発者の多くが「RailsのおかげでWebアプリケーション開発がとても楽しくなった」と述べています。</p>
791<p>Railsは「最善の開発方法は1つである」という、ある意味大胆な判断に基いて設計されています。何かを行うための最善の方法を1つ仮定して、それに沿った開発を全面的に支援します。言い換えれば、Railsで仮定されていない別の開発手法は行いにくくなります。</p>
792<p>この「Rails Way」、すなわち「Railsというレールに乗って開発する」手法を学んだ人は、開発の生産性が驚くほど向上することに気付くでしょう。逆に、レールに乗らずに従来の開発手法にこだわると、開発の楽しさが減ってしまうかもしれません。</p>
793<p>Railsの哲学には、以下の2つの主要な基本理念があります。</p>
794<ul>
795<li>
796<strong>繰り返しを避けよ(Don't Repeat Yourself: DRY):</strong>
797DRYはソフトウェア開発上の原則であり、「システムを構成する知識のあらゆる部品は、常に単一であり、明確であり、信頼できる形で表現されていなければならない」というものです。同じコードを繰り返し書くことを徹底的に避けることで、コードが保守しやすくなり、容易に拡張できるようになり、バグも減らせます。</li>
798<li>
799<strong>設定より規約が優先(Convention Over Configuration):</strong>
800Railsでは、Webアプリケーションの機能を実現する最善の方法が明確に示されており、Webアプリケーションの各種設定についても従来の経験や慣習を元に、それらのデフォルト値を定めています。デフォルト値が決まっているおかげで、開発者の意見をすべて取り入れようとした自由過ぎるWebã
800‚¢ãƒ—リケーションのように、開発者が大量の設定ファイルを設定せずに済みます。</li>
801</ul>
802<h3 id="railsアプリを新規作成する"><a class="anchorlink" href="#rails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E6%96%B0%E8%A6%8F%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">3 Railsアプリを新規作成する</a></h3>
803<p>ここでは、Railsの組み込み機能のいくつかをデモンストレーションするシンプルなeコマースアプリを<code>store</code>というプロジェクト名で構築します。</p>
804<div class="info"><p>ドル記号<code>$</code>で始まるコマンドは、ターミナルで実行する必要があります。</p></div>
805<h4 id="前提条件"><a class="anchorlink" href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">3.1 前提条件</a></h4>
806<p>このプロジェクトでは以下のものが必要です。</p>
807<ul>
808<li>Ruby 3.2以降</li>
809<li>Rails 8.1.0以降</li>
810<li>コードエディタ</li>
811</ul>
812<p>RubyやRailsをインストールする必要がある場合は、<a href="install_ruby_on_rails.html">Ruby on Rails インストールガイド</a>に記載されている手順に従ってください。</p>
813<div class="info"><p>訳注:GitHubが提供するクラウド開発環境『<a href="https://github.co.jp/features/codespaces">Codespaces</a>』の<a href="https://github.com/codespaces/templates">公式Ruby on Railsテンプレート</a>を使うと、数クリックでRailsを動かせるクラウド開発環境が手に入ります。(参考: <a href="https://railstutorial.jp/docs/codespaces">GitHub Codespacesを利用する - Railsチュートリアル</a>)</p></div>
814<p>正しいバージョンのRailsがインストールされていることを確認しておきましょう。現在のバージョンを表示するには、ターミナルを開いて以下のコマンドを実行すると、バージョン番号が出力されます。</p>
815<div class="code_container">
816<pre class="highlight">
817<span class="gp">$</span><span class="w"> </span>rails <span class="nt">--version</span>
818<span class="go">Rails 8.1.0
819</span>
820</pre>
821</div>
822<p>バージョン番号はRails 8.1.0以降になるはずです。</p>
823<h4 id="最初のrailsアプリケーションを作成する"><a class="anchorlink" href="#%E6%9C%80%E5%88%9D%E3%81%AErails%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">3.2 最初のRailsアプリケーションを作成する</a></h4>
824<p>Railsには、作業を楽にするためのさまざまなコマンドが付属しています。
825利用可能なコマンドをすべて表示するには、<code>rails --help</code>を実行します。</p>
826<p><code>rails new</code>コマンドは、新しいRailsアプリケーションの基盤を生成するので、まずこのコマンドを実行することから始めましょう。</p>
827<p><code>store</code>アプリケーションを作成するには、ターミナルで以下のコマンドを実行します。</p>
828<div class="code_container">
829<pre class="highlight">
830<span class="gp">$</span><span class="w"> </span>rails new store
831</pre>
832</div>
833<div class="note"><p><code>rails new</code>コマンドにフラグを追加すると、Railsが生成するアプリケーションをカスタマイズできます。利用可能なオプションをすべて表示するには、<code>rails new --help</code>を実行します。</p></div>
834<p>新しいアプリケーションを作成したら、そのディレクトリに移動します。</p>
835<div class="code_container">
836<pre class="highlight">
837<span class="gp">$</span><span class="w"> </span><span class="nb">cd </span>store
838</pre>
839</div>
840<h4 id="ディレクトリ構造"><a class="anchorlink" href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">3.3 ディレクトリ構造</a></h4>
841<p>新しいRailsアプリケーションに含まれるファイルとディレクトリを少し見てみましょう。このフォルダをコードエディタで開くか、ターミナルで<code>ls -la</code>を実行してファイルとディレクトリを確認できます。</p>
842<table>
843<thead>
844<tr>
845<th>ファイル/フォルダ</th>
846<th>目的</th>
847</tr>
848</thead>
849<tbody>
850<tr>
851<td>app/</td>
852<td>このディレクトリには、アプリケーションのコントローラ、モデル、ビュー、ヘルパー、メーラー、ジョブ、そしてアセットが置かれます。以後、本ガイドでは基本的にこのディレクトリを中心に説明を行います。</td>
853</tr>
854<tr>
855<td>bin/</td>
856<td>このディレクトリには、アプリケーションを起動する<code>rails</code>スクリプトが置かれます。セットアップ・アップデート・デプロイに使うスクリプトファイルもここに置けます。</td>
857</tr>
858<tr>
859<td>config/</td>
860<td>このディレクトリには、アプリケーションの各種設定ファイルï¼
860ˆãƒ«ãƒ¼ãƒ†ã‚£ãƒ³ã‚°ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãªã©ï¼‰ãŒç½®ã‹ã‚Œã¾ã™ã€‚詳しくは<a href="configuring.html">Rails アプリケーションの設定項目</a>を参照してください。</td>
861</tr>
862<tr>
863<td>config.ru</td>
864<td>Rackベースのサーバーでアプリケーションの起動に使われる<a href="https://rack.github.io">Rack</a>設定ファイルです。</td>
865</tr>
866<tr>
867<td>db/</td>
868<td>このディレクトリには、現在のデータベーススキーマと、データベースマイグレーションファイルが置かれます。</td>
869</tr>
870<tr>
871<td>Dockerfile</td>
872<td>Dockerの設定ファイルです。</td>
873</tr>
874<tr>
875<td>Gemfile<br>Gemfile.lock</td>
876<td>これらのファイルは、Railsアプリケーションで必要となるgemの依存関係を記述します。この2つのファイルは<a href="https://bundler.io">Bundler</a> gemで使われます。</td>
877</tr>
878<tr>
879<td>lib/</td>
880<td>このディレクトリには、アプリケーションで使う拡張モジュールが置かれます。</td>
881</tr>
882<tr>
883<td>log/</td>
884<td>このディレクトリには、アプリケーションのログファイルが置かれます。</td>
885</tr>
886<tr>
887<td>public/</td>
888<td>静的なファイルやコンパイル済みアセットはここに置きます。このディレクトリにあるファイルは、外部(インターネット)にそのまま公開されます。</td>
889</tr>
890<tr>
891<td>Rakefile</td>
892<td>このファイルは、コマンドラインから実行できるタスクを探索して読み込みます。このタスク定義は、Rails全体のコンポーネントに対して定義されます。独自のRakeタスクを定義したい場合は、<code>Rakefile</code>に直接書くと権限が強すぎるので、なるべく<code>lib/tasks</code>フォルダの下にRake用のファイルを追加してください。</td>
893</tr>
894<tr>
895<td>README.md</td>
896<td>アプリケーションの概要を簡潔に説明するマニュアルをここに記入します。このファイルにはアプリケーションの設定方法などを記入し、これさえ読めば誰でもアプリケーションを構築できるようにしておきましょう。</td>
897</tr>
898<tr>
899<td>script/</td>
900<td>使い捨ての、または汎用の<a href="https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/script/USAGE">スクリプト</a>や<a href="https://github.com/rails/rails/blob/main/railties/lib/rails/generators/rails/benchmark/USAGE">ベンチマーク</a>をここに置きます。</td>
901</tr>
902<tr>
903<td>storage/</td>
904<td>このディレクトリには、ディスクサービス用のSQLiteデータベースファイルやActive Storageファイルが置かれます。詳しくは<a href="active_storage_overview.html">Active Storageの概要</a>を参照してください。</td>
905</tr>
906<tr>
907<td>test/</td>
908<td>このディレクトリには、単体テストやフィクスチャなどのテスト関連ファイルを置きます。テストについて詳しくは<a href="testing.html">Railsアプリケーションをテストする</a>を参照してください。</td>
909</tr>
910<tr>
911<td>tmp/</td>
912<td>このディレクトリには、キャッシュやpidなどの一時ファイルが置かれます。</td>
913</tr>
914<tr>
915<td>vendor/</td>
916<td>サードパーティ製コードはすべてこのディレクトリに置きます。通常のRailsアプリケーションの場合、外部のgemファイルがここに置かれます。</td>
917</tr>
918<tr>
919<td>.dockerignore</td>
920<td>コンテナにコピーすべきでないファイルをDockerに指示するのに使うファイルです。</td>
921</tr>
922<tr>
923<td>.gitattributes</td>
924<td>このファイルは、gitリポジトリ内の特定のパスについてメタデータを定義します。このメタデータは、gitや他のツールで振る舞いを拡張できます。詳しくは<a href="https://git-scm.com/docs/gitattributes">gitattributesドキュメント</a>を参照してください。</td>
925</tr>
926<tr>
927<td>.github/</td>
928<td>GitHub固有のファイルが置かれます。</td>
929</tr>
930<tr>
931<td>.gitignore</td>
932<td>
932Gitに登録しないファイル(またはパターン)をこのファイルで指定します。Gitにファイルを登録しない方法について詳しくは<a href="https://help.github.com/articles/ignoring-files">GitHub - Ignoring files</a>を参照してください。</td>
933</tr>
934<tr>
935<td>.kamal/</td>
936<td>Kamalの秘密鍵とデプロイ用フックが含まれます。</td>
937</tr>
938<tr>
939<td>.rubocop.yml</td>
940<td>このファイルにはRuboCop用の設定が含まれます。</td>
941</tr>
942<tr>
943<td>.ruby-version</td>
944<td>このファイルには、デフォルトのRubyバージョンが記述されています。</td>
945</tr>
946</tbody>
947</table>
948<h4 id="mvcの基礎"><a class="anchorlink" href="#mvc%E3%81%AE%E5%9F%BA%E7%A4%8E">3.4 MVCの基礎</a></h4>
949<p>Railsのコードは、<a href="https://ja.wikipedia.org/wiki/Model_View_Controller">MVC(Model-View-Controller)</a>アーキテクチャに基づいて編成されています。MVCでは、コードの大部分が以下の3つの主要な概念に基づいて配置されます。</p>
950<ul>
951<li>
952<strong>モデル</strong>: アプリケーション内のデータ(通常はデータベースのテーブル)を管理します。</li>
953<li>
954<strong>ビュー</strong>: レスポンスをHTML、JSON、XMLなどのさまざまな形式でレンダリングします。</li>
955<li>
956<strong>コントローラ</strong>: ユーザー操作や各リクエストのロジックを処理します。</li>
957</ul>
958<p><img src="/railsguides/images/getting_started/mvc_architecture_light.jpg" alt="MVCアーキテクチャの図" data-src="/railsguides/images/getting_started/mvc_architecture_light.jpg" class="lazyload" loading="lazy"></p>
959<p>MVCの基本部分を理解したので、MVCがどのようにRailsで使われるかを見てみましょう。</p>
960<h3 id="hello-rails-bang"><a class="anchorlink" href="#hello-rails-bang">4 Hello, Rails!</a></h3>
961<p>それでは、Railsのデータベースを作成して、Railsサーバーを初めて起動してみましょう。</p>
962<p>ターミナルで<code>store</code>ディレクトリに移動し、以下のコマンドを実行します。</p>
963<div class="code_container">
964<pre class="highlight">
965<span class="gp">$</span><span class="w"> </span>bin/rails db:create
966</pre>
967</div>
968<p>これで、アプリケーションの初期データベースが作成されました。
969続いて、Railsサーバーを起動します。</p>
970<div class="code_container">
971<pre class="highlight">
972<span class="gp">$</span><span class="w"> </span>bin/rails server
973</pre>
974</div>
975<div class="note"><p>Railsコマンドをアプリケーションディレクトリ内で実行するときは、単独の<code>rails</code>よりも<code>bin/rails</code>を使うようにしましょう。これにより、そのアプリケーションで使われているRailsのバージョンが確実に使われます。</p></div>
976<p>すると、PumaというWebサーバーが起動します。Pumaサーバーは、静的ファイルやRailsアプリケーションの配信を担当します。</p>
977<div class="code_container">
978<pre class="highlight">
979<span class="gp">=&gt;</span><span class="w"> </span>Booting Puma
980<span class="gp">=&gt;</span><span class="w"> </span>Rails 8.1.0 application starting <span class="k">in </span>development
981<span class="gp">=&gt;</span><span class="w"> </span>Run <span class="sb">`</span>bin/rails server <span class="nt">--help</span><span class="sb">`</span> <span class="k">for </span>more startup options
982<span class="go">Puma starting in single mode...
983* Puma version: 6.4.3 (ruby 3.3.5-p100) ("The Eagle of Durango")
984*  Min threads: 3
985*  Max threads: 3
986*  Environment: development
987*          PID: 12345
988* Listening on http://127.0.0.1:3000
989* Listening on http://[::1]:3000
990Use Ctrl-C to stop
991</span>
992</pre>
993</div>
994<p>Railsアプリケーションを表示してみましょう。
995ブラウザで<code>http://localhost:3000</code>を開くと、デフォルトのRailsウェルカムページが表示されます。</p>
996<p><img src="/railsguides/images/getting_started/rails_welcome.png" alt="Rails起動ページのスクリーンショット" data-src="/railsguides/images/getting_started/rails_welcome.png" class="lazyload" loading="lazy"></p>
997<p>動きました!</p>
998<p>Railsの起動ページは、新しいRailsアプリケーションの「スモークテスト」として使えます。このページが表示されれば、サーバーが正常に動作していることが確認できます。</p>
999<p>実行されているターミナルのウィンドウでCtrl + Cキーを押せば、いつでもWebã
999‚µãƒ¼ãƒãƒ¼ã‚’停止できます。</p>
1000<h4 id="開発中の自動コード読み込み"><a class="anchorlink" href="#%E9%96%8B%E7%99%BA%E4%B8%AD%E3%81%AE%E8%87%AA%E5%8B%95%E3%82%B3%E3%83%BC%E3%83%89%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF">4.1 開発中の自動コード読み込み</a></h4>
1001<p>開発者の幸福はRailsの基本的な哲学であり、開発中にコードを自動で再読み込みする機能は、これを実現する方法の1つです。</p>
1002<p>Railsサーバーを起動すると、新しいファイルや既存のファイルへの変更が検出され、実行中も必要に応じてコードの読み込みや再読み込みが自動的に行われます。これにより、コード変更のたびにRailsサーバーを再起動しなくても済むので、アプリの構築に集中できます。</p>
1003<p>また、Railsアプリケーションでは、他のプログラミング言語で見られるような<code>require</code>ステートメントがほとんど使われていないことにも気付くでしょう。Railsでは命名規則に基づいてファイルを自動的に<code>require</code>するので、アプリケーションコードの記述に集中できます。</p>
1004<p>詳しくは別ガイド『<a href="autoloading_and_reloading_constants.html">Railsの自動読み込みと再読み込み</a>』を参照してください。</p>
1005<h3 id="データベースモデルを作成する"><a class="anchorlink" href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">5 データベースモデルを作成する</a></h3>
1006<p>Railsの<a href="active_record_basics.html">Active Record</a>は、リレーショナルデータベースをRubyコードにマッピングする機能であり、テーブルやレコードの作成、更新、削除など、データベースを操作するための構造化クエリ言語(SQL)を生成するのに役立ちます。</p>
1007<p>このstoreアプリケーションでは、RailsのデフォルトであるSQLiteをリレーショナルデータベースとして使っています。</p>
1008<p>それでは、このRailsアプリケーションにデータベーステーブルを追加して、シンプルな eコマースストアに製品を追加できるようにしてみましょう。</p>
1009<div class="code_container">
1010<pre class="highlight">
1011<span class="gp">$</span><span class="w"> </span>bin/rails generate model Product name:string
1012</pre>
1013</div>
1014<p>このコマンドは、データベースに<code>string</code>型の<code>name</code>カラムを持つ<code>Product</code>という名前のモデルを生成するようにRailsに指示します。他のカラム型を追加する方法についてはこの後で学習します。</p>
1015<p>コマンドを実行すると、ターミナルに次の内容が表示されます。</p>
1016<div class="code_container">
1017<pre class="highlight">
1018<span class="go">      invoke  active_record
1019      create    db/migrate/20240426151900_create_products.rb
1020      create    app/models/product.rb
1021      invoke    test_unit
1022      create      test/models/product_test.rb
1023      create      test/fixtures/products.yml
1024</span>
1025</pre>
1026</div>
1027<p>このコマンドは以下を行います。</p>
1028<ol>
1029<li>
1030<code>db/migrate</code>フォルダの下にマイグレーションファイルを作成。</li>
1031<li>
1032<code>app/models/product.rb</code>というActive Recordモデルを作成。</li>
1033<li>このモデルで使うテストファイルとフィクスチャ(fixture)ファイルを作成。</li>
1034</ol>
1035<div class="note"><p>Railsのモデル名には英語の<strong>単数形</strong>を使います。これは、インスタンス化されたモデルはデータベース内の1件のレコードを表す(データベースに1個の製品(a product)を追加する)という考えに基づいています。</p></div>
1036<h4 id="データベースのマイグレーション"><a class="anchorlink" href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">5.1 データベースのマイグレーション</a></h4>
1037<p><strong>マイグレーション</strong>(migration)とは、データベースに対して行う一連の変更のことでã
1037™ã€‚</p>
1038<p>マイグレーションを定義することで、データベースのテーブルやカラム、およびその他の属性を追加・変更・削除するためにデータベースを変更する方法を統一された形でRailsに指示します。
1039これにより、自分のコンピュータ上での開発中に行ったデータベース変更をトラッキングして、production環境に安全にデプロイできるようにします。</p>
1040<p>Railsが作成したマイグレーションをコードエディタで開いて、マイグレーションで何が行われるかを確認してみましょう。</p>
1041<div class="note"><p>以後のファイルの冒頭には、読みやすさのためファイルパスをコメントで追加してあります。</p></div>
1042<div class="code_container">
1043<pre class="highlight">
1044<span class="c1"># db/migrate/&lt;タイムスタンプ&gt;_create_products.rb</span>
1045<span class="k">class</span> <span class="nc">CreateProducts</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">8.1</span><span class="p">]</span>
1046  <span class="k">def</span> <span class="nf">change</span>
1047    <span class="n">create_table</span> <span class="ss">:products</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
1048      <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:name</span>
1049
1050      <span class="n">t</span><span class="p">.</span><span class="nf">timestamps</span>
1051    <span class="k">end</span>
1052  <span class="k">end</span>
1053<span class="k">end</span>
1054</pre>
1055</div>
1056<p>このマイグレーションは、Railsに<code>products</code>という新しいデータベーステーブルを作成するよう指示しています。</p>
1057<div class="note"><p>モデル名は<code>Product</code>のように<strong>単数形</strong>を使いますが、データベーステーブル名は<code>products</code>のように<strong>複数形</strong>を使っている点にご注目ください。これは、データベースは各モデルの「すべての」インスタンスを保持する(つまり、製品の集まり(products)のデータベースを作成している)という考え方に基づいています。</p></div>
1058<p>次の<code>create_table</code>ブロックは、このデータベーステーブルで定義するカラムと型を定義します。</p>
1059<ul>
1060<li><p><code>t.string :name</code>: <code>products</code>テーブルに<code>name</code>というカラムを作成し、型を<code>string</code>に設定するようRailsに指示します。</p></li>
1061<li><p><code>t.timestamps</code>: モデルに<code>created_at:datetime</code>と<code>updated_at:datetime</code>の2つのカラムを一度に定義するショートカットです。
1062これらのカラムは、RailsのほとんどのActive Recordモデルで表示され、レコードの作成時や更新時にActive Recordによって自動的に値が設定されます。</p></li>
1063</ul>
1064<h4 id="マイグレーションを実行する"><a class="anchorlink" href="#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">5.2 マイグレーションを実行する</a></h4>
1065<p>データベースに対して行う変更を定義したら、以下のコマンドを使ってマイグレーションを実行します。</p>
1066<div class="code_container">
1067<pre class="highlight">
1068<span class="gp">$</span><span class="w"> </span>bin/rails db:migrate
1069</pre>
1070</div>
1071<p>この<code>bin/rails db:migrate</code>コマンドは、新しいマイグレーションをチェックしてデータベースに適用します。</p>
1072<p>実行結果は以下のような感じになります。</p>
1073<div class="code_container">
1074<pre class="highlight">
1075<span class="go">== 20240426151900 CreateProducts: migrating ===================================
1076-- create_table(:products)
1077</span><span class="gp">   -&gt;</span><span class="w"> </span>0.0030s
1078<span class="go">== 20240426151900 CreateProducts: migrated (0.0031s) ==========================
1079</span>
1080</pre>
1081</div>
1082<div class="info"><p>実行したマイグレーションに誤りがあった場合は、<code>bin/rails db:rollback</code>を実行することで直前のマイグレーションに戻せます。</p></div>
1083<h3 id="railsコンソール"><a class="anchorlink" href="#rails%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB">6 Railsコンソール</a></h3>
1084<p><code>products</code>テーブルが作成されたので、Railsで操作できるようになりました。
1085さっそく試してみましょう。</p>
1086<p>ここでは、<strong>Railsコンソール</strong>と呼ばれる機能を使います。Railsコンソールは、Railsアプリケーションでコードを試すときに便利な対話型ツールです。</p>
1087<div class="code_container">
1088<pre class="highlight">
1089<span class="gp">$</span><span class="w"> </span>bin/rails console
1090</pre>
1091</div>
1092<p>上のRailsコンソールコマンドを実行すると、以下のようなプロンプトが表示されます。</p>
1093<div class="code_container">
1094<pre class="highlight">
1095Loading development environment (Rails 8.1.0)
1096store(dev)&gt;
1097</pre>
1098</div>
1099<p>ここで入力した内容は、<code>Enter</code>を押すと実行されます。
1100それではRailsバージョンを出力してみましょう。</p>
1101<div class="code_container">
1102<pre class="highlight">
1103store(dev)&gt; Rails.version
1104=&gt; "8.1.0"
1105</pre>
1106</div>
1107<p>たしかに動きました!</p>
1108<h3 id="active-recordモデルの基礎"><a class="anchorlink" href="#active-record%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E5%9F%BA%E7%A4%8E">7 Active Recordモデルの基礎</a></h3>
1109<p>Railsのモデルジェネレーターを実行して<code>Product</code>モデルを作成すると、<code>app/models/product.rb</code>にファイルが作成されました。作成したファイルにある以下のクラスは、Active Recordを使ってデータベースの<code>products</code>テーブルとやり取りします。</p>
1110<div class="code_container">
1111<pre class="highlight">
1112<span class="c1"># app/models/product.rb</span>
1113<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
1114<span class="k">end</span>
1115</pre>
1116</div>
1117<p>この<code>Product</code>クラスにコードがないことに驚くかもしれません。Railsはこのモデルの定義をどうやって知るのでしょうか?</p>
1118<p>この<code>Product</code>モデルが使われると、Railsはデータベーステーブルでカラム名と型を照会し、これらの属性のコードを自動的に生成します。Railsは、定型コードを記述する手間を省いて、代わりにバックグラウンドで処理してくれるので、開発者はアプリケーションロジックに集中できます。</p>
1119<p>この<code>Product</code>モデルでどんなカラムを検出されるかを、Railsコンソールで確認しましょう。</p>
1120<p>Railsコンソールで以下のコマンドを実行します。</p>
1121<div class="code_container">
1122<pre class="highlight">
1123store(dev)&gt; Product.column_names
1124</pre>
1125</div>
1126<p>すると、以下のように表示されるはずです。</p>
1127<div class="code_container">
1128<pre class="highlight">
1129=&gt; ["id", "name", "created_at", "updated_at"]
1130</pre>
1131</div>
1132<p>Railsは上記のカラム情報をデータベースに要求し、その情報を用いて<code>Product</code>クラスの属性を動的に定義するので、開発者が個別の属性を手動で定義する必要はありません。これは、Railsを使うことで開発がいかに簡単になるかを示す一例です。</p>
1133<h4 id="レコードを作成する"><a class="anchorlink" href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">7.1 レコードを作成する</a></h4>
1134<p>Railsコンソールで以下のコードを実行すると、<code>Product</code>モデルの新しいレコードを作成できます。</p>
1135<div class="code_container">
1136<pre class="highlight">
1137store(dev)&gt; product = Product.new(name: "T-Shirt")
1138=&gt; #&lt;Product:0x000000012e616c30 id: nil, name: "T-Shirt", created_at: nil, updated_at: nil&gt;
1139</pre>
1140</div>
1141<p>この<code>product</code>変数は、<code>Product</code>モデルのインスタンスです。この時点ではまだデータベースに保存さã
1141‚Œã¦ã„ないため、<code>id</code>、<code>created_at</code>と<code>updated_at</code>のタイムスタンプはありません。</p>
1142<p>レコードをデータベースに保存するには、<code>save</code>を呼び出します。</p>
1143<div class="code_container">
1144<pre class="highlight">
1145store(dev)&gt; product.save
1146  TRANSACTION (0.1ms)  BEGIN immediate TRANSACTION /*application='Store'*/
1147  Product Create (0.9ms)  INSERT INTO "products" ("name", "created_at", "updated_at") VALUES ('T-Shirt', '2024-11-09 16:35:01.117836', '2024-11-09 16:35:01.117836') RETURNING "id" /*application='Store'*/
1148  TRANSACTION (0.9ms)  COMMIT TRANSACTION /*application='Store'*/
1149=&gt; true
1150</pre>
1151</div>
1152<p><code>save</code>が呼び出されると、Railsはメモリ上にある属性を取得し、SQLの<code>INSERT</code>クエリを生成してこのレコードをデータベースに挿入します。</p>
1153<p>このとき、データベースレコードの<code>id</code>と、<code>created_at</code>タイムスタンプおよび<code>updated_at</code>タイムスタンプを用いて、メモリ上のオブジェクトも更新します。<code>product</code>変数の内容を表示してみれば、このことが確認できます。</p>
1154<div class="code_container">
1155<pre class="highlight">
1156store(dev)&gt; product
1157=&gt; #&lt;Product:0x00000001221f6260 id: 1, name: "T-Shirt", created_at: "2024-11-09 16:35:01.117836000 +0000", updated_at: "2024-11-09 16:35:01.117836000 +0000"&gt;
1158</pre>
1159</div>
1160<p><code>create</code>を使えば、1回の呼び出しでActive Recordオブジェクトのインスタンス化と保存を同時に実行できます。</p>
1161<div class="code_container">
1162<pre class="highlight">
1163store(dev)&gt; Product.create(name: "Pants")
1164  TRANSACTION (0.1ms)  BEGIN immediate TRANSACTION /*application='Store'*/
1165  Product Create (0.4ms)  INSERT INTO "products" ("name", "created_at", "updated_at") VALUES ('Pants', '2024-11-09 16:36:01.856751', '2024-11-09 16:36:01.856751') RETURNING "id" /*application='Store'*/
1166  TRANSACTION (0.1ms)  COMMIT TRANSACTION /*application='Store'*/
1167=&gt; #&lt;Product:0x0000000120485c80 id: 2, name: "Pants", created_at: "2024-11-09 16:36:01.856751000 +0000", updated_at: "2024-11-09 16:36:01.856751000 +0000"&gt;
1168</pre>
1169</div>
1170<h4 id="レコードをクエリで取り出す"><a class="anchorlink" href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%A7%E5%8F%96%E3%82%8A%E5%87%BA%E3%81%99">7.2 レコードをクエリで取り出す</a></h4>
1171<p>Active Recordモデルを使って、データベース内のレコードを検索することも可能です。</p>
1172<p>データベース内にある<code>Product</code>の全レコードを検索するには、<code>all</code>メソッドを使います。
1173これは<strong>クラスメソッド</strong>なので、以下のように<code>Product</code>クラスで直接呼び出せます(上記の<code>save</code>など、<code>Product</code>のインスタンスで呼び出すインスタンスメソッドとは異なります)。</p>
1174<div class="code_container">
1175<pre class="highlight">
1176store(dev)&gt; Product.all
1177  Product Load (0.1ms)  SELECT "products".* FROM "products" /* loading for pp */ LIMIT 11 /*application='Store'*/
1178=&gt; [#&lt;Product:0x0000000121845158 id: 1, name: "T-Shirt", created_at: "2024-11-09 16:35:01.117836000 +0000", updated_at: "2024-11-09 16:35:01.117836000 +0000"&gt;,
1179 #&lt;Product:0x0000000121845018 id: 2, name: "Pants", created_at: "2024-11-09 16:36:01.856751000 +0000", updated_at: "2024-11-09 16:36:01.856751000 +0000"&gt;]
1180</pre>
1181</div>
1182<p>これにより<code>SELECT</code> SQLクエリが生成され、<code>products</code>テーブルからすべてのレコードが読み込まれます。各レコードは自動的にActive Recordの<code>Product</code>モデルのインスタンスに変換されるため、Rubyから手軽に操作できます。</p>
1183<div class="info"><p><code>all</code>メソッドが返す<code>ActiveRecord::Relation</code>オブジェクトは、配列に似たデータベースレコードのコレクションで、フィルタリングや並べ替えなどのデータベース操作を実行する機能を備えています。</p></div>
1184<h4 id="レコードのフィルタリングと並べ替え"><a class="anchorlink" href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A8%E4%B8%A6%E3%81%B9%E6%9B%BF%E3%81%88">7.3 レコードのフィルタリングと並べ替え</a></h4>
1185<p>データベースから受け取った結果をフィルタで絞り込みたい場合は、以下のように<code>where</code>メソッドでカラムごとにレコードをフィルタリングできます。</p>
1186<div class="code_container">
1187<pre class="highlight">
1188store(dev)&gt; Product.where(name: "Pants")
1189  Product Load (1.5ms)  SELECT "products".* FROM "products" WHERE "products"."name" = 'Pants' /* loading for pp */ LIMIT 11 /*application='Store'*/
1190=&gt; [#&lt;Product:0x000000012184d858 id: 2, name: "Pants", created_at: "2024-11-09 16:36:01.856751000 +0000", updated_at: "2024-11-09 16:36:01.856751000 +0000"&gt;]
1191</pre>
1192</div>
1193<p>これにより、生成された<code>SELECT</code> SQLクエリに<code>WHERE</code>句も追加され、<code>"Pants"</code>にマッチする<code>name</code>を持つレコードがフィルタで絞り込まれます。同じ名前を持つレコードが複数返される可能性があるため、ここでも<code>ActiveRecord::Relation</code>が返されます。</p>
1194<p><code>order(name: :asc)</code>メソッドを使うと、レコードを以下のようにアルファベット昇順で並べ替えられます。</p>
1195<div class="code_container">
1196<pre class="highlight">
1197store(dev)&gt; Product.order(name: :asc)
1198  Product Load (0.3ms)  SELECT "products".* FROM "products" /* loading for pp */ ORDER BY "products"."name" ASC LIMIT 11 /*application='Store'*/
1199=&gt; [#&lt;Product:0x0000000120e02a88 id: 2, name: "Pants", created_at: "2024-11-09 16:36:01.856751000 +0000", updated_at: "2024-11-09 16:36:01.856751000 +0000"&gt;,
1200 #&lt;Product:0x0000000120e02948 id: 1, name: "T-Shirt", created_at: "2024-11-09 16:35:01.117836000 +0000", updated_at: "2024-11-09 16:35:01.117836000 +0000"&gt;]
1201</pre>
1202</div>
1203<h4 id="レコードを検索する"><a class="anchorlink" href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B">7.4 レコードを検索する</a></h4>
1204<p>
1204特定のレコードを1件検索したい場合はどうすればよいでしょうか?</p>
1205<p>これを行うには、<code>find</code>クラスメソッドでIDを指定する形で、1件のレコードを検索します。以下のコードは、<code>find</code>メソッドにID<code>1</code>を指定して呼び出しています。</p>
1206<div class="code_container">
1207<pre class="highlight">
1208store(dev)&gt; Product.find(1)
1209  Product Load (0.2ms)  SELECT "products".* FROM "products" WHERE "products"."id" = 1 LIMIT 1 /*application='Store'*/
1210=&gt; #&lt;Product:0x000000012054af08 id: 1, name: "T-Shirt", created_at: "2024-11-09 16:35:01.117836000 +0000", updated_at: "2024-11-09 16:35:01.117836000 +0000"&gt;
1211</pre>
1212</div>
1213<p>これにより、<code>SELECT</code>クエリが生成されますが、渡されたID <code>1</code>にマッチする<code>id</code>カラムを<code>WHERE</code>で指定しています。また、返すレコードを1件のみに絞るため、<code>LIMIT 1</code>も追加されています。</p>
1214<p>ここでは、データベースからレコードを1件だけ取得したいので、<code>ActiveRecord::Relation</code>ではなく<code>Product</code>モデルのインスタンスを取得します。</p>
1215<h4 id="レコードを更新する"><a class="anchorlink" href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B4%E6%96%B0%E3%81%99%E3%82%8B">7.5 レコードを更新する</a></h4>
1216<p>レコードを更新するには、「<code>update</code>を使う」「属性を割り当ててから<code>save</code>を呼び出す」という2つの方法が使えます。</p>
1217<p><code>Product</code>モデルのインスタンスで<code>update</code>を呼び出し、新しい属性のハッシュを渡してデータベースに保存できます。これにより、「属性の割り当て」「バリデーションの実行」「変更のデータベースへの保存」を1回のメソッド呼び出しでまとめて実行できます。</p>
1218<div class="code_container">
1219<pre class="highlight">
1220store(dev)&gt; product = Product.find(1)
1221store(dev)&gt; product.update(name: "Shoes")
1222  TRANSACTION (0.1ms)  BEGIN immediate TRANSACTION /*application='Store'*/
1223  Product Update (0.3ms)  UPDATE "products" SET "name" = 'Shoes', "updated_at" = '2024-11-09 22:38:19.638912' WHERE "products"."id" = 1 /*application='Store'*/
1224  TRANSACTION (0.4ms)  COMMIT TRANSACTION /*application='Store'*/
1225=&gt; true
1226</pre>
1227</div>
1228<p>これにより、データベース内の製品名が<code>"T-Shirt"</code>から<code>"Shoes"</code>に更新されます。
1229<code>Product.all</code>を再度実行してこれを確認してみましょう。</p>
1230<div class="code_container">
1231<pre class="highlight">
1232store(dev)&gt; Product.all
1233</pre>
1234</div>
1235<p>製品名はShoesとPantsの2つになっていることがわかります。</p>
1236<div class="code_container">
1237<pre class="highlight">
1238  Product Load (0.3ms)  SELECT "products".* FROM "products" /* loading for pp */ LIMIT 11 /*application='Store'*/
1239=&gt;
1240[#&lt;Product:0x000000012c0f7300
1241  id: 1,
1242  name: "Shoes",
1243  created_at: "2024-12-02 20:29:56.303546000 +0000",
1244  updated_at: "2024-12-02 20:30:14.127456000 +0000"&gt;,
1245 #&lt;Product:0x000000012c0f71c0
1246  id: 2,
1247  name: "Pants",
1248  created_at: "2024-12-02 20:30:02.997261000 +0000",
1249  updated_at: "2024-12-02 20:30:02.997261000 +0000"&gt;]
1250</pre>
1251</div>
1252<p>2番目の方法として、属性を割り当て、変更をバリデーションしてデータベースに保存する準備を終えてから、<code>save</code>を呼び出す方法も使えます。</p>
1253<p>今度は、<code>"Shoes"</code>という製品名を<code>"T-Shirt"</code>に戻してみましょう。</p>
1254<div class="code_container">
1255<pre class="highlight">
1256store(dev)&gt; product = Product.find(1)
1257store(dev)&gt; product.name = "T-Shirt"
1258=&gt; "T-Shirt"
1259store(dev)&gt; product.save
1260  TRANSACTION (0.1ms)  BEGIN immediate TRANSACTION /*application='Store'*/
1261  Product Update (0.2ms)  UPDATE "products" SET "name" = 'T-Shirt', "updated_at" = '2024-11-09 22:39:09.693548' WHERE "products"."id" = 1 /*application='Store'*/
1262  TRANSACTION (0.0ms)  COMMIT TRANSACTION /*application='Store'*/
1263=&gt; true
1264</pre>
1265</div>
1266<h4 id="レコードを削除する"><a class="anchorlink" href="#%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B">7.6 レコードを削除する</a></h4>
1267<p>データベースからレコードを削除するには、<code>destroy</code>メソッドを使います。</p>
1268<div class="code_container">
1269<pre class="highlight">
1270store(dev)&gt; product.destroy
1271  TRANSACTION (0.1ms)  BEGIN immediate TRANSACTION /*application='Store'*/
1272  Product Destroy (0.4ms)  DELETE FROM "products" WHERE "products"."id" = 1 /*application='Store'*/
1273  TRANSACTION (0.1ms)  COMMIT TRANSACTION /*application='Store'*/
1274=&gt; #&lt;Product:0x0000000125813d48 id: 1, name: "T-Shirt", created_at: "2024-11-09 22:39:38.498730000 +0000", updated_at: "2024-11-09 22:39:38.498730000 +0000"&gt;
1275</pre>
1276</div>
1277<p>これにより、データベースから<code>"T-Shirt"</code>製品が削除されました。<code>Product.all</code>でこれを確認すると、パンツのみが返されることが分かります。</p>
1278<div class="code_container">
1279<pre class="highlight">
1280store(dev)&gt; Product.all
1281  Product Load (1.9ms)  SELECT "products".* FROM "products" /* loading for pp */ LIMIT 11 /*application='Store'*/
1282=&gt;
1283[#&lt;Product:0x000000012abde4c8
1284  id: 2,
1285  name: "Pants",
1286  created_at: "2024-11-09 22:33:19.638912000 +0000",
1287  updated_at: "2024-11-09 22:33:19.638912000 +0000"&gt;]
1288</pre>
1289</div>
1290<h4 id="バリデーション"><a class="anchorlink" href="#%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">7.7 バリデーション</a></h4>
1291<p>Active Recordは、データベースに挿入したデータが特定のルールに準拠していることを保証するための<strong>バリデーション</strong>(validation: 検証)機能を提供しています。</p>
1292<p>すべての製品に<code>name</code>カラムが存在することを保証するために、<code>Product</code>モデルに<code>presence</code>バリデーションを追加してみましょう。</p>
1293<div class="code_container">
1294<pre class="highlight">
1295<span class="c1"># app/models/product.rb</span>
1296<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
1297  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">
1297presence: </span><span class="kp">true</span>
1298<span class="k">end</span>
1299</pre>
1300</div>
1301<p>Railsは開発中にコードの変更を自動的に再読み込みすると冒頭で説明したことを思い出すかもしれません。
1302ただし、コードを更新したときにコンソールが実行中の場合は、コンソールから手動で更新する必要があります。それでは、<code>reload!</code>を実行して更新を反映してみましょう。</p>
1303<div class="code_container">
1304<pre class="highlight">
1305store(dev)&gt; reload!
1306Reloading...
1307</pre>
1308</div>
1309<p>今度は、Railsコンソールでわざと<code>name</code>を指定せずに<code>Product</code>インスタンスを作成してみましょう。</p>
1310<div class="code_container">
1311<pre class="highlight">
1312store(dev)&gt; product = Product.new
1313store(dev)&gt; product.save
1314=&gt; false
1315</pre>
1316</div>
1317<p>今回は、<code>name</code>属性が指定されていないため、<code>save</code>は<code>false</code>を返します。</p>
1318<p>Railsは、作成・更新・保存の操作中にバリデーションを自動的に実行して、有効な入力であることを保証します。</p>
1319<p>バリデーションによって生成されたエラーのリストを表示するには、以下のようにインスタンスで<code>errors</code>を呼び出します。</p>
1320<div class="code_container">
1321<pre class="highlight">
1322store(dev)&gt; product.errors
1323=&gt; #&lt;ActiveModel::Errors [#&lt;ActiveModel::Error attribute=name, type=blank, options={}&gt;]&gt;
1324</pre>
1325</div>
1326<p>これは、存在チェックのエラーを詳しく知らせてくれる<code>ActiveModel::Errors</code>オブジェクトを返します。</p>
1327<p>また、ユーザーインターフェイスに表示できるわかりやすいエラーメッセージを生成することも可能です。</p>
1328<div class="code_container">
1329<pre class="highlight">
1330store(dev)&gt; product.errors.full_messages
1331=&gt; ["Name can't be blank"]
1332</pre>
1333</div>
1334<p>次は、この製品をブラウザで表示するためのWebインターフェースを構築しましょう。</p>
1335<p>Railsコンソールはひとまずおしまいにします。<code>exit</code>を実行してコンソールを終了できます。</p>
1336<h3 id="railsのリクエストの流れ"><a class="anchorlink" href="#rails%E3%81%AE%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%AE%E6%B5%81%E3%82%8C">8 Railsのリクエストの流れ</a></h3>
1337<p>Railsで「Hello」を表示するには、少なくとも「<strong>ルーティング</strong>」と「<strong>コントローラ</strong>」、そしてコントローラに付随する「<strong>アクション</strong>」と「<strong>ビュー</strong>」を作成する必要があります。</p>
1338<ul>
1339<li>ルーティング(route): 受け取ったリクエストを、適切なコントローラのアクションに対応付けます。</li>
1340<li>コントローラアクション(controller action): リクエストを処理し、ビューに表示するデータを準備します。</li>
1341<li>ビュー(view): データを表示するためのテンプレートです。</li>
1342</ul>
1343<p>これらは、実装の観点では以下のようになります。</p>
1344<p>ルーティングはRubyの<a href="https://ja.wikipedia.org/wiki/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E5%9B%BA%E6%9C%89%E8%A8%80%E8%AA%9E">DSL(ドメイン固有言語)</a>で記述されたルールです。
1345コントローラは普通のRubyクラスであり、そのpublicメソッドがアクションになります。
1346ビューはテンプレートであり、通常はHTMLとRubyを組み合わせて記述されます。</p>
1347<p>以上はごく簡単な説明ですが、次にこれらの各ステップについてさらに詳しく説明します。</p>
1348<h3 id="ルーティング"><a class="anchorlink" href="#%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">9 ルーティング</a></h3>
1349<p>Railsのルーティング(route、routing)はURLを構成する要素の1つであり、受信したHTTPリクエストを適切なコントローラとアクションに転送することでリクエストの処理方法を決定します。</p>
1350<p>まず、URLとHTTPリクエストメソッドについて簡単に復習しましょう。</p>
1351<h4 id="urlの構成要素"><a class="anchorlink" href="#url%E3%81%AE%E6%A7%8B%E6%88%90%E8%A6%81%E7%B4%A0">9.1 URLの構成要素</a></h4>
1352<p>URLがどのような要素から構成されているかを詳しく見てみましょう。</p>
1353<div class="code_container">
1354<pre class="highlight">
1355https://example.org/products?sale=true&amp;sort=asc
1356</pre>
1357</div>
1358<p>上のURLの各要素には、以下のような名前があります。</p>
1359<ul>
1360<li>
1361<code>https</code>の部分は<strong>プロトコル</strong>(protocol)と呼ばれます</li>
1362<li>
1363<code>example.org</code>の部分は<strong>ホスト</strong>(host)と呼ばれます</li>
1364<li>
1365<code>/products</code>の部分は<strong>パス</strong>(path)と呼ばれます</li>
1366<li>
1367<code>?sale=true&amp;sort=asc</code>の部分は<strong>クエリパラメータ</strong>(query parameters)と呼ばれます</li>
1368</ul>
1369<h4 id="httpメソッドとその目的"><a class="anchorlink" href="#http%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%A8%E3%81%9D%E3%81%AE%E7%9B%AE%E7%9A%84">9.2 HTTPメソッドとその目的</a></h4>
1370<p>HTTPリクエストは、特定のURLに対してサーバーが実行すべきアクションを指示するときにHTTPメソッド(HTTP verb
1370: HTTP動詞とも呼ばれます)を利用します。</p>
1371<p>最も一般的なHTTPメソッドは次のとおりです。</p>
1372<ul>
1373<li>
1374<code>GET</code>リクエスト:
1375特定のURLのデータを取得するようサーバーに指示します(ページの読み込みやレコードの取得など)。</li>
1376<li>
1377<code>POST</code>リクエスト:
1378処理を実行するためのデータをURLに送信します(通常は新しいレコードを作成します)。</li>
1379<li>
1380<code>PUT</code>または<code>PATCH</code>リクエスト:
1381既存のレコードを更新するためのデータをURLに送信します。</li>
1382<li>
1383<code>DELETE</code>リクエスト:
1384URLに送信されると、レコードを削除するようサーバーに指示します。</li>
1385</ul>
1386<h4 id="railsのルーティング"><a class="anchorlink" href="#rails%E3%81%AE%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">9.3 Railsのルーティング</a></h4>
1387<p>Railsにおけるルーティングは、HTTPメソッドとURLパスをペアにしたコード行を指します。
1388ルーティングは、どの<code>controller</code>と<code>action</code>でリクエストに応答すべきかをRailsに指示します。</p>
1389<p>Railsでルーティングを定義するには、コードエディタを再び開いて、<code>config/routes.rb</code>ファイル内のルーティングに、以下の<code>get</code>で始まる行を追加します。</p>
1390<div class="code_container">
1391<pre class="highlight">
1392<span class="c1"># config/routes.rb</span>
1393<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1394  <span class="c1"># (省略)</span>
1395  <span class="n">get</span> <span class="s2">"/products"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#index"</span>  <span class="c1"># この行を追加</span>
1396<span class="k">end</span>
1397</pre>
1398</div>
1399<p>このルーティングは、<code>/products</code>パスへの<code>GET</code>リクエストを探索するようRailsに指示します。この例では、リクエストのルーティング先として<code>"products#index"</code>を指定しています。</p>
1400<p>マッチするリクエストが見つかると、Railsはそのリクエストを<code>ProductsController</code>というコントローラ内の<code>index</code>アクションに送信します。Railsはこのようにしてリクエストを処理し、ブラウザにレスポンスを返します。</p>
1401<p>上のルーティングでは、「プロトコル」「ドメイン」「クエリパラメータ」の指定が不要であることに気付くでしょう。その理由は、リクエストは基本的にプロトコルとドメインによってサーバーに確実に届くためです。Railsはリクエストを取得すると、定義済みのルーティングに基づいて、リクエストに応答するためのパスを認識します。</p>
1402<p>なお、クエリパラメータは、Railsがリクエストに適用できるオプションのようなもので必須ではなく、通常はコントローラでデータをフィルタリングするときに使われます。</p>
1403<p><img src="/railsguides/images/getting_started/routing_light.jpg" alt="Railsのルーティングの流れ" data-src="/railsguides/images/getting_started/routing_light.jpg" class="lazyload" loading="lazy"></p>
1404<p>別の例も見てみましょう。
1405前述のルーティングの下に、以下の行を追加します。</p>
1406<div class="code_container">
1407<pre class="highlight">
1408<span class="c1"># config/routes.rb</span>
1409<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1410  <span class="c1"># ...</span>
1411  <span class="n">get</span> <span class="s2">"/products"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#index"</span>
1412  <span class="n">post</span> <span class="s2">
1412"/products"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#create"</span>
1413<span class="k">end</span>
1414</pre>
1415</div>
1416<p>ここでは、<code>/products</code>パスへの<code>POST</code>リクエストを受け取ったら、<code>ProductsController</code>の<code>create</code>アクションでリクエストを処理するようRailsに指示しています。</p>
1417<p>ルーティングでは、特定のパターンを持つURLにマッチさせる必要が生じる場合もあります。
1418では以下のルーティングは、どのように機能するかおわかりでしょうか?</p>
1419<div class="code_container">
1420<pre class="highlight">
1421<span class="c1"># config/routes.rb</span>
1422<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1423  <span class="c1"># ...</span>
1424  <span class="n">get</span> <span class="s2">"/products/:id"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#show"</span>
1425<span class="k">end</span>
1426</pre>
1427</div>
1428<p>このルーティングのパスには<code>:id</code>が含まれています。これは<strong>パラメータ</strong>(parameter、paramsとも)と呼ばれ、後でリクエストを処理するときに使うURLの一部がここにキャプチャされます。</p>
1429<p>たとえばユーザーが<code>/products/1</code>というパスにアクセスすると、<code>:id</code>パラメータが<code>1</code>に設定され、コントローラアクションでIDが1の製品レコードを検索して表示できるようになります。
1430<code>/products/2</code>は同様に、IDが2の製品を表示するのに使えます。</p>
1431<p>ルーティングのパラメータには、整数以外のものも使えます。</p>
1432<p>たとえば、さまざまな記事を含むブログサービスでは、以下のルーティングで<code>/blog/hello-world</code>とマッチするようになります。</p>
1433<div class="code_container">
1434<pre class="highlight">
1435<span class="c1"># config/routes.rb</span>
1436<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1437  <span class="c1"># ...</span>
1438  <span class="n">get</span> <span class="s2">"/blog/:title"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"blog#show"</span>
1439<span class="k">end</span>
1440</pre>
1441</div>
1442<p>上のルーティングでは、<code>/blog/hello-world</code>から<code>hello-world</code>というパラメータを<code>slug</code>としてキャプチャし、このパラメータにマッチするタイトルのブログ投稿を検索できるようになります。</p>
1443<h5 id="crudのルーティング"><a class="anchorlink" href="#crud%E3%81%AE%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">9.3.1 CRUDのルーティング</a></h5>
1444<p><a href="https://ja.wikipedia.org/wiki/Representational_State_Transfer#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9">リソース</a>への操作で通常必要となる一般的な操作は、「作成」「読み取り」「更新」「削除」の4つであり、<a href="https://ja.wikipedia.org/wiki/CRUD">CRUD</a>と呼ばれます。</p>
1445<p>これは、8つの一般的なコントローラアクションに相当します。</p>
1446<ul>
1447<li>
1448<code>index</code>: すべてのレコードを表示します</li>
1449<li>
1450<code>new</code>: 新しいレコード1件を作成するためのフォームをレンダリングします</li>
1451<li>
1452<code>create</code>: <code>new</code>のフォーム送信を処理し、エラーを処理してレコードを1件作成します</li>
1453<li>
1454<code>show</code>: 指定のレコード1件をレンダリングして表示します</li>
1455<li>
1456<code>edit</code>: 指定のレコード1件を更新するためのフォームをレンダリングします</li>
1457<li>
1458<code>update</code>(リソース全体): リソース全体を更新するフォーム送信を処理します。
1459これは通常、<code>PUT</code>リクエストでトリガーされ、リソースのすべての属性をç½
1459®ãæ›ãˆã¾ã™</li>
1460<li>
1461<code>update</code>(特定のリソースのみ): 特定の属性のみを更新するフォーム送信を処理します。
1462これは通常、<code>PATCH</code>リクエストでトリガーされ、リソースを部分的に更新します</li>
1463<li>
1464<code>destroy</code>: 指定のレコード1件を削除します</li>
1465</ul>
1466<p>これらのCRUDアクションのルーティングは、以下のように書くことで追加することも一応可能です。</p>
1467<div class="code_container">
1468<pre class="highlight">
1469<span class="c1"># config/routes.rb</span>
1470<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1471  <span class="c1"># ...</span>
1472  <span class="n">get</span> <span class="s2">"/products"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#index"</span>
1473
1474  <span class="n">get</span> <span class="s2">"/products/new"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#new"</span>
1475  <span class="n">post</span> <span class="s2">"/products"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#create"</span>
1476
1477  <span class="n">get</span> <span class="s2">"/products/:id"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#show"</span>
1478
1479  <span class="n">get</span> <span class="s2">"/products/:id/edit"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#edit"</span>
1480  <span class="n">patch</span> <span class="s2">"/products/:id"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#update"</span>
1481  <span class="n">put</span> <span class="s2">"/products/:id"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#update"</span>
1482
1483  <span class="n">delete</span> <span class="s2">"/products/:id"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"products#destroy"</span>
1484<span class="k">end</span>
1485</pre>
1486</div>
1487<h5 id="リソースルーティング"><a class="anchorlink" href="#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0">9.3.2 リソースルーティング</a></h5>
1488<p>これら8つのルーティングを毎回入力するのは冗長なので、Railsではルーティングを1行で定義できるショートカットを提供しています。</p>
1489<p>上記のルーティングを以下の1行に置き換えて、上と同じCRUDアクションをすべて作成できるようにしましょう。</p>
1490<div class="code_container">
1491<pre class="highlight">
1492<span class="c1"># config/routes.rb</span>
1493<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1494  <span class="c1"># ...</span>
1495  <span class="n">resources</span> <span class="ss">:products</span>
1496<span class="k">end</span>
1497</pre>
1498</div>
1499<div class="info"><p>CRUDアクションの一部しか使わない場合は、必要なアクションだけを正確に指定し、使わないアクションは無効にしておきましょう。詳しくは<a href="https://railsguides.jp/routing.html#%E4%BD%9C%E6%88%90%E3%81%95%E3%82%8C%E3%82%8B%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%92%E5%88%B6%E9%99%90%E3%81%99%E3%82%8B">ルーティングガイド</a>を参照してください。</p></div>
1500<h4 id="ルーティングコマンド"><a class="anchorlink" href="#%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89">9.4 ルーティングコマンド</a></h4>
1501<p>Railsには、アプリケーションが応答するルーティングをすべて表示するコマンドが用意されています。</p>
1502<p>ターミナルを開いて以下のコマンドを実行します。</p>
1503<div class="code_container">
1504<pre class="highlight">
1505<span class="gp">$</span><span class="w"> </span>bin/rails routes
1506</pre>
1507</div>
1508<p><code>resources :products</code>で生成されたルーティングが以下のように表示されます。</p>
1509<div class="code_container">
1510<pre class="highlight">
1511      Prefix Verb   URI Pattern                  Controller#Action
1512    products GET    /products(.:format)          products#index
1513             POST   /products(.:format)          products#create
1514 new_product GET    /products/new(.:format)      products#new
1515edit_product GET    /products/:id/edit(.:format) products#edit
1516     product GET    /products/:id(.:format)      products#show
1517             PATCH  /products/:id(.:format)      products#update
1518             PUT    /products/:id(.:format)      products#update
1519             DELETE /products/:id(.:format)      products#destroy
1520</pre>
1521</div>
1522<p>表示されるルーティングには、上の他にもヘルスチェックなどの他の組み込みのRails機能によるルーティングが含まれているのがわかります。</p>
1523<div class="info"><p>訳注: 開発中のRailsサーバーでは、<code>http://localhost:3000/rails/info/routes</code>にブラウザでアクセスすることでルーティング情報を表示できます。</p></div>
1524<h3 id="コントローラとアクション"><a class="anchorlink" href="#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%A8%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3">10 コントローラとアクション</a></h3>
1525<p>
1525製品のルーティングを定義したので、次はコントローラとアクションを実装して、これらのURLへのリクエストを処理できるようにしましょう。</p>
1526<p>以下の<code>bin/rails generate</code>コマンドは、<code>index</code>アクションを含む<code>ProductsController</code>を生成します。ルーティングは既に設定したので、<code>--skip-routes</code>フラグでジェネレータでのルーティング生成部分をスキップできます。</p>
1527<div class="code_container">
1528<pre class="highlight">
1529<span class="gp">$</span><span class="w"> </span>bin/rails generate controller Products index <span class="nt">--skip-routes</span>
1530<span class="go">      create  app/controllers/products_controller.rb
1531      invoke  erb
1532      create    app/views/products
1533      create    app/views/products/index.html.erb
1534      invoke  test_unit
1535      create    test/controllers/products_controller_test.rb
1536      invoke  helper
1537      create    app/helpers/products_helper.rb
1538      invoke    test_unit
1539</span>
1540</pre>
1541</div>
1542<p>このコマンドを実行すると、コントローラ用に以下のさまざまなファイルが生成されます。</p>
1543<ul>
1544<li>コントローラファイル自身(<code>products_controller.rb</code>)</li>
1545<li>生成したコントローラで利用するビューを保存するフォルダ(<code>app/views/products/</code>)</li>
1546<li>コントローラ生成時に指定したアクションに対応するビューファイル(<code>index.html.erb</code>)</li>
1547<li>このコントローラ用のテストファイル(<code>products_controller_test.rb</code>)</li>
1548<li>ビューのロジックを切り出して配置するためのヘルパーファイル(<code>products_helper.rb</code>)</li>
1549</ul>
1550<p><code>app/controllers/products_controller.rb</code>で定義されている<code>ProductsController</code>をエディタで開くと、以下のような感じになっているはずです。</p>
1551<div class="code_container">
1552<pre class="highlight">
1553<span class="c1"># app/controllers/products_controller.rb</span>
1554<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1555  <span class="k">def</span> <span class="nf">index</span>
1556  <span class="k">end</span>
1557<span class="k">end</span>
1558</pre>
1559</div>
1560<div class="note"><p><code>products_controller.rb</code>というファイル名は、このファイルで定義されている<code>ProductsController</code>というクラス名を小文字に変えてアンダースコア区切りに変更したものであることに気付くかもしれません。この命名パターンを守ることで、他の言語で見られるような<code>require</code>を使わなくても、Railsがコードを自動的に読み込めるようになります。</p></div>
1561<p>ここでの<code>index</code>メソッドはアクションです。メソッドの中身は空ですが、メソッドが空の場合は、デフォルトでアクション名と一致する名前のテンプレートをレンダリングするようになっているので問題ありません。</p>
1562<p><code>index</code>アクションを実行すると、<code>app/views/products/index.html.erb</code>をレンダリングします。このファイルをコードエディタで開くと、レンダリングされるHTMLが以下のように表示されます。</p>
1563<div class="code_container">
1564<pre class="highlight">
1565<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
1566<span class="nt">&lt;h1&gt;</span>Products#index<span class="nt">&lt;/h1&gt;</span>
1567<span class="nt">&lt;p&gt;</span>Find me in app/views/products/index.html.erb<span class="nt">&lt;/p&gt;</span>
1568</pre>
1569</div>
1570<h4 id="リクエストを作成する"><a class="anchorlink" href="#%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">10.1 リクエストを作成する</a></h4>
1571<p>作成した結果をブラウザで確認してみましょう。</p>
1572<p>まず、ターミナルで<code>bin/rails server</code>を実行してRailsサーバーを起動します。
1573次に、ブラウザで<code>http://localhost:3000</code>を開くと、Railsのウェルカムページが表示されます。</p>
1574<p>ブラウザで<code>http://localhost:3000/products</code>を開くと、Railsは製品の<code>index</code>ページのHTMLをレンダリングします。</p>
1575<p>このときの処理の流れは以下のようになります。</p>
1576<ol>
1577<li>ブラウザが<code>/products</code>パスへのリクエストを送信すると、ルーティングは<code>products#index</code>にマッチします。</li>
1578<li>次にRailsは、このリクエストを<code>ProductsController</code>に送信して<code>index</code>アクションを呼び出します。</li>
1579<li>
1580<code>index</code>アクションは空なので、Railsはこのコントローラアクションに一致する<code>app/views/products/index.html.erb</code>テンプレートをレンダリングして、ブラウザにレスポンスを返します。</li>
1581</ol>
1582<p>なお、<code>config/routes.rb</code>ファイルに以下の行を追加すると、rootパスにアクセスしたときのルーティングで<code>Products</code>の<code>index</code>アクションをレンダリングするようにRailsに指示できます。</p>
1583<div class="code_container">
1584<pre class="highlight">
1585<span class="c1"># config/routes.rb</span>
1586<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
1587  <span class="c1"># ...</span>
1588  <span class="n">root</span> <span class="s2">"products#index"</span>
1589  <span class="n">resources</span> <span class="ss">:products</span>
1590<span class="k">end</span>
1591</pre>
1592</div>
1593<p>これで、<code>http://localhost:3000</code>にアクセスすると、Railsが<code>Products#index</code>をレンダリングするようになります。</p>
1594<h4 id="インスタンス変数"><a class="anchorlink" href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E5%A4%89%E6%95%B0">10.2 インスタンス変数</a></h4>
1595<p>さらに先に進んで、データベースにあるレコードをいくつかレンダリングしてみましょう。</p>
1596<p><code>index</code>アクションを更新して以下のようにデータベースクエリを追加し、それをインスタンス変数に割り当ててみましょう。Railsのコントローラでは、ビューにデータを渡すときにイãƒ
1596³ã‚¹ã‚¿ãƒ³ã‚¹å¤‰æ•°ï¼ˆ<code>@</code>で始まる変数)が使われます。</p>
1597<div class="code_container">
1598<pre class="highlight">
1599<span class="c1"># app/controllers/products_controller.rb</span>
1600<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1601  <span class="k">def</span> <span class="nf">index</span>
1602    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
1603  <span class="k">end</span>
1604<span class="k">end</span>
1605</pre>
1606</div>
1607<p>次に、<code>app/views/products/index.html.erb</code>のビューテンプレートファイル内にあるHTMLを以下のERBコードに置き換えます。</p>
1608<div class="code_container">
1609<pre class="highlight">
1610<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
1611<span class="cp">&lt;%=</span> <span class="n">debug</span> <span class="vi">@products</span> <span class="cp">%&gt;</span>
1612</pre>
1613</div>
1614<p><a href="https://docs.ruby-lang.org/ja/latest/class/ERB.html">ERB</a>はEmbedded Rubyの略で、Rubyコードを実行してRailsでHTMLを動的に生成できるようにします。</p>
1615<p><code>&lt;%= %&gt;</code>タグは、その内側に書いたRubyコードを実行して戻り値をブラウザで出力するようERBに指示します。この場合、受け取った<code>@products</code>が<code>debug</code>でYAMLに変換され、YAMLが出力されます。</p>
1616<p>ブラウザで<code>http://localhost:3000/</code>
1616を更新すると、出力結果が変更されたことがわかります。表示されているのは、データベース内のレコードがYAML形式に変換されたものです。</p>
1617<p><code>debug</code>ヘルパーは、デバッグで役立つように変数をYAML形式で出力します。
1618たとえば、コントローラで複数形の<code>@products</code>のつもりでうっかり単数形の<code>@product</code>を書いてしまった場合、変数がコントローラで正しく設定されていないことを突き止めるのに役立ちます。</p>
1619<div class="info"><p>その他に利用可能なヘルパーについて詳しくは<a href="action_view_helpers.html">Action Viewヘルパーガイド</a>を参照してください。</p></div>
1620<p>次は、ビューにすべての製品名がリスト表示されるようにしてみましょう。
1621<code>app/views/products/index.html.erb</code>を以下のように更新します。</p>
1622<div class="code_container">
1623<pre class="highlight">
1624<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
1625<span class="nt">&lt;h1&gt;</span>Products<span class="nt">&lt;/h1&gt;</span>
1626
1627<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"products"</span><span class="nt">&gt;</span>
1628  <span class="cp">&lt;%</span> <span class="vi">@products</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">product</span><span class="o">|</span> <span class="cp">%&gt;</span>
1629    <span class="nt">&lt;div&gt;</span>
1630      <span class="cp">&lt;%=</span> <span class="n">product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span>
1631    <span class="nt">&lt;/div&gt;</span>
1632  <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1633<span class="nt">&lt;/div&gt;</span>
1634</pre>
1635</div>
1636<p>ERB内のコードは、<code>ActiveRecord::Relation</code>オブジェクトである<code>@products</code>内の各製品をループし、製品名を含む<code>&lt;div&gt;</code>タグをレンダリングします。</p>
1637<p>ここでも新しいERBタグが使われています。
1638<code>&lt;% %&gt;</code>の中に書いたRubyコードは実行時に評価されますが、戻り値をブラウザに出力しない点が<code>&lt;%= %&gt;</code>と異なります。これにより、そのままだとHTMLに不要な配列を出力する<code>@products.each</code>の出力が無視されるようになります。</p>
1639<h4 id="crudアクション"><a class="anchorlink" href="#crud%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3">10.3 CRUDアクション</a></h4>
1640<p>次は、個別の製品を1件ずつ表示できるようにする必要があります。これは、リソースを読み取るためのCRUDのR(Read)に相当します。</p>
1641<p>製品へのルーティングは、既に<code>resources :products</code>ルーティングでまとめて定義してあるので、<code>products#show</code>を指すルーティングとして<code>/products/:id</code> が生成されるようになっています。</p>
1642<p>次に、これに対応する<code>show</code>アクションを<code>ProductsController</code>に追加して、呼び出されたときの振る舞いを定義する必要があります。</p>
1643<h4 id="個別の製品を表示する"><a class="anchorlink" href="#%E5%80%8B%E5%88%A5%E3%81%AE%E8%A3%BD%E5%93%81%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B">10.4 個別の製品を表示する</a></h4>
1644<p><code>ProductsController</code>をエディタで開いて、以下のように<code>show</code>アクションを追加します。</p>
1645<div class="code_container">
1646<pre class="highlight">
1647<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1648  <span class="k">def</span> <span class="nf">index</span>
1649    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
1650  <span class="k">end</span>
1651
1652  <span class="k">def</span> <span class="nf">show</span>
1653    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
1654  <span class="k">end</span>
1655<span class="k">end</span>
1656</pre>
1657</div>
1658<p><code>index</code>アクションのときは、複数の製品を読み込むために複数形の<code>@products</code>を使いましたが 、この<code>show</code>アクションは、データベースから1件のレコードを読み込む(つまり1件の製品(one product)を表示する)ので、<strong>単数形の</strong><code>@product</code>を定義します。</p>
1659<p>データベースにクエリをかけるのに使うリクエストパラメータには、<code>params</code>でアクセスします。
1660この場合、<code>/products/:id</code>ルーティングの<code>:id</code>が使われます。
1661ユーザーがブラウザで<code>/products/1</code>にアクセスすると、<code>params</code>ハッシュに<code>{id: 1}</code>が含まれるので、<code>show</code>アクションで<code>Product.find(1)</code>を呼び出すと、IDが<code>1</code>の製品がデータベースから読み込まれます。</p>
1662<p>次に、<code>show</code>アクションに対応したビューが必要です。<code>ProductsController</code>はRailsの命名規則に沿って、<code>app/views/</code>フォルダの下の<code>products/</code>という名前のサブフォルダにビューファイルが置かれていることを想定しています。</p>
1663<p><code>show</code>アクションが必要としている<code>app/views/products/show.html.erb</code>ファイルをエディタで作成して、以下の内容を追加します。</p>
1664<div class="code_container">
1665<pre class="highlight">
1666<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
1667<span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
1668
1669<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Back"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span>
1670</pre>
1671</div>
1672<p>indexページに個別のshowページへのリンクを追加して、クリックして個別の製品ページに移動できるようにしておくと便利です。</p>
1673<p>そこで、<code>app/views/products/index.html.erb</code>ビューを以下のように更新して、新しく作ったshowページへのリンクを追加しましょう。<code>show</code>アクションへのパスには<code>&lt;a&gt;</code>タグを利用できます。</p>
1674<div class="code_container">
1675<pre class="highlight">
1676<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
1677<span class="nt">&lt;h1&gt;</span>Products<span class="nt">&lt;/h1&gt;</span>
1678
1679<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">
1679"products"</span><span class="nt">&gt;</span>
1680  <span class="cp">&lt;%</span> <span class="vi">@products</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">product</span><span class="o">|</span> <span class="cp">%&gt;</span>
1681    <span class="nt">&lt;div&gt;</span>
1682      <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/products/</span><span class="cp">&lt;%=</span> <span class="n">product</span><span class="p">.</span><span class="nf">id</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
1683        <span class="cp">&lt;%=</span> <span class="n">product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span>
1684      <span class="nt">&lt;/a&gt;</span>
1685    <span class="nt">&lt;/div&gt;</span>
1686  <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1687<span class="nt">&lt;/div&gt;</span>
1688</pre>
1689</div>
1690<p>ブラウザでindexページを再読み込みすると、期待通りにリンクが表示されたことがわかります。
1691しかしこれはさらに改善できます。</p>
1692<p>Railsは、パスとURLを生成するためのヘルパーメソッドを提供します。
1693<code>bin/rails routes</code>を実行すると以下のように表示されるPrefix列の<code>products</code>や<code>product</code>は、RubyコードでURLを生成できるヘルパーメソッド名に対応します。</p>
1694<div class="code_container">
1695<pre class="highlight">
1696  Prefix Verb   URI Pattern             Controller#Action
1697products GET    /products(.:format)     products#index
1698 product GET    /products/:id(.:format) products#show
1699</pre>
1700</div>
1701<p>これらのルーティングプレフィックスに対応するヘルパーメソッドは、以下のようになります。</p>
1702<ul>
1703<li>
1704<code>products_path</code>: <code>"/products"</code>というパスを生成する</li>
1705<li>
1706<code>products_url</code>: <code>"http://localhost:3000/products"</code>というURLを生成する</li>
1707<li>
1708<code>product_path(1)</code>: <code>"/products/1"</code>というパスを生成する</li>
1709<li>
1710<code>product_url(1)</code>: <code>"http://localhost:3000/products/1"</code>というURLを生成する</li>
1711</ul>
1712<p><code>プレフィックス名_path</code>は、ブラウザが現在のドメインであると理解する相対パスを返します。</p>
1713<p><code>プレフィックス名_url</code>は、「プロトコル」「ホスト」「ポート番号」を含む完全なURLを返します。</p>
1714<p>URLヘルパーは、ブラウザの外部で表示されるメールをレンダリングする場合に便利です。</p>
1715<p>URLヘルパーを<code>link_to</code>ヘルパーによる<code>&lt;a&gt;</code>タグ生成と組み合わせると、タグを直接書かずにRubyだけできれいにリンクを生成できるようになります。<code>link_to</code>には、リンクの表示名(ここでは<code>product.name</code>)と、<code>href</code>属性で使うリンク先のパスまたはURL(ここでは<code>product</code>)を渡せます。</p>
1716<p>これらのヘルパーを使って<code>app/views/products/index.html.erb</code>をリファクタリングすると、以下のように簡潔なビューコードになります。</p>
1717<div class="code_container">
1718<pre class="highlight">
1719<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
1720<span class="nt">&lt;h1&gt;</span>Products<span class="nt">&lt;/h1&gt;</span>
1721
1722<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"products"</span><span class="nt">&gt;</span>
1723  <span class="cp">&lt;%</span> <span class="vi">@products</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">product</span><span class="o">|</span> <span class="cp">%&gt;</span>
1724    <span class="nt">&lt;div&gt;</span>
1725      <span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="n">product</span><span class="p">.</span><span class="nf">name</span><span class="p">,</span> <span class="n">product_path</span><span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="nf">id</span><span class="p">)</span> <span class="cp">%&gt;</span>
1726    <span class="nt">&lt;/div&gt;</span>
1727  <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1728<span class="nt">&lt;/div&gt;</span>
1729</pre>
1730</div>
1731<h4 id="製品を作成する"><a class="anchorlink" href="#%E8%A3%BD%E5%93%81%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">10.5 製品を作成する</a></h4>
1732<p>これまでは製品をRailsコンソールで作成するしかありませんでしたが、今度はブラウザで製品を作成できるようにしましょう。</p>
1733<p>製品を作成するには、以下の2つのアクションを作成する必要があります。</p>
1734<ol>
1735<li>
1736<code>new</code>アクション: 製品情報を収集するためのフォームを作成する</li>
1737<li>
1738<code>create</code>アクション: 製品を保存してエラーをチェックする</li>
1739</ol>
1740<p>まずはコントローラで<code>new</code>アクションを作成しましょう。</p>
1741<div class="code_container">
1742<pre class="highlight">
1743<span class="c1"># app/controllers/products_controller.rb</span>
1744<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1745  <span class="k">def</span> <span class="nf">index</span>
1746    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
1747  <span class="k">end</span>
1748
1749  <span class="k">def</span> <span class="nf">show</span>
1750    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
1751  <span class="k">end</span>
1752
1753  <span class="k">def</span> <span class="nf">new</span>
1754    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span>
1755  <span class="k">end</span>
1756<span class="k">end</span>
1757</pre>
1758</div>
1759<p><code>new</code>アクションは、フォームフィールドの表示で使う新しい<code>Product</code>をインスタンス化します。</p>
1760<p>次は<code>app/views/products/index.html.erb</code>を以下のように更新して、<code>new</code>アクションにリンクできるようにします。</p>
1761<div class="code_container">
1762<pre class="highlight">
1763<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
1764<span class="nt">&lt;h1&gt;</span>Products<span class="nt">&lt;/h1&gt;</span>
1765
1766<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"New product"</span><span class="p">,</span> <span class="n">new_product_path</span> <span class="cp">%&gt;</span>
1767
1768<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"products"</span><span class="nt">&gt;</span>
1769  <span class="cp">&lt;%</span> <span class="vi">@products</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">product</span><span class="o">|</span> <span class="cp">%&gt;</span>
1770    <span class="nt">&lt;div&gt;</span>
1771      <span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="n">product</span><span class="p">.</span><span class="nf">name</span><span class="p">,</span> <span class="n">product_path</span><span class="p">(</span><span class="n">product</span><span class="p">.</span><span class="nf">id</span><span class="p">)</span> <span class="cp">%&gt;</span>
1772    <span class="nt">&lt;/div&gt;</span>
1773  <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1774<span class="nt">&lt;/div&gt;</span>
1775</pre>
1776</div>
1777<p><code>new</code>アクションに対応する<code>app/views/products/new.html.erb</code>ビューテンプレートを以下の内容で作成して、新しい<code>Product</code>のフォームをレンダリングできるようにします。</p>
1778<div class="code_container">
1779<pre class="highlight">
1780<span class="c">&lt;%# app/views/products/new.html.erb %&gt;</span>
1781<span class="nt">&lt;h1&gt;</span>New product<span class="nt">&lt;/h1&gt;</span>
1782
1783<span class="cp">&lt;%=</span> <span class="n">form_with</span> <span class="ss">model: </span><span class="vi">@product</span> <span class="k">do</span> <span class="o">|</span><span class="n">form</span><span class="o">|</span> <span class="cp">%&gt;</span>
1784  <span class="nt">&lt;div&gt;</span>
1785    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">label</span> <span class="ss">:name</span> <span class="cp">%&gt;</span>
1786    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">text_field</span> <span class="ss">:name</span> <span class="cp">%&gt;</span>
1787  <span class="nt">&lt;/div&gt;</span>
1788
1789  <span class="nt">&lt;div&gt;</span>
1790    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">submit</span> <span class="cp">%&gt;</span>
1791  <span class="nt">&lt;/div&gt;</span>
1792<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1793
1794<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">
1794"Cancel"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span>
1795</pre>
1796</div>
1797<p>この<code>new.html.erb</code>ビューでは、Railsの<code>form_with</code>ヘルパーを製品作成用のHTMLフォーム生成に利用しています。この<code>form_with</code>ヘルパーは、<strong>フォームビルダー</strong>を用いてCSRFトークン生成などの処理を行い、<code>model:</code>で指定されたものに基づいてURLを生成するとともに、送信ボタンのテキストにモデル名を反映することまで行います。</p>
1798<p>このページをブラウザで開いてソースを表示すると、フォームのHTMLは以下のようになります。</p>
1799<div class="code_container">
1800<pre class="highlight">
1801<span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">"/products"</span> <span class="na">accept-charset=</span><span class="s">"UTF-8"</span> <span class="na">method=</span><span class="s">"post"</span><span class="nt">&gt;</span>
1802  <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">name=</span><span class="s">"authenticity_token"</span> <span class="na">value=</span><span class="s">"UHQSKXCaFqy_aoK760zpSMUPy6TMnsLNgbPMABwN1zpW-Jx6k-2mISiF0ulZOINmfxPdg5xMyZqdxSW1UK-H-Q"</span> <span class="na">autocomplete=</span><span class="s">"off"</span><span class="nt">&gt;</span>
1803
1804  <span class="nt">&lt;div&gt;</span>
1805    <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"product_name"</span><span class="nt">&gt;</span>Name<span class="nt">&lt;/label&gt;</span>
1806    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"product[name]"</span> <span class="na">id=</span><span class="s">"product_name"</span><span class="nt">&gt;</span>
1807  <span class="nt">&lt;/div&gt;</span>
1808
1809  <span class="nt">&lt;div&gt;</span>
1810    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">name=</span><span class="s">"commit"</span> <span class="na">value=</span><span class="s">"Create Product"</span> <span class="na">data-disable-with=</span><span class="s">"Create Product"</span><span class="nt">&gt;</span>
1811  <span class="nt">&lt;/div&gt;</span>
1812<span class="nt">&lt;/form&gt;</span>
1813</pre>
1814</div>
1815<p>Railsのフォームビルダーによって、セキュリティ用のCSRFトークンやUTF-8サポート用の<code>accept-charset="UTF-8"</code>属性がフォームに組み込まれ、個別の入力フィールド名が設定され、<code>Create Product</code>送信ボタンも二重送信が無効な状態で追加されています。</p>
1816<p>フォームビルダーに新しい<code>Product</code>インスタンスを渡したので、新規レコード作成用のデフォルトルーティングである<code>/products</code>パスに<code>POST</code>リクエストを送信するように構成されたフォームが、自動的に生成されました。</p>
1817<p>ここから送信されるフォームを処理するには、まずコントローラに<code>create</code>アクションを以下のように実装する必要があります。</p>
1818<div class="code_container">
1819<pre class="highlight">
1820<span class="c1"># app/controllers/products_controller.rb</span>
1821<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1822  <span class="k">def</span> <span class="nf">index</span>
1823    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
1824  <span class="k">end</span>
1825
1826  <span class="k">def</span> <span class="nf">show</span>
1827    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
1828  <span class="k">end</span>
1829
1830  <span class="k">def</span> <span class="nf">new</span>
1831    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span>
1832  <span class="k">end</span>
1833
1834  <span class="k">def</span> <span class="nf">create</span>
1835    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
1836    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">save</span>
1837      <span class="n">redirect_to</span> <span class="vi">@product</span>
1838    <span class="k">else</span>
1839      <span class="n">render</span> <span class="ss">:new</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
1840    <span class="k">end</span>
1841  <span class="k">end</span>
1842
1843  <span class="kp">private</span>
1844    <span class="k">def</span> <span class="nf">product_params</span>
1845      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span> <span class="p">])</span>
1846    <span class="k">end</span>
1847<span class="k">end</span>
1848</pre>
1849</div>
1850<h5 id="strong-parameters"><a class="anchorlink" href="#strong-parameters">10.5.1 Strong Parameters</a></h5>
1851<p><code>create</code>アクションはフォームから送信されたデータを処理しますが、セキュリティのためにパラメータをフィルタリングしておかなければなりません。ここで役に立つのが、privateメソッドとして追加した<code>product_params</code>メソッドです。</p>
1852<div class="info"><p>訳注: メソッド名の<code>product</code>の部分はモデル名と同じ単数形にするのが慣例です。</p></div>
1853<p><code>product_params</code>メソッドは、リクエストで受け取ったパラメータを検査して、パラメータの配列を値として持つ<code>:product</code>というキーが必ず存在することを保証します。ここでは、製品に許可されているパラメータは<code>:name</code>のみなので、これ以外のどんなパラメータをRailsに渡しても無視されます(エラーにはなりません)。これにより、アプリケーションをハッキングしようとする悪意のあるユーザーからアプリケーションが保護されます。</p>
1854<p>詳しくは<a href="action_controller_overview.html#strong-parameters">Strong Parameter</a>を参照してください。</p>
1855<h5 id="エラー処理"><a class="anchorlink" href="#%E3%82%A8%E3%83%A9%E3%83%BC%E5%87%A6%E7%90%86">10.5.2 エラー処理</a></h5>
1856<p><code>product_params</code>を使ってこれらのパラメータを新しい<code>Product</code>に割り当てたら、データベースへの保存を試みる準備が整います。<code>@product.save</code>は、バリデーションを実行してレコードをデータベースに保存するようActive Recordに指示します。</p>
1857<p><code>save</code>が成功すると、新しい製品のshowページにリダイレクトします。<code>redirect_to</code>に Active Recordオブジェクトを渡すと、そのレコードの<code>show</code>アクションへのパスが生成されます。</p>
1858<div class="code_container">
1859<pre class="highlight">
1860<span class="n">redirect_to</span> <span class="vi">@product</span>
1861</pre>
1862</div>
1863<p>上を実行すると、<code>@product</code>は<code>Product</code>モデルのインスタンスなので、リダイレクト用に<code>"/products/2"</code>パスを生成します。このとき、パス内ではモデル名<code>Product</code>を複数形の<code>products</code>にしたうえで、オブジェクトID <code>2</code>を末尾に追加します。</p>
1864<p><code>save</code>が失敗して、レコードが有効にならなかった場合、同じフォームを再レンダリングして、ユーザーが無効なデータを修正できるようにします。<code>create</code>アクションの<code>else</code>では<code>render :new</code>をレンダリングするように指示しています。</p>
1865<p>Railsは<code>Products</code>コントローラにいることを認識しているので、<code>app/views/products/new.html.erb</code>ビューテンプレートをレンダリングする必要があります。</p>
1866<p><code>create</code>アクションでは<code>@product</code>変数が設定済みなので、<code>@product</code>をデータベースに保存できなかった場合でも、このビューテンプレートを再度レンダリングするとフォームに<code>Product</code>データが自動的に入力されます。</p>
1867<p>また、HTTPステータスを<a href="https://developer.mozilla.org/ja/docs/Web/HTTP/Status/422">422 Unprocessable Entity</a>に設定して、ブラウザにこの<code>POST</code>リクエストが失敗したことを伝えて、それに応じた処理に備えます。</p>
1868<h4 id="製品を編集する"><a class="anchorlink" href="#%E8%A3%BD%E5%93%81%E3%82%92%E7%B7%A8%E9%9B%86%E3%81%99%E3%82%8B">10.6 製品を編集する</a></h4>
1869<p>レコードを編集する処理は、レコードを作成する処理と非常に似ています。レコード作成では<code>new</code>アクションと<code>create</code>アクションを使いましたが、レコード編集では代わりに<code>edit</code>アクションと<code>update</code>アクションを使います。</p>
1870<p>コントローラで以下のコードを実装してみましょう。</p>
1871<div class="code_container">
1872<pre class="highlight">
1873<span class="c1"># app/controllers/products_controller.rb</span>
1874<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1875  <span class="k">def</span> <span class="nf">index</span>
1876    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
1877  <span class="k">end</span>
1878
1879  <span class="k">def</span> <span class="nf">show</span>
1880    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
1881  <span class="k">end</span>
1882
1883  <span class="k">def</span> <span class="nf">new</span>
1884    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span>
1885  <span class="k">end</span>
1886
1887  <span class="k">def</span> <span class="nf">create</span>
1888    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
1889    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">save</span>
1890      <span class="n">redirect_to</span> <span class="vi">@product</span>
1891    <span class="k">else</span>
1892      <span class="n">render</span> <span class="ss">:new</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
1893    <span class="k">end</span>
1894  <span class="k">end</span>
1895
1896  <span class="k">def</span> <span class="nf">edit</span>
1897    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
1898  <span class="k">end</span>
1899
1900  <span class="k">def</span> <span class="nf">update</span>
1901    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
1902    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
1903      <span class="n">redirect_to</span> <span class="vi">@product</span>
1904    <span class="k">else</span>
1905      <span class="n">render</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
1906    <span class="k">end</span>
1907  <span class="k">end</span>
1908
1909  <span class="kp">private</span>
1910    <span class="k">def</span> <span class="nf">product_params</span>
1911      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span> <span class="p">])</span>
1912    <span class="k">end</span>
1913<span class="k">end</span>
1914</pre>
1915</div>
1916<h5 id="ビューをパーシャルに切り出す"><a class="anchorlink" href="#%E3%83%93%E3%83%A5%E3%83%BC%E3%82%92%E3%83%91%E3%83%BC%E3%82%B7%E3%83%A3%E3%83%AB%E3%81%AB%E5%88%87%E3%82%8A%E5%87%BA%E3%81%99">10.6.1 ビューをパーシャルに切り出す</a></h5>
1917<p>新しい製品を作成するためのフォームは既に作成しましたが、このフォームを編集や更新のフォームでも再利用できたら便利だと思いませんか?これは、複数の場所でビューを再利用できるようにする<strong>パーシャル</strong>(partial)という機能を使ってフォームを<code>app/views/products/_form.html.erb</code>というパーシャルファイルに切り出すことで実現できます。</p>
1918<p>パーシャルのファイル名は、これがパーシャルであることを示すためにアンダースコア<code>_</code>で始まります。</p>
1919<p>それと同時に、ビューで使われているインスタンス変数をすべてローカル変数に置き換えたいと思います。ローカル変数は、パーシャルをレンダリングするときに定義できます。これを行うには、パーシャル内の<code>@product</code>を以下のように<code>product</code>に置き換えます。このとき、フォーム送信のエラーメッセージもフォームの一部として表示できるようにしておきます。</p>
1920<div class="code_container">
1921<pre class="highlight">
1922<span class="c">&lt;%# app/views/products/_form.html.erb %&gt;</span>
1923<span class="cp">&lt;%=</span> <span class="n">form_with</span> <span class="ss">model: </span><span class="n">product</span> <span class="k">do</span> <span class="o">|</span><span class="n">form</span><span class="o">|</span> <span class="cp">%&gt;</span>
1924  <span class="cp">&lt;%</span> <span class="k">if</span> <span class="n">form</span><span class="p">.</span><span class="nf">object</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">any?</span> <span class="cp">%&gt;</span>
1925    <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">
1925"error"</span><span class="nt">&gt;</span><span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">object</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">full_messages</span><span class="p">.</span><span class="nf">first</span> <span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span>
1926  <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1927
1928  <span class="nt">&lt;div&gt;</span>
1929    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">label</span> <span class="ss">:name</span> <span class="cp">%&gt;</span>
1930    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">text_field</span> <span class="ss">:name</span> <span class="cp">%&gt;</span>
1931  <span class="nt">&lt;/div&gt;</span>
1932
1933  <span class="nt">&lt;div&gt;</span>
1934    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">submit</span> <span class="cp">%&gt;</span>
1935  <span class="nt">&lt;/div&gt;</span>
1936<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
1937</pre>
1938</div>
1939<div class="info"><p>ローカル変数を使うと、値だけが異なるパーシャルを同じページで繰り返し再利用できるようになります。これは、indexページのように多数のアイテムのリストをレンダリングするときに便利です。</p></div>
1940<p>作成したこのパーシャルを<code>app/views/products/new.html.erb</code>ビューで使うには、フォームの部分を以下のようにパーシャルの<code>render</code>呼び出しに置き換えます。</p>
1941<div class="code_container">
1942<pre class="highlight">
1943<span class="c">&lt;%# app/views/products/new.html.erb %&gt;</span>
1944<span class="nt">&lt;h1&gt;</span>New product<span class="nt">&lt;/h1&gt;</span>
1945
1946<span class="cp">&lt;%=</span> <span class="n">render</span> <span class="s2">"form"</span><span class="p">,</span> <span class="ss">product: </span><span class="vi">@product</span> <span class="cp">%&gt;</span>
1947<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Cancel"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span>
1948</pre>
1949</div>
1950<p>Editビューも、フォームの<code>_form.html.erb</code>パーシャルのおかげで、Newビューとほぼ同じように書けます。</p>
1951<p>以下の内容で<code>app/views/products/edit.html.erb</code>を作成しましょう。</p>
1952<div class="code_container">
1953<pre class="highlight">
1954<span class="c">&lt;%# app/views/products/edit.html.erb %&gt;</span>
1955<span class="nt">&lt;h1&gt;</span>Edit product<span class="nt">&lt;/h1&gt;</span>
1956
1957<span class="cp">&lt;%=</span> <span class="n">render</span> <span class="s2">"form"</span><span class="p">,</span> <span class="ss">product: </span><span class="vi">@product</span> <span class="cp">%&gt;</span>
1958<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Cancel"</span><span class="p">,</span> <span class="vi">@product</span> <span class="cp">%&gt;</span>
1959</pre>
1960</div>
1961<p>ビューのパーシャルについて詳しくは、<a href="action_view_overview.html#%E3%83%91%E3%83%BC%E3%82%B7%E3%83%A3%E3%83%AB">Action Viewガイド</a>を参照してください。</p>
1962<p>次に、<code>app/views/products/show.html.erb</code>ビューテンプレートにEditページへのリンクを追加します。</p>
1963<div class="code_container">
1964<pre class="highlight">
1965<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
1966<span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
1967
1968<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Back"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span>
1969<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Edit"</span><span class="p">,</span> <span class="n">edit_product_path</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span>
1970</pre>
1971</div>
1972<h5 id="before-actionコールバックでコードをdryにする"><a class="anchorlink" href="#before-action%E3%82%B3%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E3%81%A7%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92dry%E3%81%AB%E3%81%99%E3%82%8B">10.6.2 <code>before_action</code>コールバックでコードをDRYにする</a></h5>
1973<p><code>edit</code>アクションと<code>update</code>アクションは、<code>show</code>と同様に既存のデータベースレコードが存在している必要があります。<code>before_action</code>を使うことで、そのための同じコードの重複を排除できます。</p>
1974<p><code>before_action</code>を使うと、アクション間で共有されているコードを抽出して、アクションの<strong>直前</strong>に実行できます。</p>
1975<p>上のコントローラでは、<code>@product = Product.find(params[:id])</code>という同じコードが<code>show</code>、<code>edit</code>、<code>update</code>という3つの異なるメソッドで定義されています。このクエリを<code>set_product</code>というbeforeアクションに抽出しておけば、3つのアクションで同じコードを書かずに済むのでコードが簡潔になります。</p>
1976<p>
1976これは、DRY(Don't Repeat Yourself: 繰り返しを避けよ)原則が実際に機能している良い例です。</p>
1977<div class="code_container">
1978<pre class="highlight">
1979<span class="c1"># app/controllers/products_controller.rb</span>
1980<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
1981  <span class="n">before_action</span> <span class="ss">:set_product</span><span class="p">,</span> <span class="ss">only: </span><span class="sx">%i[ show edit update ]</span>
1982
1983  <span class="k">def</span> <span class="nf">index</span>
1984    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
1985  <span class="k">end</span>
1986
1987  <span class="k">def</span> <span class="nf">show</span>
1988  <span class="k">end</span>
1989
1990  <span class="k">def</span> <span class="nf">new</span>
1991    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span>
1992  <span class="k">end</span>
1993
1994  <span class="k">def</span> <span class="nf">create</span>
1995    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
1996    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">save</span>
1997      <span class="n">redirect_to</span> <span class="vi">@product</span>
1998    <span class="k">else</span>
1999      <span class="n">render</span> <span class="ss">:new</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
2000    <span class="k">end</span>
2001  <span class="k">end</span>
2002
2003  <span class="k">def</span> <span class="nf">edit</span>
2004  <span class="k">end</span>
2005
2006  <span class="k">def</span> <span class="nf">update</span>
2007    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
2008      <span class="n">redirect_to</span> <span class="vi">@product</span>
2009    <span class="k">else</span>
2010      <span class="n">render</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
2011    <span class="k">end</span>
2012  <span class="k">end</span>
2013
2014  <span class="kp">private</span>
2015    <span class="k">def</span> <span class="nf">set_product</span>
2016      <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
2017    <span class="k">end</span>
2018
2019    <span class="k">def</span> <span class="nf">product_params</span>
2020      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span> <span class="p">])</span>
2021    <span class="k">end</span>
2022<span class="k">end</span>
2023</pre>
2024</div>
2025<h4 id="製品を削除する"><a class="anchorlink" href="#%E8%A3%BD%E5%93%81%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B">10.7 製品を削除する</a></h4>
2026<p>実装が必要な最後の機能は、製品の削除です。
2027<code>DELETE /products/:id</code>リクエストを処理するために、<code>ProductsController</code>に<code>destroy</code>アクションを追加しましょう。</p>
2028<p><code>before_action :set_product</code>コールバックに<code>destroy</code>を追加すると、他のアクションと同じ方法で<code>@product</code>インスタンス変数を設定できるようになります。</p>
2029<div class="code_container">
2030<pre class="highlight">
2031<span class="c1"># app/controllers/products_controller.rb</span>
2032<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
2033  <span class="n">before_action</span> <span class="ss">:set_product</span><span class="p">,</span> <span class="ss">only: </span><span class="sx">%i[ show edit update destroy ]</span>
2034
2035  <span class="k">def</span> <span class="nf">index</span>
2036    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">all</span>
2037  <span class="k">end</span>
2038
2039  <span class="k">def</span> <span class="nf">show</span>
2040  <span class="k">end</span>
2041
2042  <span class="k">def</span> <span class="nf">new</span>
2043    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span>
2044  <span class="k">end</span>
2045
2046  <span class="k">def</span> <span class="nf">create</span>
2047    <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
2048    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">save</span>
2049      <span class="n">redirect_to</span> <span class="vi">@product</span>
2050    <span class="k">else</span>
2051      <span class="n">render</span> <span class="ss">:new</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
2052    <span class="k">end</span>
2053  <span class="k">end</span>
2054
2055  <span class="k">def</span> <span class="nf">edit</span>
2056  <span class="k">end</span>
2057
2058  <span class="k">def</span> <span class="nf">update</span>
2059    <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="n">product_params</span><span class="p">)</span>
2060      <span class="n">redirect_to</span> <span class="vi">@product</span>
2061    <span class="k">else</span>
2062      <span class="n">render</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">status: :unprocessable_entity</span>
2063    <span class="k">end</span>
2064  <span class="k">end</span>
2065
2066  <span class="k">def</span> <span class="nf">destroy</span>
2067    <span class="vi">@product</span><span class="p">.</span><span class="nf">destroy</span>
2068    <span class="n">redirect_to</span> <span class="n">products_path</span>
2069  <span class="k">end</span>
2070
2071  <span class="kp">private</span>
2072    <span class="k">def</span> <span class="nf">set_product</span>
2073      <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:id</span><span class="p">])</span>
2074    <span class="k">end</span>
2075
2076    <span class="k">def</span> <span class="nf">product_params</span>
2077      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span> <span class="p">])</span>
2078    <span class="k">end</span>
2079<span class="k">end</span>
2080</pre>
2081</div>
2082<p>製品を削除できるようにするには、<code>app/views/products/show.html.erb</code>で以下のようにDeleteボタンを追加する必要があります。</p>
2083<div class="code_container">
2084<pre class="highlight">
2085<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
2086<span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2087
2088<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Back"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span>
2089<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Edit"</span><span class="p">,</span> <span class="n">edit_product_path</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span>
2090<span class="cp">&lt;%=</span> <span class="n">button_to</span> <span class="s2">"Delete"</span><span class="p">,</span> <span class="vi">@product</span><span class="p">,</span> <span class="ss">method: :delete</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">turbo_confirm: </span><span class="s2">"Are you sure?"</span> <span class="p">}</span> <span class="cp">%&gt;</span>
2091</pre>
2092</div>
2093<p><code>button_to</code>は、Deleteというテキストが表示されたボタンを1つ持つフォームを生成します。このボタンをクリックすると、フォームが<code>/products/:id</code>に<code>DELETE</code>リクエストとして送信され、コントローラの<code>destroy</code>アクションがトリガーされます。</p>
2094<p>なお、<code>turbo_confirm</code>データ属性は、フォームを送信する前にユーザーに確認ダイアログを表示するようにTurboというJavaScriptライブラリに指示します。これについては、後ほど詳しく説明します。</p>
2095<h3 id="認証機能を追加する"><a class="anchorlink" href="#%E8%AA%8D%E8%A8%BC%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">11 認証機能を追加する</a></h3>
2096<p>今のままでは誰でも製品を編集・削除できてしまうので、安全でã
2096¯ã‚りません。製品管理でユーザー認証を必須にすることで、セキュリティを強化しましょう。</p>
2097<p>ここでは、Railsに付属している認証機能ジェネレータが利用できます。この認証機能ジェネレータを使って、<code>User</code>モデルと<code>Session</code>モデル、アプリケーションにログインするために必要なコントローラとビューを作成できます。</p>
2098<p>再びターミナルを開いて、以下の認証機能ジェネレータコマンドを実行します。</p>
2099<div class="code_container">
2100<pre class="highlight">
2101<span class="gp">$</span><span class="w"> </span>bin/rails generate authentication
2102</pre>
2103</div>
2104<p>続いて以下のマイグレーションを実行し、<code>User</code>モデル用の<code>users</code>テーブルと、<code>Session</code>モデル用の<code>sessions</code>テーブルをデータベースに追加します。</p>
2105<div class="code_container">
2106<pre class="highlight">
2107<span class="gp">$</span><span class="w"> </span>bin/rails db:migrate
2108</pre>
2109</div>
2110<p>ユーザーを作成するために、Railsコンソールを開きます。</p>
2111<div class="code_container">
2112<pre class="highlight">
2113<span class="gp">$</span><span class="w"> </span>bin/rails console
2114</pre>
2115</div>
2116<p>Railsコンソールでユーザーを作成するには、<code>User.create!</code>メソッドを実行します。以下の例の通りでなくても構わないので、独自のメールアドレスやパスワードを自由に使えます。</p>
2117<div class="code_container">
2118<pre class="highlight">
2119store(dev)&gt; User.create! email_address: "[email protected]", password: "s3cr3t", password_confirmation: "s3cr3t"
2120</pre>
2121</div>
2122<p>Railsコンソールを終了し、ジェネレータが追加した<code>bcrypt</code> gemを反映するために以下のコマンドを実行してRailsサーバーを再起動します(BCryptは認証用のパスワードを安全にハッシュ化するのに使われます)。</p>
2123<div class="code_container">
2124<pre class="highlight">
2125<span class="gp">$</span><span class="w"> </span>bin/rails server
2126</pre>
2127</div>
2128<p>ブラウザでRailsアプリを開くと、どのページにアクセスしてもユーザー名とパスワードの入力を求められるようになります。</p>
2129<p><code>http://localhost:3000/products/new</code>をブラウザで開いて、<code>User</code>レコードの作成時に入力したメールアドレスとパスワードを入力してみましょう。</p>
2130<p>正しいユーザー名とパスワードを入力するとページにアクセスできるようになります。また、ブラウザは今後のリクエストに備えてこうしたcredential(認証情報)を保存するので、ページビューを移動するたびに入力する必要はありません。</p>
2131<h4 id="ログアウト機能を追加する"><a class="anchorlink" href="#%E3%83%AD%E3%82%B0%E3%82%A2%E3%82%A6%E3%83%88%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">11.1 ログアウト機能を追加する</a></h4>
2132<p>アプリケーションからログアウトするためのボタンを<code>app/views/layouts/application.html.erb</code>レイアウトファイルの冒頭に追加しましょう。このレイアウトには、ヘッダーやフッターなど、すべてのページで使うHTMLを配置します。</p>
2133<p>以下のように、<code>&lt;body&gt;</code>タグ内にホームへのリンクとログアウトボタンを含む小さな<code>&lt;nav&gt;</code>セクションを追加し、Rubyの<code>yield</code>メソッドを<code>&lt;main&gt;</code>タグで囲みます。</p>
2134<div class="code_container">
2135<pre class="highlight">
2136<span class="c">&lt;%# app/views/layouts/application.html.erb %&gt;</span>
2137<span class="cp">&lt;!DOCTYPE html&gt;</span>
2138<span class="nt">&lt;html&gt;</span>
2139  <span class="c">&lt;%# (省略) %&gt;</span>
2140  <span class="nt">&lt;body&gt;</span>
2141    <span class="nt">&lt;nav&gt;</span>
2142      <span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="n">root_path</span> <span class="cp">%&gt;</span>
2143      <span class="cp">&lt;%=</span> <span class="n">button_to</span> <span class="s2">"Log out"</span><span class="p">,</span> <span class="n">session_path</span><span class="p">,</span> <span class="ss">method: :delete</span> <span class="k">if</span> <span class="n">authenticated?</span> <span class="cp">%&gt;</span>
2144    <span class="nt">&lt;/nav&gt;</span>
2145
2146    <span class="nt">&lt;main&gt;</span>
2147      <span class="cp">&lt;%=</span> <span class="k">yield</span> <span class="cp">%&gt;</span>
2148    <span class="nt">&lt;/main&gt;</span>
2149  <span class="nt">&lt;/body&gt;</span>
2150<span class="nt">&lt;/html&gt;</span>
2151</pre>
2152</div>
2153<p>ユーザーが認証済みの場合にのみ、Log outボタンが表示されます。
2154Log outボタンをクリックすると、<code>session_path</code>に<code>DELETE</code>リクエストが送信されて、ユーザーがログアウトします。</p>
2155<h4 id="認証なしのアクセスも許可する"><a class="anchorlink" href="#%E8%AA%8D%E8%A8%BC%E3%81%AA%E3%81%97%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%82%E8%A8%B1%E5%8F%AF%E3%81%99%E3%82%8B">11.2 認証なしのアクセスも許可する</a></h4>
2156<p>ただし、ストアの製品indexページとshowページは誰でもアクセスできるようにしておく必要があります。Railsの認証ジェネレータは、デフォルトではすべてのページへのアクセスを認証済みユーザーのみに制限します。</p>
2157<p>ゲストが製品を表示できるようにするには、コントローラで以下のように認証なしのアクセスを許可します。</p>
2158<div class="code_container">
2159<pre class="highlight">
2160<span class="c1"># app/controllers/products_controller.rb</span>
2161<span class="k">class</span> <span class="nc">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
2162  <span class="n">allow_unauthenticated_access</span> <span class="ss">only: </span><span class="sx">%i[ index show ]</span>
2163  <span class="c1"># (省略)</span>
2164<span class="k">end</span>
2165</pre>
2166</div>
2167<p>ログアウトしてから再び製品のindexページとshowページにアクセスし、認証なしでアクセスできるかどうかを確認してみてください。</p>
2168<h4 id="認証済みユーザーにだけリンクを表示する"><a class="anchorlink" href="#%E8%AA%8D%E8%A8%BC%E6%B8%88%E3%81%BF%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AB%E3%81%A0%E3%81%91%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B">11.3 認証済みユーザーにだけリンクを表示する</a></h4>
2169<p>製品を作成してよいのはログイン済みのユーザーだけにしておきたいので、<code>app/views/products/index.html.erb</code>ビューの<code>link_to</code>行を以下のように変更して、ユーザーが認証済みの場合にのみNew productリンクを表示するようにしましょう。</p>
2170<div class="code_container">
2171<pre class="highlight">
2172<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
2173<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"New product"</span><span class="p">,</span> <span class="n">new_product_path</span> <span class="k">if</span> <span class="n">authenticated?</span> <span class="cp">%&gt;</span>
2174</pre>
2175</div>
2176<p>Log outボタンをクリックすると、indexページのNew productリンクが非表示になります。<code>http://localhost:3000/session/new</code>をブラウザで開いてログインすると、indexページにNew productリンクが表示されます。</p>
2177<p>オプションとして、先ほどの<code>app/views/layouts/application.html.erb</code>レイアウトの<code>&lt;nav&gt;</code>セクションに以下のルーティングへのリンクも追加して、認証されていない場合はLoginリンクを表示するようにしてもよいでしょう。</p>
2178<div class="code_container">
2179<pre class="highlight">
2180<span class="c">&lt;%# app/views/layouts/application.html.erb %&gt;</span>
2181<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Login"</span><span class="p">,</span> <span class="n">new_session_path</span> <span class="k">unless</span> <span class="n">authenticated?</span> <span class="cp">%&gt;</span>
2182</pre>
2183</div>
2184<p>また、<code>app/views/products/show.html.erb</code>ビューのEditリンクとDeleteリンクを以下のように更新して、認証済みの場合にのみEditリンクとDeleteリンクを表示するようにしてもよいでしょう。</p>
2185<div class="code_container">
2186<pre class="highlight">
2187<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
2188<span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2189
2190<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Back"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span>
2191<span class="cp">&lt;%</span> <span class="k">if</span> <span class="n">authenticated?</span> <span class="cp">%&gt;</span>
2192  <span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Edit"</span><span class="p">,</span> <span class="n">edit_product_path</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span>
2193  <span class="cp">&lt;%=</span> <span class="n">button_to</span> <span class="s2">"Delete"</span><span class="p">,</span> <span class="vi">@product</span><span class="p">,</span> <span class="ss">method: :delete</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">turbo_confirm: </span><span class="s2">"Are you sure?"</span> <span class="p">}</span> <span class="cp">%&gt;</span>
2194<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2195</pre>
2196</div>
2197<h3 id="製品をキャッシュに乗せる"><a class="anchorlink" href="#%E8%A3%BD%E5%93%81%E3%82%92%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AB%E4%B9%97%E3%81%9B%E3%82%8B">12 製品をキャッシュに乗せる</a></h3>
2198<p>ページの特定の部分をキャッシュすると、パフォーマンスが向上する場合があります。
2199Railsは、データベース上に構築されるキャッシュストアであるSolid Cacheをデフォルトで組み込むことで、このプロセスを簡素化しています。</p>
2200<p><code>cache</code>メソッドを使うと、HTMLをキャッシュに保存できます。<code>app/views/products/show.html.erb</code>の<code>&lt;h1&gt;</code>見出しを以下のように囲んで、キャッシュを有効にしてみましょう。</p>
2201<div class="code_container">
2202<pre class="highlight">
2203<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
2204<span class="cp">&lt;%</span> <span class="n">
2204cache</span> <span class="vi">@product</span> <span class="k">do</span> <span class="cp">%&gt;</span>
2205  <span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2206<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2207</pre>
2208</div>
2209<p><code>@product</code>を<code>cache</code>メソッドに渡すと、製品に固有のキャッシュキーを生成します。Active Recordオブジェクトにある<code>cache_key</code>メソッドは、<code>"products/1"</code>のような文字列を返します。ビューの<code>cache</code>ヘルパーは、これをテンプレートダイジェストと組み合わせて、このHTMLに固有のキーを作成します。</p>
2210<p>developmentモードでキャッシュを有効にするには、ターミナルで次のコマンドを実行します。</p>
2211<div class="code_container">
2212<pre class="highlight">
2213<span class="gp">$</span><span class="w"> </span>bin/rails dev:cache
2214</pre>
2215</div>
2216<p>製品の<code>show</code>アクション(<code>/products/2</code>など)にアクセスすると、Railsのサーバーログに新しいキャッシュ行が表示されます。</p>
2217<div class="code_container">
2218<pre class="highlight">
2219<span class="go">Read fragment views/products/show:a5a585f985894cd27c8b3d49bb81de3a/products/1-20240918154439539125 (1.6ms)
2220Write fragment views/products/show:a5a585f985894cd27c8b3d49bb81de3a/products/1-20240918154439539125 (4.0ms)
2221</span>
2222</pre>
2223</div>
2224<p>キャッシュを有効にしてからこのページを初めて開くと、Railsはキャッシュキーを生成して、キャッシュストアが存在するかどうかを問い合わせます。これがログの<code>Read fragment</code>行です。</p>
2225<p>これは初めて表示したページビューなのでキャッシュは存在せず、HTMLが生成されてキャッシュに書き込まれます。これはログの<code>Write fragment</code>行として確認できます。</p>
2226<p>ページを更新すると、ログに<code>Write fragment</code>が出力されなくなることがわかります。</p>
2227<div class="code_container">
2228<pre class="highlight">
2229<span class="go">Read fragment views/products/show:a5a585f985894cd27c8b3d49bb81de3a/products/1-20240918154439539125 (1.3ms)
2230</span>
2231</pre>
2232</div>
2233<p>キャッシュエントリは最後のリクエストによって書き込まれたため、Railsは2回目のリクエストでキャッシュエントリを見つけます。また、Railsはレコードが更新されるとキャッシュキーを変更して、古いキャッシュデータが誤ってレンダリングされないようにします。</p>
2234<p>詳しくは、<a href="caching_with_rails.html">Rails のキャッシュ</a>ガイドを参照してください。</p>
2235<h3 id="フィールドをaction-textでリッチテキストにする"><a class="anchorlink" href="#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%82%92action-text%E3%81%A7%E3%83%AA%E3%83%83%E3%83%81%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AB%E3%81%99%E3%82%8B">13 フィールドをAction Textでリッチテキストにする</a></h3>
2236<p>リッチテキスト機能やマルチメディア要素の埋め込み機能は、多くのアプリケーションで求められています。RailsのAction Textを使えば、こうした機能をすぐに使えるようになります。</p>
2237<p>Action Textの利用を開始するには、まずインストーラーを実行します。</p>
2238<div class="code_container">
2239<pre class="highlight">
2240<span class="gp">$</span><span class="w"> </span>bin/rails action_text:install
2241<span class="gp">$</span><span class="w"> </span>bundle <span class="nb">install</span>
2242<span class="gp">$</span><span class="w"> </span>bin/rails db:migrate
2243</pre>
2244</div>
2245<p>すべての新機能が読み込まれていることを確認するために、Railsサーバーを再起動します。</p>
2246<p>次に、リッチテキストのdescription(説明)フィールドを製品に追加してみましょう。</p>
2247<p>まず、<code>Product</code>モデルに以下のコードを追加します。</p>
2248<div class="code_container">
2249<pre class="highlight">
2250<span class="c1"># app/models/product.rb</span>
2251<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2252  <span class="n">has_rich_text</span> <span class="ss">:description</span>
2253  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">
2253presence: </span><span class="kp">true</span>
2254<span class="k">end</span>
2255</pre>
2256</div>
2257<p>これで、<code>app/views/products/_form.html.erb</code>のフォームを以下のように更新して、送信ボタンの上に説明の編集用リッチテキストフィールドを追加できるようになります。</p>
2258<div class="code_container">
2259<pre class="highlight">
2260<span class="c">&lt;%# app/views/products/_form.html.erb %&gt;</span>
2261<span class="cp">&lt;%=</span> <span class="n">form_with</span> <span class="ss">model: </span><span class="n">product</span> <span class="k">do</span> <span class="o">|</span><span class="n">form</span><span class="o">|</span> <span class="cp">%&gt;</span>
2262  <span class="c">&lt;%# (省略) %&gt;</span>
2263
2264  <span class="nt">&lt;div&gt;</span>
2265    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">label</span> <span class="ss">:description</span><span class="p">,</span> <span class="ss">style: </span><span class="s2">"display: block"</span> <span class="cp">%&gt;</span>
2266    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">rich_textarea</span> <span class="ss">:description</span> <span class="cp">%&gt;</span>
2267  <span class="nt">&lt;/div&gt;</span>
2268
2269  <span class="nt">&lt;div&gt;</span>
2270    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">submit</span> <span class="cp">%&gt;</span>
2271  <span class="nt">&lt;/div&gt;</span>
2272<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2273</pre>
2274</div>
2275<p>この新しいパラメータをフォームで送信するには、<code>app/controllers/products_controller.rb</code>コントローラ側で許可する必要があるので、<code>expect</code>で許可するパラメータを以下のように更新して、<code>description</code>を追加します。</p>
2276<div class="code_container">
2277<pre class="highlight">
2278<span class="c1"># app/controllers/products_controller.rb</span>
2279    <span class="c1"># 信頼できるパラメータリストのみを許可する</span>
2280    <span class="k">def</span> <span class="nf">product_params</span>
2281      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:description</span> <span class="p">])</span>
2282    <span class="k">end</span>
2283</pre>
2284</div>
2285<p>さらに、showビュー(<code>app/views/products/show.html.erb</code>)の<code>cache</code>ブロックも以下のように更新して、説明フィールドを表示する必要があります。</p>
2286<div class="code_container">
2287<pre class="highlight">
2288<span class="c">&lt;%# app/views/products/show.html.erb%&gt;</span>
2289<span class="cp">&lt;%</span> <span class="n">cache</span> <span class="vi">@product</span> <span class="k">do</span> <span class="cp">%&gt;</span>
2290  <span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2291  <span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">description</span> <span class="cp">%&gt;</span>
2292<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2293</pre>
2294</div>
2295<p>ビューが変更されると、Railsによって生成されるキャッシュキーも変更されるので、キャッシュがビューテンプレートの最新バージョンと同期した状態が維持されます。</p>
2296<p>それでは、新しい製品を作成して、descriptionフィールドに太字や斜体のテキストを追加してみましょう。製品を作成すると、書式付きテキストがShowページに表示されるようになり、製品を編集すると、このリッチテキストがテキスト領域に保持されるようになります。</p>
2297<p>詳しくは、<a href="action_text_overview.html">Action Textの概要</a>を参照してください。</p>
2298<h3 id="active-storageでファイルアップロード機能を追加する"><a class="anchorlink" href="#active-storage%E3%81%A7%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">14 Active Storageでファイルアップロード機能を追加する</a></h3>
2299<p>Action Textは、ファイルのアップロードを手軽に行えるActive StorageというRailsの別の機能の上に構築されています。</p>
2300<p>製品のEditページを開いて、適当な画像をリッチテキストエディタにドラッグしてから、レコードを更新してみてください。画像がアップロードされて、リッチテキストエディタ内で表示されていることがわかります。素晴らしいですね!</p>
2301<p>Active Storageは、Action Textと別に直接利用することも可能です。</p>
2302<p><code>Product</code>モデルに製品画像を添付する機能も追加してみましょう。</p>
2303<div class="code_container">
2304<pre class="highlight">
2305<span class="c1"># app/models/product.rb</span>
2306<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2307  <span class="n">has_one_attached</span> <span class="ss">:featured_image</span>
2308  <span class="n">has_rich_text</span> <span class="ss">:description</span>
2309  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">
2309presence: </span><span class="kp">true</span>
2310<span class="k">end</span>
2311</pre>
2312</div>
2313<p>続いて、<code>app/views/products/_form.html.erb</code>フォームのSubmitボタンの上に、以下のようにファイルアップロード用フィールドを追加します。</p>
2314<div class="code_container">
2315<pre class="highlight">
2316<span class="c">&lt;%# app/views/products/_form.html.erb %&gt;</span>
2317<span class="cp">&lt;%=</span> <span class="n">form_with</span> <span class="ss">model: </span><span class="n">product</span> <span class="k">do</span> <span class="o">|</span><span class="n">form</span><span class="o">|</span> <span class="cp">%&gt;</span>
2318  <span class="c">&lt;%# (省略) %&gt;</span>
2319
2320  <span class="nt">&lt;div&gt;</span>
2321    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">label</span> <span class="ss">:featured_image</span><span class="p">,</span> <span class="ss">style: </span><span class="s2">"display: block"</span> <span class="cp">%&gt;</span>
2322    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">file_field</span> <span class="ss">:featured_image</span><span class="p">,</span> <span class="ss">accept: </span><span class="s2">"image/*"</span> <span class="cp">%&gt;</span>
2323  <span class="nt">&lt;/div&gt;</span>
2324
2325  <span class="nt">&lt;div&gt;</span>
2326    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">submit</span> <span class="cp">%&gt;</span>
2327  <span class="nt">&lt;/div&gt;</span>
2328<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2329</pre>
2330</div>
2331<p>これまでと同様に、<code>:featured_image</code>も許可済みパラメータとして<code>app/controllers/products_controller.rb</code>に追加します。</p>
2332<div class="code_container">
2333<pre class="highlight">
2334<span class="c1"># app/controllers/products_controller.rb</span>
2335    <span class="c1"># 信頼できるパラメータリストのみを許可する</span>
2336    <span class="k">def</span> <span class="nf">product_params</span>
2337      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:description</span><span class="p">,</span> <span class="ss">:featured_image</span> <span class="p">])</span>
2338    <span class="k">end</span>
2339</pre>
2340</div>
2341<p>最後に、<code>app/views/products/show.html.erb</code>の冒頭に以下のコードを追加して、製品画像を表示できるようにしましょう。</p>
2342<div class="code_container">
2343<pre class="highlight">
2344<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
2345<span class="cp">&lt;%=</span> <span class="n">image_tag</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">featured_image</span> <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">featured_image</span><span class="p">.</span><span class="nf">attached?</span> <span class="cp">%&gt;</span>
2346</pre>
2347</div>
2348<p><code>http://localhost:3000/products/new</code>をブラウザで開いて、Featured imageの「ファイルを選択」ボタンをクリックして製品画像をアップロードしてみると、保存後にshowページに画像が表示されるようになります。</p>
2349<p>詳しくは、<a href="active_storage_overview.html">Active Storage の概要</a>を参照してください。</p>
2350<h3 id="国際化(i18n)"><a class="anchorlink" href="#%E5%9B%BD%E9%9A%9B%E5%8C%96%EF%BC%88i18n%EF%BC%89">15 国際化(I18n)</a></h3>
2351<p>Railsを使えば、アプリを他の言語に翻訳しやすくなります。</p>
2352<p>ビューの<code>translate</code>ヘルパー(短縮形は<code>t</code>)は、名前で訳文を検索して、現在のロケール設定に合うテキストを返します。</p>
2353<p><code>app/views/products/index.html.erb</code>の<code>&lt;h1&gt;</code>見出しタグを以下のように更新して、見出しに訳文が使われるようにしてみましょう。</p>
2354<div class="code_container">
2355<pre class="highlight">
2356<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
2357<span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="n">t</span> <span class="s2">"hello"</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2358</pre>
2359</div>
2360<p>ページを更新すると、見出しテキストが<code>Products</code>から<code>Hello world</code>に変わっていることがわかります。このテキストはどこから来たのでしょうか?</p>
2361<p>デフォルトの言語は英語(<code>en</code>)なので、Railsは<code>config/locales/en.yml</code>(これは<code>rails new</code>で自動作成されます)を探索し、この<code>en</code>ロケールの中にある以下のキー(訳文サンプル)にマッチします。</p>
2362<div class="code_container">
2363<pre class="highlight">
2364# config/locales/en.yml
2365en:
2366  hello: "Hello world"
2367</pre>
2368</div>
2369<p>それでは、日本語用の新しいロケールファイルを作成してみましょう。エディタで<code>config/locales/ja.yml</code>ファイルを作成し、以下の訳文を追加します。</p>
2370<div class="code_container">
2371<pre class="highlight">
2372# config/locales/ja.yml
2373ja:
2374  hello: "こんにちは、世界"
2375</pre>
2376</div>
2377<p>次に、どのロケールを利用するかをRailsに伝える必要があります。</p>
2378<p>最も手軽な方法は、ロケールパラメータをURLから探すことでã
2378™ã€‚<code>app/controllers/application_controller.rb</code>で以下のコードを追加することで、これを実現できます。</p>
2379<div class="code_container">
2380<pre class="highlight">
2381<span class="c1"># app/controllers/application_controller.rb</span>
2382<span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
2383  <span class="c1"># (省略)</span>
2384
2385  <span class="n">around_action</span> <span class="ss">:switch_locale</span>
2386
2387  <span class="k">def</span> <span class="nf">switch_locale</span><span class="p">(</span><span class="o">&amp;</span><span class="n">action</span><span class="p">)</span>
2388    <span class="n">locale</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:locale</span><span class="p">]</span> <span class="o">||</span> <span class="no">I18n</span><span class="p">.</span><span class="nf">default_locale</span>
2389    <span class="no">I18n</span><span class="p">.</span><span class="nf">with_locale</span><span class="p">(</span><span class="n">locale</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">action</span><span class="p">)</span>
2390  <span class="k">end</span>
2391<span class="k">end</span>
2392</pre>
2393</div>
2394<p>このコードはすべてのリクエストで実行され、パラメータ内の<code>locale</code>を探索して、見つからない場合はデフォルトのロケール(通常は<code>en</code>)にフォールバックします。これに基づいてリクエストのロケールを設定し、完了後にリセットします。</p>
2395<ul>
2396<li>
2397<code>http://localhost:3000/products?locale=en</code>をブラウザで開くと、英文が表示されます。</li>
2398<li>
2399<code>http://localhost:3000/products?locale=ja</code>をブラウザで開くと、日本語の訳文が表示されます。</li>
2400<li>ロケールを指定しない<code>http://localhost:3000/products</code>をブラウザで開くと、英語にフォールバックします。</li>
2401</ul>
2402<p>次は、indexページの<code>&lt;h1&gt;</code>
2402見出しのサンプル訳文を、実際の訳文に差し替えてみましょう。<code>app/views/products/index.html.erb</code>の見出しを以下のように更新します</p>
2403<div class="code_container">
2404<pre class="highlight">
2405<span class="c">&lt;%# app/views/products/index.html.erb %&gt;</span>
2406<span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="n">t</span> <span class="s2">".title"</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2407</pre>
2408</div>
2409<div class="info"><p><code>title</code>の直前の<code>.</code>は、ロケールを<strong>相対検索</strong>することを表す指示です。相対検索では、キーにコントローラ名とアクション名が自動的に含まれるため、コントローラ名やアクション名を毎回入力せずに済みます。英語ロケールで<code>.title</code>を指定すると、実際には<code>en.products.index.title</code>が検索されます。</p></div>
2410<p><code>config/locales/en.yml</code>では、以下のように<code>products</code>と<code>index</code>を追加し、その下に<code>title</code>キーを追加することで、<code>コントローラ名.ビュー名.訳文名</code>と一致するようにします。</p>
2411<div class="code_container">
2412<pre class="highlight">
2413# config/locales/en.yml
2414en:
2415  hello: "Hello world"
2416  products:
2417    index:
2418      title: "Products"
2419</pre>
2420</div>
2421<p>日本語のロケールファイルにも、以下のように英語ロケールファイルと対応する形で訳文を追加します。</p>
2422<div class="code_container">
2423<pre class="highlight">
2424# config/locales/ja.yml
2425ja:
2426  hello: "こんにちは、世界"
2427  products:
2428    index:
2429      title: "製品"
2430</pre>
2431</div>
2432<p>これで、<code>http://localhost:3000/?locale=en</code>で英語ロケールを表示すると「Products」が表示され、<code>http://localhost:3000/?locale=ja</code>で日本語ロケールを表示すると「製品」が表示されるようになります。</p>
2433<p>詳しくは<a href="i18n.html">Rails 国際化(I18n)API</a>ガイドを参照してください。</p>
2434<h3 id="action-mailerとメール通知"><a class="anchorlink" href="#action-mailer%E3%81%A8%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%9A%E7%9F%A5">16 Action Mailerとメール通知</a></h3>
2435<p>製品の在庫が復活したときに通知を受け取るための電子メールを登録する機能は、eコマースストアでよく使われる機能です。Railsの基本についてひととおり確認したので、今度はこの機能をストアに追加してみましょう。</p>
2436<h4 id="基本的な在庫トラッキング機能"><a class="anchorlink" href="#%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E5%9C%A8%E5%BA%AB%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0%E6%A9%9F%E8%83%BD">16.1 基本的な在庫トラッキング機能</a></h4>
2437<p>まず、Productモデルに<code>inventory_count</code>(在庫数)を追加して、在庫数をトラッキングできるようにしましょう。以下のコマンドを実行してマイグレーションを生成します。</p>
2438<div class="code_container">
2439<pre class="highlight">
2440<span class="gp">$</span><span class="w"> </span>bin/rails generate migration AddInventoryCountToProducts inventory_count:integer
2441</pre>
2442</div>
2443<div class="note"><p>Railsは、<code>AddInventoryCountToProducts</code>が<code>products</code>テーブルを対象としていると推測します。これは、名前が<code>add_&lt;カラム名&gt;_to_&lt;テーブル名&gt;</code>ルールに一致するためです。このパターンにより、ジェネレータで <code>add_column :products, ...</code>が事前に入力されるので、後はカラムの詳細を入力するだけで済みます。命名規則について詳しくは、<code>bin/rails generate migration --help</code>コマンドを実行してください。</p></div>
2444<p>これでマイグレーションファイルが生成されます。マイグレーションファイルを開いて、<code>inventory_count</code>が決して<code>nil</code>にならないようにするために、デフォルト値として<code>0</code>を追加します。</p>
2445<div class="code_container">
2446<pre class="highlight">
2447<span class="c1"># db/migrate/&lt;タイムスタンプ&gt;_add_inventory_count_to_products.rb</span>
2448<span class="k">class</span> <span class="nc">AddInventoryCountToProducts</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">8.1</span><span class="p">]</span>
2449  <span class="k">def</span> <span class="nf">change</span>
2450    <span class="n">add_column</span> <span class="ss">:products</span><span class="p">,</span> <span class="ss">:inventory_count</span><span class="p">,</span> <span class="ss">:integer</span><span class="p">,</span> <span class="ss">default: </span><span class="mi">0</span>
2451  <span class="k">end</span>
2452<span class="k">end</span>
2453</pre>
2454</div>
2455<p>続いて以下のコマンドでマイグレーションを実行します。</p>
2456<div class="code_container">
2457<pre class="highlight">
2458<span class="gp">$</span><span class="w"> </span>bin/rails db:migrate
2459</pre>
2460</div>
2461<p>製品の<code>app/views/products/_form.html.erb</code>フォームにも、以下のように在庫数のフィールドを追加する必要があります。</p>
2462<div class="code_container">
2463<pre class="highlight">
2464<span class="c">&lt;%# app/views/products/_form.html.erb %&gt;</span>
2465<span class="cp">&lt;%=</span> <span class="n">form_with</span> <span class="ss">model: </span><span class="n">product</span> <span class="k">do</span> <span class="o">|</span><span class="n">form</span><span class="o">|</span> <span class="cp">%&gt;</span>
2466  <span class="c">&lt;%# (省略) %&gt;</span>
2467
2468  <span class="nt">&lt;div&gt;</span>
2469    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">label</span> <span class="ss">:inventory_count</span><span class="p">,</span> <span class="ss">style: </span><span class="s2">"display: block"</span> <span class="cp">%&gt;</span>
2470    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">number_field</span> <span class="ss">:inventory_count</span> <span class="cp">%&gt;</span>
2471  <span class="nt">&lt;/div&gt;</span>
2472
2473  <span class="nt">&lt;div&gt;</span>
2474    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">submit</span> <span class="cp">%&gt;</span>
2475  <span class="nt">&lt;/div&gt;</span>
2476<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2477</pre>
2478</div>
2479<p><code>app/controllers/products_controller.rb</code>コントローラでも、<code>expect</code>で許可するパラメータに<code>:inventory_count</code>を追加する必要があります。</p>
2480<div class="code_container">
2481<pre class="highlight">
2482<span class="c1"># app/controllers/products_controller.rb</span>
2483    <span class="k">def</span> <span class="nf">product_params</span>
2484      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">product: </span><span class="p">[</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:description</span><span class="p">,</span> <span class="ss">:featured_image</span><span class="p">,</span> <span class="ss">:inventory_count</span> <span class="p">])</span>
2485    <span class="k">end</span>
2486</pre>
2487</div>
2488<p>在庫数が決して負の数にならないようにしておくと便利なので、<code>app/models/product.rb</code>モデルにそのためのバリデーションも追加します。</p>
2489<div class="code_container">
2490<pre class="highlight">
2491<span class="c1"># app/models/product.rb</span>
2492<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2493  <span class="n">has_one_attached</span> <span class="ss">:featured_image</span>
2494  <span class="n">has_rich_text</span> <span class="ss">:description</span>
2495
2496  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
2497  <span class="n">validates</span> <span class="ss">:inventory_count</span><span class="p">,</span> <span class="ss">numericality: </span><span class="p">{</span> <span class="ss">greater_than_or_equal_to: </span><span class="mi">0</span> <span class="p">}</span>
2498<span class="k">end</span>
2499</pre>
2500</div>
2501<p>以上の変更により、ストア内の製品の在庫数を更新できるようになりました。</p>
2502<h4 id="通知の購読者を製品に追加する"><a class="anchorlink" href="#%E9%80%9A%E7%9F%A5%E3%81%AE%E8%B3%BC%E8%AA%AD%E8%80%85%E3%82%92%E8%A3%BD%E5%93%81%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">16.2 通知の購読者を製品に追加する</a></h4>
2503<p>商品の在庫が復活したことをユーザーに通知するには、在庫情報の購読者(subscriber)をトラッキングする機能が必要です。</p>
2504<p>購読希望者のメールアドレスを保存して個別の商品に関連付けるための<code>Subscriber</code>というモデルを生成しましょう。</p>
2505<div class="note"><p>ここでは<code>email</code>フィールドに型を指定していませんが、Railsのマイグレーションで型が指定されていない場合、自動的に<code>string</code>型がデフォルトになることを利用しています。</p></div>
2506<div class="code_container">
2507<pre class="highlight">
2508<span class="gp">$</span><span class="w"> </span>bin/rails generate model Subscriber product:belongs_to email
2509</pre>
2510</div>
2511<p>上のコマンドで<code>product:belongs_to</code>オプションを指定したことで、購読者と製品が<strong>1対多</strong>リレーションを持つことを表す<code>belongs_to :product</code>という宣言が<code>Subscriber</code>モデルに含まれるようになります。つまり、<code>Subscriber</code>モデルのインスタンスは1つの<code>Product</code>インスタンスに「属する(belongs to)」ということです。</p>
2512<p>次に、生成されたマイグレーションファイル(<code>db/migrate/&lt;タイムスタンプ&gt;_create_subscribers.rb</code>)をエディタで開きます。</p>
2513<div class="code_container">
2514<pre class="highlight">
2515<span class="c1"># db/migrate/&lt;タイムスタンプ&gt;_create_subscribers.rb</span>
2516<span class="k">class</span> <span class="nc">CreateSubscribers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="p">[</span><span class="mf">8.1</span><span class="p">]</span>
2517  <span class="k">def</span> <span class="nf">change</span>
2518    <span class="n">create_table</span> <span class="ss">:subscribers</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
2519      <span class="n">t</span><span class="p">.</span><span class="nf">belongs_to</span> <span class="ss">:product</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">
2519false</span><span class="p">,</span> <span class="ss">foreign_key: </span><span class="kp">true</span>
2520      <span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:email</span>
2521      <span class="n">t</span><span class="p">.</span><span class="nf">timestamps</span>
2522    <span class="k">end</span>
2523  <span class="k">end</span>
2524<span class="k">end</span>
2525</pre>
2526</div>
2527<p>このマイグレーションは、<code>Product</code>のマイグレーションと非常に似ています。主な新しい点は<code>belongs_to</code>が含まれていることで、これにより<code>product_id</code>という外部キーカラムが追加されます。</p>
2528<p>続いて新しいマイグレーションを実行します。</p>
2529<div class="code_container">
2530<pre class="highlight">
2531<span class="gp">$</span><span class="w"> </span>bin/rails db:migrate
2532</pre>
2533</div>
2534<p>ただし、1つの製品に購読者が複数存在する可能性もあるため、<code>Product</code>モデルにも<code>has_many :subscribers, dependent: :destroy</code>を手動で追加することで、2つのモデル同士の関連付けの残りの部分も指定します。これにより、2つのデータベーステーブル間のクエリをjoin(結合)する方法が Railsで認識されます。</p>
2535<div class="code_container">
2536<pre class="highlight">
2537<span class="c1"># app/models/product.rb</span>
2538<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2539  <span class="n">has_many</span> <span class="ss">:subscribers</span><span class="p">,</span> <span class="ss">dependent: :destroy</span>
2540  <span class="n">has_one_attached</span> <span class="ss">:featured_image</span>
2541  <span class="n">has_rich_text</span> <span class="ss">:description</span>
2542
2543  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
2544  <span class="n">validates</span> <span class="ss">:inventory_count</span><span class="p">,</span> <span class="ss">numericality: </span><span class="p">{</span> <span class="ss">greater_than_or_equal_to: </span><span class="mi">0</span> <span class="p">}</span>
2545<span class="k">end</span>
2546</pre>
2547</div>
2548<p>次は、購読者を作成するためのコントローラが必要です。<code>app/controllers/subscribers_controller.rb</code>コントローラを以下の内容で作成しましょう。</p>
2549<div class="code_container">
2550<pre class="highlight">
2551<span class="c1"># app/controllers/subscribers_controller.rb</span>
2552<span class="k">class</span> <span class="nc">SubscribersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
2553  <span class="n">allow_unauthenticated_access</span>
2554  <span class="n">before_action</span> <span class="ss">:set_product</span>
2555
2556  <span class="k">def</span> <span class="nf">create</span>
2557    <span class="vi">@product</span><span class="p">.</span><span class="nf">subscribers</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="n">subscriber_params</span><span class="p">).</span><span class="nf">first_or_create</span>
2558    <span class="n">redirect_to</span> <span class="vi">@product</span><span class="p">,</span> <span class="ss">notice: </span><span class="s2">"You are now subscribed."</span>
2559  <span class="k">end</span>
2560
2561  <span class="kp">private</span>
2562
2563    <span class="k">def</span> <span class="nf">set_product</span>
2564      <span class="vi">@product</span> <span class="o">=</span> <span class="no">Product</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="ss">:product_id</span><span class="p">])</span>
2565    <span class="k">end</span>
2566
2567    <span class="k">def</span> <span class="nf">subscriber_params</span>
2568      <span class="n">params</span><span class="p">.</span><span class="nf">expect</span><span class="p">(</span><span class="ss">
2568subscriber: </span><span class="p">[</span> <span class="ss">:email</span> <span class="p">])</span>
2569    <span class="k">end</span>
2570<span class="k">end</span>
2571</pre>
2572</div>
2573<p><code>redirect_to</code>メソッドの<code>notice:</code>引数には、購読が完了したことを知らせるflashメッセージを指定しています。</p>
2574<p><a href="https://api.rubyonrails.org/v8.1/classes/ActionDispatch/Flash.html">flash</a>は、コントローラのアクション間で一時的なデータを渡す手段を提供します。flashに設定したデータは次のアクションで有効になり、その後消去されます。flashは通常、コントローラのアクションでメッセージ(通知やアラートなど)を設定し、その後、ユーザーにメッセージを表示するアクションにリダイレクトするために使われます。</p>
2575<p>このflashメッセージを表示するには、<code>app/views/layouts/application.html.erb</code>レイアウトの<code>&lt;body&gt;</code>タグで以下のように通知を追加します。</p>
2576<div class="code_container">
2577<pre class="highlight">
2578<span class="c">&lt;%# app/views/layouts/application.html.erb %&gt;</span>
2579<span class="nt">&lt;html&gt;</span>
2580  <span class="c">&lt;%# (省略) %&gt;</span>
2581  <span class="nt">&lt;body&gt;</span>
2582    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"notice"</span><span class="nt">&gt;</span><span class="cp">&lt;%=</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:notice</span><span class="p">]</span> <span class="cp">%&gt;</span><span class="nt">&lt;/div&gt;</span>
2583    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"alert"</span><span class="nt">&gt;</span><span class="cp">&lt;%=</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:alert</span><span class="p">]</span> <span class="cp">%&gt;</span><span class="nt">&lt;/div&gt;</span>
2584    <span class="c">&lt;%# (省略) %&gt;</span>
2585  <span class="nt">&lt;/body&gt;</span>
2586<span class="nt">&lt;/html&gt;</span>
2587</pre>
2588</div>
2589<p>flashについて詳しくは、<a href="action_controller_overview.html#flash">Action Controllerガイド</a>を参照してください。</p>
2590<p>ユーザーが特定の製品を指定して通知を購読できるようにするため、<code>Subscriber</code>がどの<code>Product</code>に属しているかをネステッドルーティングで指定しましょう。</p>
2591<p><code>config/routes.rb</code>ファイルの<code>resources :products</code>を以下のように変更します。</p>
2592<div class="code_container">
2593<pre class="highlight">
2594<span class="c1"># config/routes.rb</span>
2595<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
2596  <span class="c1"># (省略)</span>
2597  <span class="n">resources</span> <span class="ss">:products</span> <span class="k">do</span>
2598    <span class="n">resources</span> <span class="ss">:subscribers</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span> <span class="ss">:create</span> <span class="p">]</span>
2599  <span class="k">end</span>
2600<span class="k">end</span>
2601</pre>
2602</div>
2603<p>製品のShowページに在庫数を表示して、在庫があるかどうかをチェックできるようにしましょう。在庫がない場合は、在庫切れのメッセージと購読用のフォームを表示して、在庫が復活したときにユーザーが通知をメールで受け取れるようにします。</p>
2604<p>在庫表示用の<code>app/views/products/_inventory.html.erb</code>パーシャルを以下の内容で作成します。</p>
2605<div class="code_container">
2606<pre class="highlight">
2607<span class="c">&lt;%# app/views/products/_inventory.html.erb %&gt;</span>
2608<span class="cp">&lt;%</span> <span class="k">if</span> <span class="n">product</span><span class="p">.</span><span class="nf">inventory_count</span><span class="p">.</span><span class="nf">positive?</span> <span class="cp">%&gt;</span>
2609  <span class="nt">&lt;p&gt;</span><span class="cp">&lt;%=</span> <span class="n">product</span><span class="p">.</span><span class="nf">inventory_count</span> <span class="cp">%&gt;</span> in stock<span class="nt">&lt;/p&gt;</span>
2610<span class="cp">&lt;%</span> <span class="k">else</span> <span class="cp">%&gt;</span>
2611  <span class="nt">&lt;p&gt;</span>Out of stock<span class="nt">&lt;/p&gt;</span>
2612  <span class="nt">&lt;p&gt;</span>Email me when available.<span class="nt">&lt;/p&gt;</span>
2613
2614  <span class="cp">&lt;%=</span> <span class="n">form_with</span> <span class="ss">model: </span><span class="p">[</span><span class="n">product</span><span class="p">,</span> <span class="no">Subscriber</span><span class="p">.</span><span class="nf">new</span><span class="p">]</span> <span class="k">do</span> <span class="o">|</span><span class="n">form</span><span class="o">|</span> <span class="cp">%&gt;</span>
2615    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">email_field</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">placeholder: </span><span class="s2">"[email protected]"</span><span class="p">,</span> <span class="ss">required: </span><span class="kp">true</span> <span class="cp">%&gt;</span>
2616    <span class="cp">&lt;%=</span> <span class="n">form</span><span class="p">.</span><span class="nf">submit</span> <span class="s2">
2616"Submit"</span> <span class="cp">%&gt;</span>
2617  <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2618<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2619</pre>
2620</div>
2621<p>次に、<code>app/views/products/show.html.erb</code>の<code>cache</code>ブロックの下に以下のコードを追加して、上のパーシャルをレンダリングします。</p>
2622<div class="code_container">
2623<pre class="highlight">
2624<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
2625<span class="cp">&lt;%=</span> <span class="n">render</span> <span class="s2">"inventory"</span><span class="p">,</span> <span class="ss">product: </span><span class="vi">@product</span> <span class="cp">%&gt;</span>
2626</pre>
2627</div>
2628<h4 id="「在庫あり」メールによる通知機能を追加する"><a class="anchorlink" href="#%E3%80%8C%E5%9C%A8%E5%BA%AB%E3%81%82%E3%82%8A%E3%80%8D%E3%83%A1%E3%83%BC%E3%83%AB%E3%81%AB%E3%82%88%E3%82%8B%E9%80%9A%E7%9F%A5%E6%A9%9F%E8%83%BD%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">16.3 「在庫あり」メールによる通知機能を追加する</a></h4>
2629<p>商品の在庫が復活したときに購読者に通知する機能には、Railsのメール送信機能である<a href="action_mailer_basics.html">Action Mailer</a>を使うことにします。</p>
2630<p>以下のコマンドを実行することでメーラーを生成できます。</p>
2631<div class="code_container">
2632<pre class="highlight">
2633<span class="gp">$</span><span class="w"> </span>bin/rails g mailer Product in_stock
2634</pre>
2635</div>
2636<p>これにより、<code>app/mailers/product_mailer.rb</code>に<code>in_stock</code>メソッドを持つクラスが生成されます。</p>
2637<p>購読者のメールアドレスにメールを送信できるようにするには、この<code>in_stock</code>メソッドを以下のように更新します。</p>
2638<div class="code_container">
2639<pre class="highlight">
2640<span class="c1"># app/mailers/product_mailer.rb</span>
2641<span class="k">class</span> <span class="nc">ProductMailer</span> <span class="o">&lt;</span> <span class="no">ApplicationMailer</span>
2642  <span class="c1"># Subject can be set in your I18n file at config/locales/en.yml</span>
2643  <span class="c1"># with the following lookup:</span>
2644  <span class="c1">#</span>
2645  <span class="c1">#   en.product_mailer.in_stock.subject</span>
2646  <span class="c1">#</span>
2647  <span class="k">def</span> <span class="nf">in_stock</span>
2648    <span class="vi">@product</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="ss">:product</span><span class="p">]</span>
2649    <span class="n">mail</span> <span class="ss">to: </span><span class="n">params</span><span class="p">[</span><span class="ss">:subscriber</span><span class="p">].</span><span class="nf">email</span>
2650  <span class="k">end</span>
2651<span class="k">end</span>
2652</pre>
2653</div>
2654<p>Action Mailerのジェネレータを実行すると、<code>app/views/</code>フォルダの下にもメールテンプレートが2つ生成されます。1つはHTMLメールの送信用、もう1つはテキストメールの送信用です。
2655これらのテンプレートを更新することで、「在庫あり」メッセージや製品へのリンクをメールに含められるようになります。</p>
2656<p><code>app/views/product_mailer/in_stock.html.erb</code>を以下のように変更します。</p>
2657<div class="code_container">
2658<pre class="highlight">
2659<span class="c">&lt;%# app/views/product_mailer/in_stock.html.erb %&gt;</span>
2660<span class="nt">&lt;h1&gt;</span>Good news!<span class="nt">&lt;/h1&gt;</span>
2661
2662<span class="nt">&lt;p&gt;</span><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span><span class="p">,</span> <span class="n">product_url</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span> is back in stock.<span class="nt">&lt;/p&gt;</span>
2663</pre>
2664</div>
2665<p><code>app/views/product_mailer/in_stock.text.erb</code>を以下のように変更します。</p>
2666<div class="code_container">
2667<pre class="highlight">
2668Good news!
2669
2670<span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span> is back in stock.
2671<span class="cp">&lt;%=</span> <span class="n">product_url</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span>
2672</pre>
2673</div>
2674<p>メールクライアントでユーザーがリンクをクリックしたときにブラウザで開くようにするには、相対パスではなく完全なURLが必要なので、メーラーでは<code>product_path</code>ではなく<code>product_url</code>を使います。</p>
2675<p><code>bin/rails console</code>でRailsコンソールを開いて、以下のように送信先の製品とサブスクライバを読み込むことで、メールをテストできます。</p>
2676<div class="code_container">
2677<pre class="highlight">
2678store(dev)&gt; product = Product.first
2679store(dev)&gt; subscriber = product.subscribers.find_or_create_by(email: "[email protected]")
2680store(dev)&gt; ProductMailer.with(product: product, subscriber: subscriber).in_stock.deliver_later
2681</pre>
2682</div>
2683<p>メールが送信されたことがログで以下のように確認できます。</p>
2684<div class="code_container">
2685<pre class="highlight">
2686ProductMailer#in_stock: processed outbound mail in 63.0ms
2687Delivered mail [email protected] (33.1ms)
2688Date: Fri, 26 Jul 2024 08:50:39 -0500
2689From: [email protected]
2690To: [email protected]
2691Message-ID: &lt;[email protected]&gt;
2692Subject: In stock
2693Mime-Version: 1.0
2694Content-Type: multipart/alternative;
2695 boundary="--==_mimepart_66a3a9afd235e_108b04a4c4136f";
2696 charset=UTF-8
2697Content-Transfer-Encoding: 7bit
2698
2699
2700----==_mimepart_66a3a9afd235e_108b04a4c4136f
2701Content-Type: text/plain;
2702 charset=UTF-8
2703Content-Transfer-Encoding: 7bit
2704
2705Good news!
2706
2707T-Shirt is back in stock.
2708http://localhost:3000/products/1
2709
2710
2711----==_mimepart_66a3a9afd235e_108b04a4c4136f
2712Content-Type: text/html;
2713 charset=UTF-8
2714Content-Transfer-Encoding: 7bit
2715
2716&lt;!-- BEGIN app/views/layouts/mailer.html.erb --&gt;&lt;!DOCTYPE html&gt;
2717&lt;html&gt;
2718  &lt;head&gt;
2719    &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;
2720    &lt;style&gt;
2721      /* Email styles need to be inline */
2722    &lt;/style&gt;
2723  &lt;/head&gt;
2724
2725  &lt;body&gt;
2726    &lt;!-- BEGIN app/views/product_mailer/in_stock.html.erb --&gt;&lt;h1&gt;Good news!&lt;/h1&gt;
2727
2728&lt;p&gt;&lt;a href="http://localhost:3000/products/1"&gt;T-Shirt&lt;/a&gt; is back in stock.&lt;/p&gt;
2729&lt;!-- END app/views/product_mailer/in_stock.html.erb --&gt;
2730  &lt;/body&gt;
2731&lt;/html&gt;
2732&lt;!-- END app/views/layouts/mailer.html.erb --&gt;
2733----==_mimepart_66a3a9afd235e_108b04a4c4136f--
2734
2735Performed ActionMailer::MailDeliveryJob (Job ID: 5e2bd5f2-f54f-4088-ace3-3f6eb15aaf46) from Async(default) in 111.34ms
2736</pre>
2737</div>
2738<p>これらのメールをトリガーするには、在庫数が0から正の数に変わるたびにメールをé€
2738ä¿¡ã™ã‚‹ã‚³ãƒ¼ãƒ«ãƒãƒƒã‚¯ã‚’<code>Product</code>モデルに追加します。</p>
2739<div class="code_container">
2740<pre class="highlight">
2741<span class="c1"># app/models/product.rb</span>
2742<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2743  <span class="n">has_many</span> <span class="ss">:subscribers</span><span class="p">,</span> <span class="ss">dependent: :destroy</span>
2744  <span class="n">has_one_attached</span> <span class="ss">:featured_image</span>
2745  <span class="n">has_rich_text</span> <span class="ss">:description</span>
2746
2747  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
2748  <span class="n">validates</span> <span class="ss">:inventory_count</span><span class="p">,</span> <span class="ss">numericality: </span><span class="p">{</span> <span class="ss">greater_than_or_equal_to: </span><span class="mi">0</span> <span class="p">}</span>
2749
2750  <span class="n">after_update_commit</span> <span class="ss">:notify_subscribers</span><span class="p">,</span> <span class="ss">if: :back_in_stock?</span>
2751
2752  <span class="k">def</span> <span class="nf">back_in_stock?</span>
2753    <span class="n">inventory_count_previously_was</span><span class="p">.</span><span class="nf">zero?</span> <span class="o">&amp;&amp;</span> <span class="n">inventory_count</span><span class="p">.</span><span class="nf">positive?</span>
2754  <span class="k">end</span>
2755
2756  <span class="k">def</span> <span class="nf">notify_subscribers</span>
2757    <span class="n">subscribers</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">subscriber</span><span class="o">|</span>
2758      <span class="no">ProductMailer</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="ss">product: </span><span class="nb">self</span><span class="p">,</span> <span class="ss">subscriber: </span><span class="n">subscriber</span><span class="p">).</span><span class="nf">in_stock</span><span class="p">.</span><span class="nf">deliver_later</span>
2759    <span class="k">end</span>
2760  <span class="k">end</span>
2761<span class="k">end</span>
2762</pre>
2763</div>
2764<p>上で追加した<a href="https://railsguides.jp/active_record_callbacks.html#after-commit%E3%82%B3%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E3%81%AE%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9"><code>after_update_commit</code></a>は、変更がデータベースに保存された直後に実行されるActive Recordコールバックです。
2765<code>if: :back_in_stock?</code>は、<code>back_in_stock?</code>メソッドが<code>true</code>を返す場合にのみコールバックを実行するように指示します。</p>
2766<p>Active Recordで属性の変更をトラッキングできるようにするために、<code>back_in_stock?</code>メソッドでは<code>inventory_count</code>の直前の値を確認するのに<code>inventory_count_previously_was</code>(<code>inventory_count</code>属性から自動生成された<a href="https://api.rubyonrails.org/v8.1/classes/ActiveModel/Dirty.html#method-i-attribute_previously_was"><code>属性名_previously_was</code></a>メソッド)を使っています。続いて、その値を現在の在庫数と比較して、製品の在庫が復活したかどうかを判断します。</p>
2767<p><code>notify_subscribers</code>は、特定の製品のすべての購読者のリストを得るためにActive Record関連付けを利用して<code>subscribers</code>テーブルを照会してから、個別の購読者に送信する<code>in_stock</code>メールをキューに登録します。</p>
2768<h4 id="共通コードをconcernに抽出する"><a class="anchorlink" href="#%E5%85%B1%E9%80%9A%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92concern%E3%81%AB%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B">16.4 共通コードをconcernに抽出する</a></h4>
2769<p>この<code>Product</code>モデルには、通知を処理するためのコードがかなり多く含まã
2769‚Œã¦ã„ます。Railsでは、これを<a href="https://api.rubyonrails.org/v8.1/classes/ActiveSupport/Concern.html"><code>ActiveSupport::Concern</code></a>に切り出すことでコードを整理できます。concernは通常のRubyモジュールですが、使いやすくするためのシンタックスシュガーが追加されています。</p>
2770<p>最初に、<code>Notifications</code>モジュールを作成してみましょう。</p>
2771<p><code>app/models/product/</code>ディレクトリを作成してから、<code>app/models/product/notifications.rb</code>ファイルを以下の内容で作成します。</p>
2772<div class="code_container">
2773<pre class="highlight">
2774<span class="c1"># app/models/product/notifications.rb</span>
2775<span class="k">module</span> <span class="nn">Product::Notifications</span>
2776  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>
2777
2778  <span class="n">included</span> <span class="k">do</span>
2779    <span class="n">has_many</span> <span class="ss">:subscribers</span><span class="p">,</span> <span class="ss">dependent: :destroy</span>
2780    <span class="n">after_update_commit</span> <span class="ss">:notify_subscribers</span><span class="p">,</span> <span class="ss">if: :back_in_stock?</span>
2781  <span class="k">end</span>
2782
2783  <span class="k">def</span> <span class="nf">back_in_stock?</span>
2784    <span class="n">inventory_count_previously_was</span><span class="p">.</span><span class="nf">zero?</span> <span class="o">&amp;&amp;</span> <span class="n">inventory_count</span><span class="p">.</span><span class="nf">positive?</span>
2785  <span class="k">end</span>
2786
2787  <span class="k">def</span> <span class="nf">notify_subscribers</span>
2788    <span class="n">subscribers</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">subscriber</span><span class="o">|</span>
2789      <span class="no">ProductMailer</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="ss">product: </span><span class="nb">self</span><span class="p">,</span> <span class="ss">subscriber: </span><span class="n">subscriber</span><span class="p">).</span><span class="nf">in_stock</span><span class="p">.</span><span class="nf">deliver_later</span>
2790    <span class="k">end</span>
2791  <span class="k">end</span>
2792<span class="k">end</span>
2793</pre>
2794</div>
2795<p>このモジュールがクラスに<code>include</code>されると、<a href="https://railsguides.jp/association_basics.html#entryable%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B"><code>included</code></a>ブロック内に記述したコードは、最初からそのクラスの一部であるかのように実行されます。また、このモジュール内で定義したメソッドは、そのクラスのオブジェクト(インスタンス)で呼び出せる通常のインスタンスメソッドになります。</p>
2796<p>通知をトリガーするコードを<code>Notification</code>モジュールに切り出したので、<code>app/models/product.rb</code>モデルで以下のように<code>Notifications</code>モジュールを<code>include</code>してコードを簡潔にできます。</p>
2797<div class="code_container">
2798<pre class="highlight">
2799<span class="c1"># app/models/product.rb</span>
2800<span class="k">class</span> <span class="nc">Product</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2801  <span class="kp">include</span> <span class="no">Notifications</span>
2802
2803  <span class="n">has_one_attached</span> <span class="ss">:featured_image</span>
2804  <span class="n">has_rich_text</span> <span class="ss">:description</span>
2805
2806  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
2807  <span class="n">validates</span> <span class="ss">:inventory_count</span><span class="p">,</span> <span class="ss">numericality: </span><span class="p">{</span> <span class="ss">greater_than_or_equal_to: </span><span class="mi">0</span> <span class="p">}</span>
2808<span class="k">end</span>
2809</pre>
2810</div>
2811<p>concernは、Railsアプリケーションの機能を整理するための優れた手法のひとつです。製品に機能を繰り返し追加していると、やがてクラスが乱雑になります。代わりに、concernを使って各機能を<code>Product::Notifications</code>などの自己完結型モジュールに抽出できます。このモジュールには、サブスクライバの処理と通知の送信方法に関する機能がすべて含まれています。</p>
2812<p>コードをconcernに抽出すると、機能の再利用性も高まります。たとえば、サブスクライバ通知も必要とする新しいモデルを手軽に導入できるようになります。このモジュールを利用するè
2812¤‡æ•°ã®ãƒ¢ãƒ‡ãƒ«ã§åŒã˜æ©Ÿèƒ½ã‚’提供できます。</p>
2813<h4 id="通知購読の解除リンクをメールに追加する"><a class="anchorlink" href="#%E9%80%9A%E7%9F%A5%E8%B3%BC%E8%AA%AD%E3%81%AE%E8%A7%A3%E9%99%A4%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E3%83%A1%E3%83%BC%E3%83%AB%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">16.5 通知購読の解除リンクをメールに追加する</a></h4>
2814<p>通知を購読したユーザーが購読を解除できるようにしておきたいので、次は購読の解除機能を構築することにしましょう。</p>
2815<p>最初に、<code>config/routes.rb</code>のルーティングに購読解除用のルーティングを追加する必要があります。これは解除用メールに含めるURLとして使われます。</p>
2816<div class="code_container">
2817<pre class="highlight">
2818<span class="c1"># config/routes.rb</span>
2819<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
2820  <span class="c1"># ...</span>
2821  <span class="n">resources</span> <span class="ss">:products</span> <span class="k">do</span>
2822    <span class="n">resources</span> <span class="ss">:subscribers</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span> <span class="ss">:create</span> <span class="p">]</span>
2823  <span class="k">end</span>
2824  <span class="n">resource</span> <span class="ss">:unsubscribe</span><span class="p">,</span> <span class="ss">only: </span><span class="p">[</span> <span class="ss">:show</span> <span class="p">]</span>
2825</pre>
2826</div>
2827<p>購読解除用のルーティングは最上位レベルに追加します。<code>/unsubscribe?token=xyz</code>のようなルーティングを処理するために、単数形の<code>resource</code>メソッドを使っているにご注意ください。</p>
2828<p>Active Recordには、さまざまな目的でデータベースレコードを検索するための一意のトークンを生成できる<a href="https://api.rubyonrails.org/v8.1/classes/ActiveRecord/TokenFor/ClassMethods.html#method-i-generates_token_for"><code>generates_token_for</code></a>という機能があります。これを使って、電子メールの登録解除用URLに含める一意の登録解除用トークンを<code>Subscriber</code>モデルで生成できます。</p>
2829<div class="code_container">
2830<pre class="highlight">
2831<span class="c1"># app/models/subscriber.rb</span>
2832<span class="k">class</span> <span class="nc">Subscriber</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
2833  <span class="n">belongs_to</span> <span class="ss">:product</span>
2834  <span class="n">generates_token_for</span> <span class="ss">:unsubscribe</span>
2835<span class="k">end</span>
2836</pre>
2837</div>
2838<p>コントローラは最初に、URLに含まれるトークンを用いて<code>Subscriber</code>
2838のレコードを検索し、対応する購読者が見つかったら、レコードを破棄(<code>destroy</code>)してホームページにリダイレクトします。</p>
2839<p><code>app/controllers/unsubscribes_controller.rb</code>を以下の内容で作成します。</p>
2840<div class="code_container">
2841<pre class="highlight">
2842<span class="c1"># app/controllers/unsubscribes_controller.rb</span>
2843<span class="k">class</span> <span class="nc">UnsubscribesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
2844  <span class="n">allow_unauthenticated_access</span>
2845  <span class="n">before_action</span> <span class="ss">:set_subscriber</span>
2846
2847  <span class="k">def</span> <span class="nf">show</span>
2848    <span class="vi">@subscriber</span><span class="o">&amp;</span><span class="p">.</span><span class="nf">destroy</span>
2849    <span class="n">redirect_to</span> <span class="n">root_path</span><span class="p">,</span> <span class="ss">notice: </span><span class="s2">"Unsubscribed successfully."</span>
2850  <span class="k">end</span>
2851
2852  <span class="kp">private</span>
2853
2854    <span class="k">def</span> <span class="nf">set_subscriber</span>
2855      <span class="vi">@subscriber</span> <span class="o">=</span> <span class="no">Subscriber</span><span class="p">.</span><span class="nf">find_by_token_for</span><span class="p">(</span><span class="ss">:unsubscribe</span><span class="p">,</span> <span class="n">params</span><span class="p">[</span><span class="ss">:token</span><span class="p">])</span>
2856    <span class="k">end</span>
2857<span class="k">end</span>
2858</pre>
2859</div>
2860<p>仕上げに、登録解除用リンクをメールテンプレートに追加しましょう。</p>
2861<p><code>app/views/product_mailer/in_stock.html.erb</code>で以下のように<code>link_to</code>を追加します。</p>
2862<div class="code_container">
2863<pre class="highlight">
2864<span class="c">&lt;%# app/views/product_mailer/in_stock.html.erb %&gt;</span>
2865<span class="nt">&lt;h1&gt;</span>Good news!<span class="nt">&lt;/h1&gt;</span>
2866
2867<span class="nt">&lt;p&gt;</span><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span><span class="p">,</span> <span class="n">product_url</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span> is back in stock.<span class="nt">&lt;/p&gt;</span>
2868
2869<span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Unsubscribe"</span><span class="p">,</span> <span class="n">unsubscribe_url</span><span class="p">(</span><span class="ss">token: </span><span class="n">params</span><span class="p">[</span><span class="ss">:subscriber</span><span class="p">].</span><span class="nf">generate_token_for</span><span class="p">(</span><span class="ss">:unsubscribe</span><span class="p">))</span> <span class="cp">%&gt;</span>
2870</pre>
2871</div>
2872<p><code>app/views/product_mailer/in_stock.text.erb</code>にも以下のように平文でURLを追加します。</p>
2873<div class="code_container">
2874<pre class="highlight">
2875<span class="c">&lt;%# app/views/product_mailer/in_stock.text.erb %&gt;</span>
2876Good news!
2877
2878<span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span> is back in stock.
2879<span class="cp">&lt;%=</span> <span class="n">product_url</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span>
2880
2881Unsubscribe: <span class="cp">&lt;%=</span> <span class="n">unsubscribe_url</span><span class="p">(</span><span class="ss">token: </span><span class="n">params</span><span class="p">[</span><span class="ss">:subscriber</span><span class="p">].</span><span class="nf">generate_token_for</span><span class="p">(</span><span class="ss">:unsubscribe</span><span class="p">))</span> <span class="cp">%&gt;</span>
2882</pre>
2883</div>
2884<p>ユーザーがこの登録解除用リンクをクリックすると、データベースから<code>Subscriber</code>のレコードが削除されます。追加したコントローラは、無効なトークンや期限切れのトークンをエラーなしで安全に処理します。</p>
2885<p>Railsコンソールを起動してメールをもう1件送信し、ログに表示される登録解除リンク(<code>Unsubscribe:</code>で始まります)のURLを見つけてブラウザで開き、正常に登録解除できることをテストしてみてください。成功するとstoreのトップページが開いて「Unsubscribed successfully.」と表示されます。</p>
2886<h3 id="cssとjavascriptを追加する"><a class="anchorlink" href="#css%E3%81%A8javascript%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">17 CSSとJavaScriptを追加する</a></h3>
2887<p>CSSやJavaScriptはWebアプリケーション構築の中心となるため、Railsでの利用方法を学びましょう。</p>
2888<h4 id="propshaft"><a class="anchorlink" href="#propshaft">17.1 Propshaft</a></h4>
2889<p>Railsで、「CSS」「JavaScript」「画像」などのアセットを取得してブラウザに配信する<strong>アセットパイプライン</strong>(asset pipeline)には、<a href="https://github.com/rails/propshaft">Propshaft</a>が使われています。</p>
2890<p>production環境のPropshaftは、アセットの各バージョンをトラッキングしてキャッシュすることで、ページを高速化します。アセットパイプラインの仕組ã
2890¿ã«ã¤ã„て詳しくは、<a href="asset_pipeline.html">アセット パイプライン ガイド</a>を参照してください。</p>
2891<div class="note"><p>訳注: Rails 8.0からは、従来の<a href="https://github.com/rails/sprockets">Sprockets</a>に代わってPropshaftがデフォルトのアセットパイプラインになりました。</p></div>
2892<p><code>app/assets/stylesheets/application.css</code>ファイルを以下のように更新して、フォントをsans-serifに変更してみましょう。</p>
2893<div class="code_container">
2894<pre class="highlight">
2895/* app/assets/stylesheets/application.css */
2896body {
2897  font-family: Arial, Helvetica, sans-serif;
2898  padding: 1rem;
2899}
2900
2901nav {
2902  justify-content: flex-end;
2903  display: flex;
2904  font-size: 0.875em;
2905  gap: 0.5rem;
2906  max-width: 1024px;
2907  margin: 0 auto;
2908  padding: 1rem;
2909}
2910
2911nav a {
2912  display: inline-block;
2913}
2914
2915main {
2916  max-width: 1024px;
2917  margin: 0 auto;
2918}
2919
2920.alert,
2921.error {
2922  color: red;
2923}
2924
2925.notice {
2926  color: green;
2927}
2928
2929section.product {
2930  display: flex;
2931  gap: 1rem;
2932  flex-direction: row;
2933}
2934
2935section.product img {
2936  border-radius: 8px;
2937  flex-basis: 50%;
2938  max-width: 50%;
2939}
2940</pre>
2941</div>
2942<p>続いて、<code>app/views/products/show.html.erb</code>ファイルを以下のように更新して、新しいスタイルを反映します。</p>
2943<div class="code_container">
2944<pre class="highlight">
2945<span class="c">&lt;%# app/views/products/show.html.erb %&gt;</span>
2946<span class="nt">&lt;p&gt;</span><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Back"</span><span class="p">,</span> <span class="n">products_path</span> <span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span>
2947
2948<span class="nt">&lt;section</span> <span class="na">class=</span><span class="s">"product"</span><span class="nt">&gt;</span>
2949  <span class="cp">&lt;%=</span> <span class="n">image_tag</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">featured_image</span> <span class="k">if</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">featured_image</span><span class="p">.</span><span class="nf">attached?</span> <span class="cp">%&gt;</span>
2950
2951  <span class="nt">&lt;section</span> <span class="na">class=</span><span class="s">"product-info"</span><span class="nt">&gt;</span>
2952    <span class="cp">&lt;%</span> <span class="n">cache</span> <span class="vi">@product</span> <span class="k">do</span> <span class="cp">%&gt;</span>
2953      <span class="nt">&lt;h1&gt;</span><span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h1&gt;</span>
2954      <span class="cp">&lt;%=</span> <span class="vi">@product</span><span class="p">.</span><span class="nf">description</span> <span class="cp">%&gt;</span>
2955    <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2956
2957    <span class="cp">&lt;%=</span> <span class="n">render</span> <span class="s2">"inventory"</span><span class="p">,</span> <span class="ss">product: </span><span class="vi">@product</span> <span class="cp">%&gt;</span>
2958
2959    <span class="cp">&lt;%</span> <span class="k">if</span> <span class="n">authenticated?</span> <span class="cp">%&gt;</span>
2960      <span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Edit"</span><span class="p">,</span> <span class="n">edit_product_path</span><span class="p">(</span><span class="vi">@product</span><span class="p">)</span> <span class="cp">%&gt;</span>
2961      <span class="cp">&lt;%=</span> <span class="n">button_to</span> <span class="s2">"Delete"</span><span class="p">,</span> <span class="vi">@product</span><span class="p">,</span> <span class="ss">method: :delete</span><span class="p">,</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">turbo_confirm: </span><span class="s2">"Are you sure?"</span> <span class="p">}</span> <span class="cp">%&gt;</span>
2962    <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
2963  <span class="nt">&lt;/section&gt;</span>
2964<span class="nt">&lt;/section&gt;</span>
2965</pre>
2966</div>
2967<p>ブラウザでページを再読み込みすると、CSSが反映されたことを確認できます。</p>
2968<h4 id="importmap"><a class="anchorlink" href="#importmap">17.2 importmap</a></h4>
2969<p>RailsのJavaScriptでは、デフォルトで<a href="https://developer.mozilla.org/ja/docs/Web/HTML/Element/script/type/importmap">importmap</a>を経由する形で利用します。これにより、ビルドステップを必要とせずに現代のJavaScriptモジュールを書けるようになります。</p>
2970<p>利用するJavaScriptパッケージ名は、<code>config/importmap.rb</code>に記述されます。このファイルは、利用するJavaScriptパッケージ名を、ブラウザでimportmapタグを生成するためのソースファイルと<code>pin</code>で対応付けます。</p>
2971<div class="code_container">
2972<pre class="highlight">
2973<span class="c1"># config/importmap.rb</span>
2974<span class="c1"># Pin npm packages by running ./bin/importmap</span>
2975
2976<span class="n">pin</span> <span class="s2">"application"</span>
2977<span class="n">pin</span> <span class="s2">"@hotwired/turbo-rails"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"turbo.min.js"</span>
2978<span class="n">pin</span> <span class="s2">"@hotwired/stimulus"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"stimulus.min.js"</span>
2979<span class="n">pin</span> <span class="s2">"@hotwired/stimulus-loading"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"stimulus-loading.js"</span>
2980<span class="n">pin_all_from</span> <span class="s2">"app/javascript/controllers"</span><span class="p">,</span> <span class="ss">under: </span><span class="s2">"controllers"</span>
2981<span class="n">pin</span> <span class="s2">"trix"</span>
2982<span class="n">pin</span> <span class="s2">
2982"@rails/actiontext"</span><span class="p">,</span> <span class="ss">to: </span><span class="s2">"actiontext.esm.js"</span>
2983</pre>
2984</div>
2985<div class="info"><p>上の<code>pin</code>は、それぞれJavaScriptパッケージ名(例: <code>"@hotwired/turbo-rails"</code>)を特定のファイルやURL(例: <code>"turbo.min.js"</code>)に対応付けます。<code>pin_all_from</code>は、ディレクトリ内のすべてのファイル(例: <code>app/javascript/controllers</code>)を名前空間(例: <code>"controllers"</code>)に一括で対応付けます。</p></div>
2986<p>importmapは、最新のJavaScript機能をサポートすると同時に、JavaScriptのセットアップをクリーンかつ最小限に保ちます。</p>
2987<p>Railsの<code>config/importmap.rb</code>ファイルには、既にいくつかのJavaScriptファイルが存在しています。これらは、Hotwireと呼ばれるRailsのデフォルトのフロントエンドフレームワークです。</p>
2988<h4 id="hotwire"><a class="anchorlink" href="#hotwire">17.3 Hotwire</a></h4>
2989<p><a href="https://hotwired.dev/">Hotwire</a>は、サーバー側で生成されるHTMLを最大限に活用するように設計されたJavaScriptフレームワークで、以下の3つのコアコンポーネントで構成されています。</p>
2990<ol>
2991<li><p><a href="https://turbo.hotwired.dev/"><strong>Turbo</strong></a>: カスタムJavaScriptを記述せずに、ナビゲーションやフォーム送信、ページコンポーネント、更新を処理します。</p></li>
2992<li><p><a href="https://stimulus.hotwired.dev/"><strong>Stimulus</strong></a>: ページに機能を追加するカスタムJavaScriptが必要な場合のフレームワークを提供します。</p></li>
2993<li><p><a href="https://native.hotwired.dev/"><strong>Native</strong></a>: Web アプリを埋め込み、ネイティブ モバイル機能で段階的に拡張することで、ハイブリッドモバイル アプリを作成できます。</p></li>
2994</ol>
2995<p>storeアプリではまだJavaScriptを記述していませんが、storeアプリのフロントエンドでは既にHotwireが動いています。たとえば、製品を追加・編集するために作成したフォームを動かすのに暗黙でTurboが使われています。</p>
2996<p>詳しくは、<a href="asset_pipeline.html">アセットパイプライン</a>ガイドや<a href="working_with_javascript_in_rails.html">RailsでのJavaScript利用</a>ガイドを参照してください。</p>
2997<h3 id="railsでテストを書く"><a class="anchorlink" href="#rails%E3%81%A7%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E6%9B%B8%E3%81%8F">18 Railsでテストを書く</a></h3>
2998<p>Railsには堅牢なテストスイートが付属しています。製品の在庫が復活したときに正しい件数のメールが送信されることを確認するテストを記述してみましょう。</p>
2999<h4 id="フィクスチャ"><a class="anchorlink" href="#%E3%83%95%E3%82%A3%E3%82%AF%E3%82%B9%E3%83%81%E3%83%A3">18.1 フィクスチャ</a></h4>
3000<p>Railsでモデルを生成すると、モデルに対応するフィクスチャファイルが<code>test/fixtures/</code>ディレクトリに自動的に作成されます。</p>
3001<p><strong>フィクスチャ</strong>(fixture)は、テストを実行する前にテストデータベースに取り込まれる定義済みのデータセットです。フィクスチャは、レコードを覚えやすい名前で定義できるため、テストで手軽にアクセスできます。</p>
3002<p>フィクスチャファイルは、デフォルトでは空なので、テスト用のフィクスチャを取り込む必要があります。</p>
3003<p><code>Product</code>モデルのテスト用に、<code>test/fixtures/products.yml</code>フィクスチャファイルを以下のように更新しましょう。</p>
3004<div class="code_container">
3005<pre class="highlight">
3006# test/fixtures/products.yml
3007tshirt:
3008  name: T-Shirt
3009  inventory_count: 15
3010</pre>
3011</div>
3012<p><code>Subscriber</code>モデルのテスト用に、以下の2つのフィクスチャを<code>
3012test/fixtures/subscribers.yml</code>に追加します。</p>
3013<div class="code_container">
3014<pre class="highlight">
3015# test/fixtures/subscribers.yml
3016david:
3017  product: tshirt
3018  email: [email protected]
3019
3020chris:
3021  product: tshirt
3022  email: [email protected]
3023</pre>
3024</div>
3025<p>この<code>subscribers.yml</code>フィクスチャを見ると、<code>tshirt</code>という名前で<code>Product</code>用フィクスチャを参照できていることがわかります。Railsはこれらをデータベース内で自動的に関連付けるので、レコードIDや関連付けをテストコード内で手動で管理する必要はありません。</p>
3026<p>これらのフィクスチャは、テストスイートを実行するとき自動でデータベースに挿入されます。</p>
3027<h4 id="メール送信をテストする"><a class="anchorlink" href="#%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%81%E4%BF%A1%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B">18.2 メール送信をテストする</a></h4>
3028<p><code>test/models/product_test.rb</code>に以下のテストを追加してみましょう。</p>
3029<div class="code_container">
3030<pre class="highlight">
3031<span class="c1"># test/models/product_test.rb</span>
3032<span class="nb">require</span> <span class="s2">"test_helper"</span>
3033
3034<span class="k">class</span> <span class="nc">ProductTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
3035  <span class="kp">include</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">TestHelper</span>
3036
3037  <span class="nb">test</span> <span class="s2">"sends email notifications when back in stock"</span> <span class="k">do</span>
3038    <span class="n">product</span> <span class="o">=</span> <span class="n">products</span><span class="p">(</span><span class="ss">:tshirt</span><span class="p">)</span>
3039
3040    <span class="c1"># 製品を在庫切れにする</span>
3041    <span class="n">product</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="ss">inventory_count: </span><span class="mi">0</span><span class="p">)</span>
3042
3043    <span class="n">assert_emails</span> <span class="mi">2</span> <span class="k">do</span>
3044      <span class="n">product</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="ss">inventory_count: </span><span class="mi">99</span><span class="p">)</span>
3045    <span class="k">end</span>
3046  <span class="k">end</span>
3047<span class="k">end</span>
3048</pre>
3049</div>
3050<p>このテストで行っていることを1つずつ詳しく見てみましょう。</p>
3051<p>最初に、Action Mailer用のテストヘルパーを<code>include</code>して、テスト中に送信されたメールを監視できるようにします。</p>
3052<p><code>tshirt</code>フィクスチャは フィクスチャが生成する<code>products()</code>ヘルパーメソッドで読み込まれ、そのレコードのActive Recordオブジェクトを返します。各フィクスチャはテストスイートでこのようなヘルパーを生成します(データベースIDは実行ごとに異なる可能性があるため、フィクスチャを名前で簡単に参照できるようにします)。</p>
3053<p>次に、在庫を0に更新して、Tシャツを在庫切れの状態にします。</p>
3054<p>次に、ブロック内のコードによって2件のメールが生成されたことを<a href="https://api.rubyonrails.org/v8.1/classes/ActionMailer/TestHelper.html#method-i-assert_emails"><code>assert_emails</code></a>アサーションで確認します。</p>
3055<p>メールをトリガーするには、ブロック内で製品の在庫数を更新します。これにより、<code>Product</code>モデルの<code>notify_subscribers</code>コールバックがトリガーされ、メールが送信されます。</p>
3056<p>実行が完了すると、<code>assert_emails</code>はメールの件数をカウントし、期待される件数と一致することを確認します。</p>
3057<p>以下のようにファイル名を指定して個別のテストファイルを実行してみましょう。なお、単に<code>bin/rails test</code>コマンドを実行すると、すべてのテストスイートを実行することも可能です。</p>
3058<div class="code_container">
3059<pre class="highlight">
3060<span class="gp">$</span><span class="w"> </span>bin/rails <span class="nb">test test</span>/models/product_test.rb
3061<span class="go">Running 1 tests in a single process (parallelization threshold is 50)
3062Run options: --seed 3556
3063
3064</span><span class="gp">#</span><span class="w"> </span>Running:
3065<span class="go">
3066</span><span class="c">.
3067</span><span class="go">
3068Finished in 0.343842s, 2.9083 runs/s, 5.8166 assertions/s.
30691 runs, 2 assertions, 0 failures, 0 errors, 0 skips
3070</span>
3071</pre>
3072</div>
3073<p><code>product_test.rb</code>のテストはパスしました</p>
3074<p><code>ProductMailer</code>を生成したときにも、<code>test/mailers/product_mailer_test.rb</code>にサンプルテストが生成されていますので、こちらも以下のように更新してパスするようにしましょう。</p>
3075<div class="code_container">
3076<pre class="highlight">
3077<span class="c1"># test/mailers/product_mailer_test.rb</span>
3078<span class="nb">require</span> <span class="s2">"test_helper"</span>
3079
3080<span class="k">class</span> <span class="nc">ProductMailerTest</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">TestCase</span>
3081  <span class="nb">test</span> <span class="s2">"in_stock"</span> <span class="k">do</span>
3082    <span class="n">mail</span> <span class="o">=</span> <span class="no">ProductMailer</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="ss">product: </span><span class="n">products</span><span class="p">(</span><span class="ss">:tshirt</span><span class="p">),</span> <span class="ss">
3082subscriber: </span><span class="n">subscribers</span><span class="p">(</span><span class="ss">:david</span><span class="p">)).</span><span class="nf">in_stock</span>
3083    <span class="n">assert_equal</span> <span class="s2">"In stock"</span><span class="p">,</span> <span class="n">mail</span><span class="p">.</span><span class="nf">subject</span>
3084    <span class="n">assert_equal</span> <span class="p">[</span> <span class="s2">"[email protected]"</span> <span class="p">],</span> <span class="n">mail</span><span class="p">.</span><span class="nf">to</span>
3085    <span class="n">assert_equal</span> <span class="p">[</span> <span class="s2">"[email protected]"</span> <span class="p">],</span> <span class="n">mail</span><span class="p">.</span><span class="nf">from</span>
3086    <span class="n">assert_match</span> <span class="s2">"Good news!"</span><span class="p">,</span> <span class="n">mail</span><span class="p">.</span><span class="nf">body</span><span class="p">.</span><span class="nf">encoded</span>
3087  <span class="k">end</span>
3088<span class="k">end</span>
3089</pre>
3090</div>
3091<p>今度は以下のように全テストスイートを実行してみると、すべてのテストがパスすることが確認できます。</p>
3092<div class="code_container">
3093<pre class="highlight">
3094<span class="gp">$</span><span class="w"> </span>bin/rails <span class="nb">test</span>
3095<span class="go">Running 2 tests in a single process (parallelization threshold is 50)
3096Run options: --seed 16302
3097
3098</span><span class="gp">#</span><span class="w"> </span>Running:
3099<span class="go">
3100</span><span class="c">..
3101</span><span class="go">
3102Finished in 0.665856s, 3.0037 runs/s, 10.5128 assertions/s.
31032 runs, 7 assertions, 0 failures, 0 errors, 0 skips
3104</span>
3105</pre>
3106</div>
3107<p>これらのテストを出発点として、アプリケーション機能を完全にカバーするテストスイートを今後も構築できます。</p>
3108<p>詳しくは、<a href="testing.html">Railsアプリケーションのテスト</a>ガイドを参照してください。</p>
3109<h3 id="rubocopでコードの形式を統一する"><a class="anchorlink" href="#rubocop%E3%81%A7%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E5%BD%A2%E5%BC%8F%E3%82%92%E7%B5%B1%E4%B8%80%E3%81%99%E3%82%8B">19 RuboCopでコードの形式を統一する</a></h3>
3110<p>コードを書くときのフォーマットが、人や場所によってばらついてしまうことがあります。Railsには、コードのフォーマットを統一するうえで役に立つRuboCopという<a href="https://ja.wikipedia.org/wiki/Lint#%E3%80%8Clint%E3%80%8D%E3%81%AE%E6%B4%BE%E7%94%9F%E7%94%A8%E6%B3%95">linter</a>が付属しています。</p>
3111<p>以下のコマンドを実行することで、コードが一貫しているかどうかをチェックできます。</p>
3112<div class="code_container">
3113<pre class="highlight">
3114<span class="gp">$</span><span class="w"> </span>bin/rubocop
3115</pre>
3116</div>
3117<p>実行すると、違反とその内容が出力されます。
3118なお、以下は違反が発生していない場合の出力です。</p>
3119<div class="code_container">
3120<pre class="highlight">
3121<span class="go">Inspecting 53 files
3122</span><span class="c">.....................................................
3123</span><span class="go">
312453 files inspected, no offenses detected
3125</span>
3126</pre>
3127</div>
3128<p>RuboCopのコマンドで以下のように<code>--autocorrect</code>(または短縮形の<code>-a</code>)フラグを追加すると、修正可能な違反が自動修正されます。</p>
3129<div class="code_container">
3130<pre class="highlight">
3131<span class="gp">$</span><span class="w"> </span>bin/rubocop <span class="nt">-a</span>
3132</pre>
3133</div>
3134<h3 id="セキュリティチェック"><a class="anchorlink" href="#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">20 セキュリティチェック</a></h3>
3135<p>Railsには、アプリケーションのセキュリティ問題(セッションハイジャック、セッション固定、リダイレクトなどの、攻撃につながる可能性のある脆弱性)をチェックするためのBrakeman gemが含まれています。</p>
3136<p><code>bin/brakeman</code>コマンドを実行すると、アプリケーションの脆弱性を分析してレポートを出力します。</p>
3137<div class="code_container">
3138<pre class="highlight">
3139<span class="gp">$</span><span class="w"> </span>bin/brakeman
3140<span class="go">Loading scanner...
3141</span><span class="c">...
3142</span><span class="go">
3143== Overview ==
3144
3145Controllers: 6
3146Models: 6
3147Templates: 15
3148Errors: 0
3149Security Warnings: 0
3150
3151== Warning Types ==
3152
3153
3154No warnings found
3155</span>
3156</pre>
3157</div>
3158<p>セキュリティについて詳しくは、<a href="security.html">Railsアプリケーションのセキュリティ</a>ガイドを参照してください。</p>
3159<h3 id="github-actionsでciを実行する"><a class="anchorlink" href="#github-actions%E3%81%A7ci%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B">21 GitHub ActionsでCIを実行する</a></h3>
3160<p>Railsアプリを生成すると、<code>.github/</code>フォルダも生成されます。ここには、rubocopやbrakemanやテストスイートを自動実行するよう事前構成済みの<a href="https://github.co.jp/features/actions">GitHub Actions</a>設定が含まれています。</p>
3161<p>GitHub Actionsが有効になっているGitHubリポジトリにRailsアプリのコードをプッシュすれば、それだけでGitHub Actionsでこれらの手順が自動的に実行され、項目ごとに成功や失敗を報告してくれます。</p>
3162<p>これにより、コードの変更を監視して欠陥や問題を検出し、作業の品質を統一的に担保できるようになります。</p>
3163<h3 id="kamalでproduction環境にデプロイする"><a class="anchorlink" href="#kamal%E3%81%A7production%E7%92%B0%E5%A2%83%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B">22 Kamalでproduction環境にデプロイする</a></h3>
3164<p>いよいよお楽しみが始まります。アプリをデプロイしてみましょう。</p>
3165<p>Railsに付属している<a href="https://kamal-deploy.org">Kamal</a>というデプロイツールを使えば、アプリケーションをサーバーに直接デプロイできます。KamalはアプリケーションをDockerコンテナで実行し、ダウンタイムなしでデプロイします。</p>
3166<p>
3166Railsには、デフォルトでproduction環境に対応したDockerfileが付属しており、Kamalはこれを使ってDockerイメージをビルドし、コンテナ化されたアプリケーションとそのすべての依存関係と設定を作成します。</p>
3167<p>なお、このDockerfileでは、production環境でアセットを効果的に圧縮して配信するために、<a href="https://github.com/basecamp/thruster">Thruster</a>を使っています。</p>
3168<p>Kamalでデプロイを行うには、以下のものが必要です。</p>
3169<ul>
3170<li>1GB以上のRAMを搭載したUbuntu LTSを実行するサーバー。
3171デプロイ先のサーバーは、定期的なセキュリティとバグ修正を受けられるように、LTS(長期サポート)版のUbuntu OSを実行している必要があります。HetznerやDigitalOceanなどのホスティングサービスでは、Kamalの利用をすぐ開始できるサーバーを提供しています。</li>
3172<li>
3173<a href="https://hub.docker.com">Docker Hub</a>のアカウントとアクセストークン。
3174Docker Hubは、アプリケーションのイメージを保存し、サーバーにダウンロードして実行できるようにします。</li>
3175</ul>
3176<p>Docker Hubで、アプリケーションイメージの<a href="https://hub.docker.com/repository/create">リポジトリを作成</a>します。リポジトリの名前は「store」にしておきます。</p>
3177<p><code>config/deploy.yml</code>ファイルをエディタで開いて、<code>192.168.0.1</code>をサーバーのIPアドレスに置き換え、<code>your-user</code>をDocker Hubのユーザー名に置き換えます。</p>
3178<div class="code_container">
3179<pre class="highlight">
3180# config/deploy.yml
3181# Name of your application. Used to uniquely configure containers.
3182service: store
3183
3184# Name of the container image.
3185image: your-user/store
3186
3187# Deploy to these servers.
3188servers:
3189  web:
3190    - 192.168.0.1
3191
3192# Credentials for your image host.
3193registry:
3194  # Specify the registry server, if you're not using Docker Hub
3195  # server: registry.digitalocean.com / ghcr.io / ...
3196  username: your-user
3197</pre>
3198</div>
3199<p><code>proxy:</code>セクションでは、アプリケーションでSSLを有効にするためのドメインも追加できます。DNSレコードがサーバーを確実に指していることを確認してください。Kamalは、<a href="https://letsencrypt.org/">LetsEncrypt</a>を用いてドメインのSSL証明書を発行します。</p>
3200<div class="code_container">
3201<pre class="highlight">
3202# config/deploy.yml
3203proxy:
3204  ssl: true
3205  host: app.example.com
3206</pre>
3207</div>
3208<p>KamalがアプリケーションのDockerイメージをプッシュできるように、DockerのWebサイトで読み取りと書き込みの権限を持つ<a href="https://app.docker.com/settings/personal-access-tokens/create">アクセストークンを作成</a>します。</p>
3209<p>次に、ターミナルでアクセストークンを<code>KAMAL_REGISTRY_PASSWORD</code>環境変数に<code>export</code>して、Kamalがアクセストークンを利用できるようにします。</p>
3210<div class="code_container">
3211<pre class="highlight">
3212<span class="go">export KAMAL_REGISTRY_PASSWORD=your-access-token
3213</span>
3214</pre>
3215</div>
3216<p>サーバーのセットアップとアプリケーションのデプロイを初めて行うときは、以下のコマンドを実行します。</p>
3217<div class="code_container">
3218<pre class="highlight">
3219<span class="gp">$</span><span class="w"> </span>bin/kamal setup
3220</pre>
3221</div>
3222<p>おめでとうございます! 新しいRailsアプリケーションがproduction環境で動くようになりました!</p>
3223<p>新しいRailsアプリケーションが動いていることを確認してみましょう。ブラウザでサーバーのIPアドレスを入力すると、ストアが動いていることが確認できるはずです。</p>
3224<p>以後、アプリに変更を加えたら、以下のコマンドを実行するだけでproduction環境にプッシュできます。</p>
3225<div class="code_container">
3226<pre class="highlight">
3227<span class="gp">$</span><span class="w"> </span>bin/kamal deploy
3228</pre>
3229</div>
3230<h4 id="production環境でユーザーを作成する"><a class="anchorlink" href="#production%E7%92%B0%E5%A2%83%E3%81%A7%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">22.1 production環境でユーザーを作成する</a></h4>
3231<p>production環境で製品の作成・編集を行うには、production環境のデータベースに<code>User</code>のレコードが必要です。</p>
3232<p>以下のKamalコマンドを使えば、production環境に接続してRailsコンソールを開けます。</p>
3233<div class="code_container">
3234<pre class="highlight">
3235<span class="gp">$</span><span class="w"> </span>bin/kamal console
3236</pre>
3237</div>
3238<div class="code_container">
3239<pre class="highlight">
3240<span class="n">store</span><span class="p">(</span><span class="n">prod</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">User</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="ss">email_address: </span><span class="s2">"[email protected]"</span><span class="p">,</span> <span class="ss">password: </span><span class="s2">"s3cr3t"</span><span class="p">,</span> <span class="ss">password_confirmation: </span><span class="s2">"s3cr3t"</span><span class="p">)</span>
3241</pre>
3242</div>
3243<p>これで、入力したメールアドレスとパスワードでproduction環境にログインして、製品を管理できるようになります。</p>
3244<h4 id="solid-queueでバックグラウンドジョブを処理する"><a class="anchorlink" href="#solid-queue%E3%81%A7%E3%83%90%E3%83%83%E3%82%AF%E3%82%B0%E3%83%A9%E3%82%A6%E3%83%B3%E3%83%89%E3%82%B8%E3%83%A7%E3%83%96%E3%82%92%E5%87%A6%E7%90%86%E3%81%99%E3%82%8B">22.2 Solid Queueでバックグラウンドジョブを処理する</a></h4>
3245<p>バックグラウンドジョブを使うと、タスクをバックグラウンドで非同期的に別プロセスで実行できるため、ユーザーエクスペリエンスを損なわずに済みます。</p>
3246<p>
324610,000人の受信者に在庫メールを送信することを想像してみましょう。大量のメール送信には時間がかかる可能性があるため、そのタスクをバックグラウンドジョブに乗せ換えることで、Railsアプリの応答性を維持できるようになります。</p>
3247<p>development環境のRailsでは、<code>:async</code>キューアダプタでActiveJobのバックグラウンドジョブを処理します。asyncアダプタは保留中のジョブをメモリに保存しますが、再起動すると保留中のジョブは失われます。これはdevelopment環境には最適ですが、production環境には適していません。</p>
3248<p>Railsのproduction環境では、バックグラウンドジョブをより堅牢にするために<code>solid_queue</code>を利用します。Solid Queueは、ジョブをデータベースに保存して、別プロセスで実行します。</p>
3249<p>Solid Queueは、<code>config/deploy.yml</code>の<code>SOLID_QUEUE_IN_PUMA: true</code>環境変数によって、production環境でのKamalデプロイで有効になっています。これにより、<a href="https://github.com/puma/puma">Puma</a> WebサーバーはSolid Queueプロセスの開始と停止を自動的に行います。</p>
3250<p>メールがAction Mailerの<code>deliver_later</code>メソッドによって送信されると、これらのメールはバックグラウンド送信のためにActive Jobに送信されるので、HTTPリクエストが遅延せずに済みます。production環境でSolid Queueを使うと、メールはバックグラウンドで送信され、送信に失敗した場合に自動的に再試行され、ジョブは再起動中でもデータベースに安全に保持されます。</p>
3251<p><img src="/railsguides/images/getting_started/background_jobs_light.jpg" alt="バックグラウンドジョブの流れ" data-src="/railsguides/images/getting_started/background_jobs_light.jpg" class="lazyload" loading="lazy"></p>
3252<h3 id="今後のステップ"><a class="anchorlink" href="#%E4%BB%8A%E5%BE%8C%E3%81%AE%E3%82%B9%E3%83%86%E3%83%83%E3%83%97">23 今後のステップ</a></h3>
3253<p>初めてのRailsアプリケーションの構築、お疲れ様でした。デプロイの完了おめでとうございます!</p>
3254<p>次は、<a href="sign_up_and_settings.html">演習:ユーザー登録・設定機能の追加</a>に従って学習を続けてください。</p>
3255<div class="info"><p>訳注:Railsガイドでは開発経験者が早くキャッチアップできるよう、多くの用語説明を省略しています。読んでいて「難しい」と感じた場合は<a href="https://railstutorial.jp/">Railsチュートリアル</a>からお読みください。</p></div>
3256<p>Railsをトピック別に押さえたい方は、以下のガイドもぜひご活用ください。</p>
3257<ul>
3258<li><a href="active_record_basics.html">Active Recordの基礎</a></li>
3259<li><a href="layouts_and_rendering.html">レイアウトとレンダリング</a></li>
3260<li><a href="testing.html">Railsテスティングガイド</a></li>
3261<li><a href="debugging_rails_applications.html">Railsアプリケーションのデバッグ</a></li>
3262<li><a href="security.html">Railsセキュリティガイド</a></li>
3263</ul>
3264<p>アプリは楽しく作りましょう!</p>
3265<h3 id="関連リンク"><a class="anchorlink" href="#%E9%96%A2%E9%80%A3%E3%83%AA%E3%83%B3%E3%82%AF">24 関連リンク</a></h3>
3266          <ul>
3267<li><a href="https://kaigionrails.org/2025/talks/takahashim/">Kaigi on Rails - Railsアプリケーション開発者のためのブックガイド</a></li>
3268<li><a href="https://note.com/yasslab/n/n89d6850e296d">Rails 8はSQLiteで大幅に強化された「個人が扱えるフレームワーク」</a></li>
3269<li><a href="https://qiita.com/yui-knk/items/4b4aaf4b8dea049efe6e">Ruby on Railsガイドのすすめ - yui-knk</a></li>
3270<li><a href="https://speakerdeck.com/yasslab/railsguides-as-an-oss-gate">OSS入門としてのRailsガイド (OSS Gate のご紹介)</a></li>
3271<li><a href="https://toprubycompanies.info/">Top Ruby Companies (Rubyを活用している上場企業まとめサイト)</a></li>
3272<li><a href="https://rubyonrails.org/doctrine/ja">The Rails Doctrine (日本語訳) - Ruby on Rails 公式サイト</a></li>
3273<li><a href="https://railstutorial.jp">Ruby on Railsチュートリアル - プロダクト開発の0→1を学ぼう</a></li>
3274</ul>
3275
3276
3277            <p id="mdEditLink" style="font-size: 1.1428em; text-align: right;">
3278              <a href="https://github.com/yasslab/railsguides.jp/tree/master/guides/source/ja/getting_started.md">🖋 GitHubで編集を提案する</a>
3279	       / 
3280              <a href="https://guides.rubyonrails.org/getting_started.html">📕 英語で読む</a>
3281            </p>
3282
3283            <h3 id="feedback" class="section" style="padding-top: 30px;"><a href="#feedback">フィードバックについて</a></h3>
3284            <p>
3285              Railsガイドは GitHub の <a href="https://github.com/yasslab/railsguides.jp">yasslab/railsguides.jp</a> で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に <a href="https://github.com/yasslab/railsguides.jp/pulls?q=is%3Apr">Pull Request</a> を出して頂けると嬉しいです。Pull Request の送り方については <a href="https://github.com/yasslab/railsguides.jp#readme">GitHub の README</a> をご参照ください。
3286            </p>
3287            <p>
3288              原著における間違いを見つけたら『<a href="/contributing_to_ruby_on_rails.html#rails%E3%81%AE%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AB%E8%B2%A2%E7%8C%AE%E3%81%99%E3%82%8B">Rails のドキュメントに貢献する</a>』を参考にしながらぜひ <a href="https://rubyonrails.org/community/">Rails コミュニティ</a>に貢献してみてください 🛠💨✨
3289            </p>
3290            <p>
3291              本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。
3292            </p>
3293            <p style="text-align: right">
3294	      Railsガイド運営チーム (<a target="_blank" rel="noopener" href="https://twitter.com/RailsGuidesJP">@RailsGuidesJP</a>)
3295            </p>
3296
3297
3298            <h3 id="supporters" class="section" style="padding-top: 30px;"><a href="#supporters">支援・協賛</a></h3>
3299
3300	    <a href="https://railsguides.jp/sponsors">
3301              <img class="lazyload" loading="lazy" alt="Railsガイド協賛プラン - バナー画像" src="/railsguides/images/spinner.svg" data-src="/railsguides/images/sponsors_banner.webp">
3302	    </a>
3303
3304            <p style="margin: 15px auto 30px;">
3305              Railsガイドは下記の協賛企業から継続的な支援を受けています。もしご興味あれば、<a href="https://railsguides.jp/sponsors">
3305協賛プラン</a>から気軽にお問い合わせいただけると嬉しいです。
3306            </p>
3307<div id="footer-bnrs">
3308            <!--  協賛企業一覧は sponsor.js にを移した-->
3309          </div>
3310
3311            
3312
3313            <!--
3314            <div class="footer-bnr">
3315              <div class="bnr-item" id='footer-carbonads'>
3316                <a href="https://www.carbonads.net/open-source?utm_source=railsguide&utm_medium=banner&utm_campaign=supporter" target="_blank" rel="noopener">
3317                  <img class="border lazyload" alt="Advertising For Open Source Projects | Carbon Ads" src="/railsguides/images/spinner.svg" data-src="/railsguides/images/logos/carbon.gif" loading="lazy" />
3318                </a>
3319              </div>
3320              <div class="bnr-item" id='footer-sponsors'>
3321                <a href="https://railsguides.jp/sponsors"><img alt="協賛プラン" src="/railsguides/images/spinner.svg" data-src="/railsguides/images/logos/bnr-kyosan.gif" loading="lazy" class="lazyload" /></a>
3322              </div>
3323            </div>
3324            -->
3325
3326          <ol class="snsb" style="padding-top: 50px;">
3327            <li style="padding-right: 8px;"><a class="github-button" href="https://github.com/yasslab/railsguides.jp" data-icon="octicon-star" data-show-count="true" aria-label="Star yasslab/railsguides.jp on GitHub">Star</a></li>
3328            <li style="padding-right: 8px;">
3329<a href="https://b.hatena.ne.jp/entry/s/railsguides.jp" class="hatena-bookmark-button" data-hatena-bookmark-title="Ruby on Rails ガイド:体系的に Rails を学ぼう" data-hatena-bookmark-layout="simple-balloon" data-hatena-bookmark-lang="ja" title="このエントリーをはてなブックマークに追加"><img src="https://b.st-hatena.com/images/entry-button/[email protected]" alt="このエントリーをはてなブックマークに追加" width="20" height="20" style="border: none;"></a>
3329<script type="text/javascript" src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script>
3329
3330</li>
3331            <li>
3332<a class="twitter-share-button" href="https://twitter.com/intent/tweet" data-hashtags="Railsガイド" data-via="RailsGuidesJP" data-related="RailsGuidesJP,YassLab" lang="ja" width="80"></a>
3332<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
3332
3333</li>
3334            <li><div class="fb-like" data-href="https://railsguides.jp/" data-width="225" data-layout="button_count" data-action="like" data-share="false"></div></li>
3335          </ol>
3336        </div>
3337<!--//mainCol-->
3338      </div>
3339<!--//wrapper-->
3340    </div>
3341<!--//container-->
3342
3343    <hr class="hide">
3344
3345    
3346
3347    
3347<script type="text/javascript" src="/railsguides/javascripts/jquery.min.js"></script>
3347
3348    
3348<script type="text/javascript" src="/railsguides/javascripts/responsive-tables.js"></script>
3348
3349    
3349<script type="text/javascript" src="/railsguides/javascripts/guides.js"></script>
3349
3350    
3350<script type="text/javascript"> $(guidesIndex.bind); </script>
3350
3351    
3351<script async defer src="https://buttons.github.io/buttons.js"></script>
3351
3352  </body>
3353    </div>
3354
3355    <footer>
3356  <ul class="footer-cover">
3357    <li class="footer-contents-li">
3358      <a href="/"><img data-src="/images/railsguides.png" loading="lazy" class="lazyload" alt="Railsガイド" style="width: 50%;"></a>
3359      <p style='font-size: 12px;'>Railsガイドの<a href="https://github.com/yasslab/railsguides.jp/tree/master/guides/source" target="_blank" rel="noopener">コンテンツ部分</a>は <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.ja" target="_blank" rel="noopener">CC BY-SA 4.0</a> に基づいて公開されています。『Rails』や『Ruby on Rails』という名称、ならびに Rails のロゴ画像は DHH の登録商標です。本サイトのロゴ画像などの一部は YassLab 社の著作物です。</p>
3360      <ul class="footer-sns-list">
3361        <li class="footer-sns-list__item"><a href="https://twitter.com/RailsGuidesJP" target=_blank" rel="noopener"><i class="fa fa-twitter-square fa-2x" aria-hidden="true" title="Twitter"></i></a></li>
3362        <li class="footer-sns-list__item"><a href="https://www.facebook.com/yasslab.jp" target=_blank" rel="noopener"><i class="fa fa-facebook-square fa-2x" aria-hidden="true" title="Facebook"></i></a></li>
3363        <li class="footer-sns-list__item"><a href="https://github.com/yasslab/railsguides.jp" target=_blank" rel="noopener"><i class="fa fa-github-square fa-2x" aria-hidden="true" title="GitHub"></i></a></li>
3364        <li class="footer-sns-list__item"><a href="https://note.com/yasslab/m/ma5df1164c798" target=_blank" rel="noopener"><i class="fa fa-rss-square fa-2x" aria-hidden="true" title="note"></i></a></li>
3365      </ul>
3366    </li>
3367    <li class="footer-contents-li">
3368      <h4 class="footer-h4">サービス</h4>
3369      <ul class="footer-contents-links">
3370        <li><a href="/ebook">電子書籍</a></li>
3371        <li><a href="/pro">Proプラン</a></li>
3372	<li><a href="/sponsors">協賛プラン</a></li>
3373      </ul>
3374    </li>
3375    <li class="footer-contents-li">
3376      <h4 class="footer-h4">運営に関する情報</h4>
3377      <ul class="footer-contents-links">
3378        <li><a target="_blank" rel="noopener" href="https://yasslab.jp/ja/">運営会社</a></li>
3379        <li><a href="/term-of-service">利用規約</a></li>
3380        <li><a href="/policy">特定商取引法に基づく表記</a></li>
3381        <li><a target="_blank" rel="noopener" href="https://yasslab.jp/ja/privacy">プライバシーポリシー</a></li>
3382        <li><a href="/contact">お問い合わせ</a></li>
3383      </ul>
3384    </li>
3385  </ul>
3386</footer>
3387
3388    
3389        
3389<script type="text/javascript">
3390      $('select[name=verselect]').change(function() {
3391        if ($(this).val() != '') {
3392            dataLayer.push({
3393              event: 'verselect',
3394              selectedElement: $(this).val()
3395            });
3396        }
3397      });
3398    </script>
3398
3399  
3399<script type="text/javascript">
3400    $('select[name=verselect]').change(function() {
3401      if ($(this).val() == '') {
3402        return false
3403      }
3404
3405      pathname = location.pathname.replace(/\/+$/, '').split('/').pop();
3406      if( pathname.endsWith('.html'))  {
3407        document.location.href = '/v' + $(this).val() + '/' + pathname;
3408      } else {
3409        document.location.href = '/v' + $(this).val() + '/';
3410      }
3411    });
3412    $("#guidesLogout").on("click", function (e) {
3413      $("#algolia-search").hide();
3414      document.activeElement.blur();
3415      $("#nav_login_menu").toggleClass("toggle");
3416      $("#nav-sp").removeClass("toggle");
3417      $("#guides").hide();
3418      $("#notice-check").attr('checked', false).prop('checked', false).change();
3419      return false;
3420    });
3421    $("#mainNav__toggle").on("click", function (e) {
3422      $("#guides").hide();
3423      $("#algolia-search").hide();
3424      document.activeElement.blur();
3425      $("#nav-sp").toggleClass("toggle");
3426      $("#logout").toggleClass("toggle");
3427      $("#notice-check").attr('checked', false).prop('checked', false).change();
3428      return false;
3429    });
3430    // ハンバーガーメニューで他の場所をクリックしたら閉じるように
3431    $(document).on("click", function (e) {
3432      if (!$(e.target).closest('#nav-sp').length) {
3433        $('#nav-sp').removeClass('toggle');
3434      }
3435    });
3436    $("#guidesMenu").on("click", function (e) {
3437      $("#nav-sp").removeClass("toggle");
3438    });
3439    $(window).resize(function() {
3440      if ($(window).width() > 1087) {
3441        $("#nav > .nav").removeClass("toggle");
3442        $("#logout").removeClass("toggle");
3443        $("#header > div.wrapper").removeClass("extend-height");
3444      }
3445    });
3446    $("#search-input").keyup(function(){
3447        var val = $(this).val();
3448        $(".algolia-search__list").attr("href", "/v8.1" + "/search?q=" + encodeURIComponent(val));
3449    });
3450
3451
3452    // 他の場所をクリックすると目次が閉じるように
3453    $(document).on('click', function(e) {
3454      if (!$(e.target).closest('#guides').length) {
3455        $("#guides").hide();
3456      }
3457      //お知らせを閉じる
3458      if (!$(e.target).closest('.notice-section').length) {
3459        $("#notice-check").attr('checked', false).prop('checked', false).change();
3460      }
3461      // 検索フォームを閉じる
3462      if (!$(e.target).closest('#algolia-search').length) {
3463        $("#algolia-search").hide();
3464      }
3465      // ログインメニューを閉じる
3466      if (!$(e.target).closest('.nav-login-menu-list').length) {
3467        $("#nav_login_menu").removeClass("toggle");
3468      }
3469    })
3470    // esc is hide of guides
3471    $(window).keydown(function(e){
3472      if(e.keyCode === 27){
3473        $("#guides").hide();
3474        return false;
3475      }
3476    });
3477  </script>
3477
3478
3479
3480    
3480<script>
3481      twemoji.parse(document.body);
3482      function current_version_js() {
3483        return '8.1';
3484      }
3485    </script>
3485
3486  </body>
3487</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.