Rails初学者必見！作って学ぶRailsのリクエスト/レスポンス

## はじめに
初学者に絶対押さえておいてほしいURLでのアクセス(リクエスト)から表示が返ってくる(レスポンス)までにRailsがどんなことをしているのかの概要のお話です。
これを押さえておくとRailsの大枠を掴んで学習の効率がアップしたり、エラーが起きた時にどこに原因があるかを推測しやすくなったりします。

## 今回やること
scaffoldコマンドを使って簡単なメモアプリを作成しながら、Railsがどんなことをやっているのかを一つ一つ確認したり、どういうルールがあるのかの解説をします。

## こんな人におすすめ
Railsの書籍を1周やってみたけどちゃんと理解できていない気がする...
URLを入力してからユーザーに画面が表示されるまでの流れが説明できない...
エラー画面が出た時にどこをみたらいいのかよくわからない...

## 前提知識
指定のバージョンのRubyをインストール出来ること
ターミナルの簡単な操作が出来ること
エディタを使ったファイルの閲覧が出来ること

## 今回作成するアプリケーションの環境
Ruby: 3.1.2
Rails: 7.0.4

## まずはrails new

```
gem install rails -v 7.0.4
rails _7.0.4_ new memo_app --skip-hotwire --skip-test --skip-jbuilder
```

## サーバーの起動確認

```
cd memo_app
bin/rails s
```

`http://localhost:3000/`にアクセスしてこんな画面が出てきたら成功です。

![81ab0297f9565e45fcfb2c5f2cb1b384.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/0eb21257-8689-69bb-1dff-faf49ad4ed62.png)

## scaffoldコマンドを使ってmemo機能
便利コマンドを使ってmemo機能を作ってしまいましょう
ここではわかりやすくするためhelper関連のファイルはいらないので`--no-helper`を使って飛ばしています

```
bin/rails g scaffold memo title:string body:text --no-helper
```

上記のコマンドを打つとこんな感じでたくさんファイルが作られます

```
❯❯❯bin/rails g scaffold memo title:string body:text --no-helper                                                 [main]
      invoke  active_record
      create    db/migrate/20221205161342_create_memos.rb
      create    app/models/memo.rb
      invoke  resource_route
       route    resources :memos
      invoke  scaffold_controller
      create    app/controllers/memos_controller.rb
      invoke    erb
      create      app/views/memos
      create      app/views/memos/index.html.erb
      create      app/views/memos/edit.html.erb
      create      app/views/memos/show.html.erb
      create      app/views/memos/new.html.erb
      create      app/views/memos/_form.html.erb
      create      app/views/memos/_memo.html.erb
      invoke    resource_route
```

## 作られたファイルを見てみよう
作られたファイルは大きく分けると
1. モデル関連のファイル
2. ルーティング関連のファイル
3. コントローラー関連のファイル
4. 表示関連のファイル

の4種類のファイルや記述が追加されています。
少し中身をのぞいてみましょう
ファイル名の横の`▶︎`をクリックすると詳細がみれますよ

### モデル関連のファイル

<details>

<summary>db/migrate/20221205161342_create_memos.rb</summary>

これはデータベースのテーブルを作るためのマイグレーションファイルと呼ばれるものです。
長い数字には作成した時の日時が入っています。
ここの記載に沿ってmemosテーブルが作られます。

```ruby
class CreateMemos < ActiveRecord::Migration[7.0]
  def change
    create_table :memos do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end
```

</details>

<details>

<summary>app/models/memo.rb</summary>

これはモデルの記述を書くファイルです。
今はそれほど記述はないようですね。

```ruby
class Memo < ApplicationRecord
end
```

</details>

### ルーティング関連のファイル

<details>

<summary>config/routes.rb</summary>

configディレクトリはRails全体の設定を記載するディレクトリです。
`routes.rb`はルーティング(リクエストが来た時にどこにいけばいいのかの設定)を記述するファイルです。
何やらmemosに関する記述とコメントアウト(人間には見えるがプログラムによって実行はされない記述)がありますね。

```ruby
Rails.application.routes.draw do
  resources :memos
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
end
```

</details>

### コントローラー関連のファイル

<details>

<summary>app/controllers/memos_controller.rb</summary>

なにやらたくさん記述してありますね。

```ruby
class MemosController < ApplicationController
  before_action :set_memo, only: %i[ show edit update destroy ]

  # GET /memos
  def index
    @memos = Memo.all
  end

  # GET /memos/1
  def show
  end

  # GET /memos/new
  def new
    @memo = Memo.new
  end

  # GET /memos/1/edit
  def edit
  end

  # POST /memos
  def create
    @memo = Memo.new(memo_params)

    if @memo.save
      redirect_to @memo, notice: "Memo was successfully created."
    else
      render :new, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /memos/1
  def update
    if @memo.update(memo_params)
      redirect_to @memo, notice: "Memo was successfully updated."
    else
      render :edit, status: :unprocessable_entity
    end
  end

  # DELETE /memos/1
  def destroy
    @memo.destroy
    redirect_to memos_url, notice: "Memo was successfully destroyed."
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_memo
      @memo = Memo.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def memo_params
      params.require(:memo).permit(:title, :body)
    end
end
```

</details>

### 表示関連のファイル
<details>

<summary>app/views/memos/index.html.erb</summary>

indexはデータの一覧を表示するために使われることが多いです。

```erb
<p style="color: green"><%= notice %></p>

<h1>Memos</h1>

<div id="memos">
  <% @memos.each do |memo| %>
    <%= render memo %>
    <p>
      <%= link_to "Show this memo", memo %>
    </p>
  <% end %>
</div>

<%= link_to "New memo", new_memo_path %>
```

</details>

<details>

<summary>app/views/memos/edit.html.erb</summary>

editはデータの編集のためによく使われます。

```erb
<h1>Editing memo</h1>

<%= render "form", memo: @memo %>

<br>

<div>
  <%= link_to "Show this memo", @memo %> |
  <%= link_to "Back to memos", memos_path %>
</div>
```

</details>

<details>

<summary>app/views/memos/show.html.erb</summary>

showは特定のデータの詳細情報を表示するのによく使われます。

```erb
<p style="color: green"><%= notice %></p>

<%= render @memo %>

<div>
  <%= link_to "Edit this memo", edit_memo_path(@memo) %> |
  <%= link_to "Back to memos", memos_path %>

  <%= button_to "Destroy this memo", @memo, method: :delete %>
</div>
```

</details>

<details>

<summary>app/views/memos/new.html.erb</summary>

newはデータの新規作成でよく使われます。

```erb
<h1>New memo</h1>

<%= render "form", memo: @memo %>

<br>

<div>
  <%= link_to "Back to memos", memos_path %>
</div>
```

</details>

<details>

<summary>app/views/memos/_form.html.erb</summary>

`_`から始まるファイルは部分テンプレートやパーシャルと呼ばれるファイルでその名の通り、表示の一部分を切り取ったものが書かれ、他のファイルの中で呼び出して使います。
いろいろな表示で共通のものなどをパーシャルをつかって切り出しておくと何度も同じコードを書く手間がなくなったり、コード自体もスッキリしてわかりやすくなったりするのでよく使われます。

このファイルはフォームのコードを切り出していて、newやeditで呼び出して使われています。

```erb
<%= form_with(model: memo) do |form| %>
  <% if memo.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(memo.errors.count, "error") %> prohibited this memo from being saved:</h2>

      <ul>
        <% memo.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :title, style: "display: block" %>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :body, style: "display: block" %>
    <%= form.text_area :body %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>
```

</details>

<details>

<summary>app/views/memos/_memo.html.erb</summary>

このファイルも`_`から始まるのでパーシャルです。
これはmemoの情報を表示するコードが書いてありindexやshowで呼び出されています。

```erb
<div id="<%= dom_id memo %>">
  <p>
    <strong>Title:</strong>
    <%= memo.title %>
  </p>

  <p>
    <strong>Body:</strong>
    <%= memo.body %>
  </p>

</div>
```

</details>

## memoの新規作成画面を表示してみよう
`bin/rails s`でサーバーを立ち上げていない人は立ち上げましょう。

さて、ここでmemoの新規作成画面にアクセスするためにはどのようなURLでアクセスすればいいか考えてみてください。

<details>
<summary>どのようにURLを調べましたか？</summary>
いろいろな調べ方があると思います

- ルーティングファイルを確認しに行った人
- 知識をもとにuserの時はこうだったから...って考えた人
- コマンドを使ってURLを調べた人

ちゃんとアクセス出来ればどれも100点満点です。

正解は
http://localhost:3000/memos/new
ですね。

エラー画面が表示されましたか？
大丈夫あなたの画面は正常です。(すでに解決できている人はそのまま進んでください)
次で解説しますね。

![e2d424be2eb33ed524477e46dbad1a93.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/8d97ebeb-b6e2-89c7-e3b3-f7174cd9de2d.png)


</details>

## データベースを作成する

次のようなエラーが表示されてしまいました。

```
ActiveRecord::StatementInvalid in MemosController#new
Could not find table 'memos'
```

`ActiveRecord::StatementInvalid`はデータベースに何か問題があるよというエラーです。
`Could not find table 'memos'`memosテーブルが見つかりませんといっていますね。
そういえばテーブルを作成するコマンドを打っていませんでした。
コマンドを使ってマイグレーションファイルで定義したテーブルを作成しましょう。

```
bin/rails db:migrate
```

どうやらmemosテーブルが作成されたみたいですね

```
❯❯❯bin/rails db:migrate                                                                                        +[main]
== 20221205161342 CreateMemos: migrating ======================================
-- create_table(:memos)
   -> 0.0009s
== 20221205161342 CreateMemos: migrated (0.0009s) =============================
```

このコマンドを打った時にこっそり作成されるファイルがあります。
それは、`schema.rb`というファイルです。
このファイルにはマイグレーションファイルをもとにRailsが実際に作成したテーブルやカラムの状態が書いてあります。

どんなテーブルやカラムがあるかはこのファイルにまとまっているので覚えておくとどんなテーブルがあるか確認したい時や、自分が書いたマイグレーションファイルが正しく定義出来ていたのかを確認したい時などに役に立つので覚えておきましょう。

<details>
<summary>db/schema.rb</summary>

```ruby
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.0].define(version: 2022_12_05_161342) do
  create_table "memos", force: :cascade do |t|
    t.string "title"
    t.text "body"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end
```

</details>

## ルーティングについて知る
気を取り直してルーティングの確認に戻りましょう。
再度
http://localhost:3000/memos/new
にアクセスしてみてください。
こんな感じの画面が表示されていれば成功です。

![765c09248de576af0af9ce9cdd285fb5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/504c66c2-43bb-6cc4-7983-bef51163ae8d.png)

先ほどはどのような方法でもどんなURLでアクセスすればいいかわかれば100点と言いましたが、どんなURLでアクセス出来るか調べたい時におすすめの方法があります。

Railsサーバーを立ち上げた状態で
`http://localhost:3000/rails/info/routes`
にアクセスしてみてください。
このような画面が出てくると思います。

![c637fbaf19cc760680e9c0b432a8fee0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/53d206b0-6d5f-2bc9-17b1-cac860c42209.png)

この画面は画面が広く見れるのと、検索が使えるのでわかりやすくおすすめです。

## `http://localhost:3000/rails/info/routes`の見方
この表は

- Helper
- HTTP Verb
- Path
- Controller#Action

の4つにわかれています。

### Helper
Helperはコントローラーのリダイレクト先の指定(`redirect_to memos_path`)や`link_to`のようなメソッドで使えるURLヘルパーというものです。
いちいち`http://localhost:3000/memos/new`のように記載しなくても短く書くことが出来、定義の変更にも対応しやすい書き方です。

### HTTP Verb
HTTP VerbのVerbはHTTPリクエストメソッドのことです。
GETやPOST、PUT、PATCH、DELETEなどですね。

### Path
パスはメモの新規作成ページのURLでいうところの
`http://localhost:3000/memos/new`の`/memos/new`の部分のことです。
パスは場所のことを指しており、ユーザーがどの情報が欲しいのかはこのパスを見て判断されます。
しかし、ルーティングのページをよく見てみると同じパスが存在していますね。
![33d4dce2148394eeb6ecfa77d2e1f8e7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/4c9d6fcc-7c90-d7ba-0255-92f62108691b.png)
同じパスが存在していて困りそうですが、これは同じパスでも先ほど出てきたHTTPリクエストメソッド(HTTP Verb)が違えば違うリクエストとして解釈されるので
GETリクエストの`http://localhost:3000/memos/1`
と
POSTリクエストの`http://localhost:3000/memos/1`
は明確に区別されるので大丈夫です。

### Controller#Action
Controller#Actionにはどのコントローラーのどのアクション(newとかshowとかindexとかcreateとか)で処理するのかが書いてあります。

### ルーティングの見方まとめ
GET `http://localhost:3000/memos/1`というリクエストが飛んできた時にRailsはこのGETリクエストで`/memos/1`というPathへのリクエストがきたら`memos#show`つまり、MemosControllerのshowアクションを実行してあげればいいと解釈してくれるというわけです。

## メモ一覧画面が表示されるまでの流れを追ってみよう

### メモ一覧画面へのリクエスト
メモ一覧画面はGETの`http://localhost:3000/memos`でリクエストを送れます。
RailsはGETリクエストでパスが`/memos`のところをルーティングの対応表から探します。
![ed54ed816b698d479d99385fbaa9004b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/0c605b8d-1a50-1834-3479-16c9d0a20f35.png)
そうするとこのリクエストはどのコントローラーのどのアクションで対応すればいいリクエストなのかがわかります。
ここではController#Actionのところに`memos#index`と書いてあるのでMemosControllerのindexアクションでこのリクエスト用の対応するということがわかります。

### コントローラーでの対応

app/controllers/memos_controller.rb(indexアクションに関係するものだけ抜粋)

```ruby
class MemosController < ApplicationController
  # GET /memos
  def index
    @memos = Memo.all
  end
end
```

indexアクションの中には`@memos = Memo.all`と書いてあります。
ここではモデルとやりとりして全てのメモデータを`@memos`というインスタンス変数に代入しています。

データを取得したり、代入していることはわかったけど、ここから先にどうやったらユーザーに画面を見せることができるのかよくわかりませんね。

実はこの先の動きはRailsで決まっているお約束に沿って行われます。

それは各アクションでの処理が終わったらそのコントローラーと同じ名前のディレクトリにある、今実行したアクション名と同じ名前を持つHTMLファイルをユーザーに返してあげるとうお約束です。

このコントローラーはMemosControllerなので`app/views/memos`ディレクトリを探します。
そしてアクション名はindexなので`index.html.erb`ファイルを探して来ます。
つまり、なにも書いてはいないけどルールに沿って`app/views/memes/index.html.erb`を探してくれるというわけです。

app/views/memos/index.html.erb

```erb
<p style="color: green"><%= notice %></p>

<h1>Memos</h1>

<div id="memos">
  <% @memos.each do |memo| %>
    <%= render memo %>
    <p>
      <%= link_to "Show this memo", memo %>
    </p>
  <% end %>
</div>

<%= link_to "New memo", new_memo_path %>
```

### 表示用のテンプレートにモデルからもらった値などをセットする
検証ツールを使って実際に表示されているHTMLを見てみると`link_to`と書いてあるところが
`<a href="/memos/new">New memo</a>`
のようになっていたりと`index.html.erb`とは少し違うのがわかるかと思います。

![29a398b15e58aa473de4f55848d2a260.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/1bb66133-3792-1f53-5db3-ae1fa7d457fe.png)

`index.html.erb`ファイルはテンプレートというもので、Railsはこのファイルをそのままユーザーに渡しているわけではなく、インスタンス変数の値を埋め込んだり、Railsの記法やRubyのコードなどを実行した結果をHTMLに変換してそれを返しています。
コントローラーのところで出てきたインスタンス変数はこういったテンプレートファイルに値をセットするために定義することが多いです。

### 値をセットするなどして作成したHTMLファイルをブラウザに渡す
最後にRailsは作成したHTMLファイルをブラウザに渡すことでユーザーはメモ一覧画面をみることが出来ます。

実際に生成されたHTML
```html
<html>
  <script type="text/javascript">window["_gaUserPrefs"] = { ioo : function() { return true; } }</script>
  <head>
    <title>MemoApp</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="csrf-param" content="authenticity_token">
    <meta name="csrf-token" content="QH_kuXKR-beYiKhZBEvQDR-dESiU2sdwMsrE7-appT7mAqo0F7Kj52AfXBRlKA7xS6lIV2etAGyRJdLg8krC7g">
    <link rel="stylesheet" href="/assets/application-e0cf9d8fcb18bf7f909d8d91a5e78499f82ac29523d475bf3a9ab265d5e2b451.css">
    <script type="importmap" data-turbo-track="reload">{
      "imports": {
        "application": "/assets/application-3897b39d0f7fe7e947af9b84a1e1304bb30eb1dadb983104797d0a5e26a08736.js"
      }
    }</script>
    <link rel="modulepreload" href="/assets/application-3897b39d0f7fe7e947af9b84a1e1304bb30eb1dadb983104797d0a5e26a08736.js">
    <script src="/assets/es-module-shims.min-d89e73202ec09dede55fb74115af9c5f9f2bb965433de1c2446e1faa6dac2470.js" async="async" data-turbo-track="reload"></script>
    <script type="module">import "application"</script>
  </head>

  <body>
    <p style="color: green"></p>
    <h1>Memos</h1>

    <div id="memos">
    </div>

    <a href="/memos/new">New memo</a>
  </body>
</html>
```

index.html.erbに書いてあること以外にもいろいろ書いてありますね。
Railsは対象のファイルだけを返すのではなく、レイアウトと呼ばれるファイルに書いてある
`<%= yield %>`
にindex.html.erbなどのファイルの中身を入れ込んで丸っとHTMLに変換したものを返しています。

<details>
<summary>app/views/layouts/application.html.erb</summary>

`<%= yield %>`と書いてあるところがありますね

```erb
<!DOCTYPE html>
<html>
  <head>
    <title>MemoApp</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

</details>

最後にブラウザがレスポンスで返されたHTMLを人間に見やすいように表示することで
ユーザーは
GETリクエストで`http://localhost:3000/memos`
にリクエストを送るとメモ一覧画面を見れるようになります。
![0e76ec4b9fbda829282e70deb9ce83e2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/b9292091-e5a2-7139-cacf-bd40f73e6fac.png)

## [おまけ]エラーの内容を予想してみよう
### Routing Error
![f178738936fb9ba06d29bac773f5ebaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/503af79e-da42-8778-46e4-c426067ef1fd.png)

<details>
<summary>どんな原因でしょう？</summary>

`No route matches [GET] "/memoss"`と書いてあることから
GETリクエストで`http://localhost:3000/memoss`
にアクセスが来たけどルーティングの表を見ても対応する項目がないということですね。

このことからルーティングが定義されていないのか、パスやHTTPリクエストメソッドで意図しないものが使われているといったことが推測できますね。

</details>

## NameError in MemosController#index
![99163037754aa388af950c6f28a3d079.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/f9f7d2e5-8e3d-2cdd-5cd8-9a388ce6fa85.png)

<details>
<summary>どんな原因でしょう？</summary>

どうやらコントローラーのindexアクションの途中の処理でエラーが起きているようですね。

ということはルーティングまではOKということがわかりますね。

それと`@memos = Memos.all`このコードでやりたいことはモデルとやりとりして全てのメモデータを`@memos`というインスタンス変数に代入することでした。

メモモデルはapp/models/memo.rbに定義されています。

```ruby
class Memo < ApplicationRecord
end
```

よく見てみるとmemo.rbファイルに定義してあるのは`Memo`で、コントローラーで使っているのは`Memos`になっていますね。
モデルのクラスを呼び出しているので`@memos = Memo.all`にしてあげるとよさそうなことが推測できます。

</details>

### No template for interactive request
![54857879bb334366acf2b105c6b136bf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/29640984-8f4e-e4ad-5715-bf486fb5fa18.png)

<details>
<summary>どんな原因でしょう？</summary>

`MemosController#index is missing a template for request formats: text/html`と書いてあることから対応するテンプレートファイルがないことがわかるかと思います。

Railsには各アクションでの処理が終わったらそのコントローラーと同じ名前のディレクトリにある、今実行したアクション名と同じ名前を持つHTMLファイルをユーザーに返してあげるとうお約束です。

このことからルーティングはOK、コントローラーに書いてある処理もOK、その後テンプレートファイルに値をセットしようとした時に該当のファイル(ここでは`app/views/memos/index.html.erb`)が見当たらないということなので、ファイルの作り忘れやファイル名が間違っているなどの原因が推測できますね。

</details>

## まとめ
Railsのリクエストからレスポンスまでの大枠を掴むとどんなところでエラーが出ているのかの特定がしやすくなったり、大枠を掴むことで詳細の理解が深まったりといいことがたくさんなので意識していきましょう。