Railsチュートリアル 第2章 - Toyアプリケーション - Usersコントローラ、Userモデル、index.html.erbビュー

# 前提

[Railsチュートリアル 第2章 - Toyアプリケーション - Usersリソースの挙動を変更する](https://qiita.com/rapidliner00/items/d79af84fbda8444946f4)からの続きです。

ホストは以下の状態であることを前提としています。

```:zsh
>>> pwd
~/docker/rails_tutorial_test/toy_app
```

Rails用のDockerコンテナが実行されており、以下の状態であることを前提としています。

```:bash
# pwd
/var/www/toy_app

# rails server
=> Booting Puma
=> Rails 5.1.6 application starting in development 
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.9.1 (ruby 2.5.1-p57), codename: Private Caller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

{この時点の状態}
```

# Usersコントローラとは

```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end

  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def user_params
      params.require(:user).permit(:name, :email)
    end
end
```

scaffoldで生成されたUsersコントローラの実体は、`pp/controllers/users_controller.rb`というRubyのソースコードです。以下の事柄が重要であろうと思います。

* index, show, new, edit, create, update, destroyというアクションが定義されている
* 各アクションは、RESTアーキテクチャに基づいて定義されている
  * コメント文を見るに、HTTPの各リクエストの使い方もRESTアーキテクチャに基づいている
* `@users`や`@user`といった変数が登場する
  * `@users`は、Userモデルからすべてのユーザーの一覧を取り出したものである
  * `@user`は、Userモデルから単一のユーザーを取り出したものである

関係するドキュメントとして、Railsチュートリアルに[RailsにおけるRESTアーキテクチャを構成するすべてのアクションの一覧](https://railstutorial.jp/chapters/toy_app?version=5.1#table-demo_RESTful_users)という表が公開されています。

# Usersモデルとは

もう一度`app/controllers/users_controller.rb`、とりわけ`GET /users`に関する部分を見てみましょう。

```ruby:app/controllers/users_controller.rb
  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end
```

`@users = User.all`というのは、「Userモデルからすべてのユーザーの一覧を取り出す」という意味の記述です。というわけで、今度はUserモデルの定義である`app/models/user.rb`を見てみましょう。

```ruby:app/models/user.rb
class User < ApplicationRecord
end
```

「`ApplicationRecord`というクラスを継承している」ということ以外何も書かれていない、驚くほどシンプルな定義ですね。`ApplicationRecord`の定義は、`app/models/application_record.rb`に書かれています。

```ruby:app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end
```

「`ActiveRecord`というクラスを継承している」という記述に到達しました。`ActiveRecord`クラスに関して記述された文書としては、最初に見つかったものでは@penguin_noteさんの[Rails: ActiveRecord::Baseメソッドのまとめ - Qiita](https://qiita.com/penguin_note/items/adb0b9bf7c13c1b1d44d)があります。

# `index.html.erb`ビュー

Toyアプリケーションの「/」にアクセスした際、`rails server`は以下のようなメッセージを返します。

```
Started GET "/" for 172.17.0.1 at 2019-08-01 07:57:08 +0000
Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#index as HTML
  Rendering users/index.html.erb within layouts/application
  User Load (6.7ms)  SELECT "users".* FROM "users"
  Rendered users/index.html.erb within layouts/application (54.8ms)
Completed 200 OK in 632ms (Views: 472.4ms | ActiveRecord: 29.5ms)
```

今回注目するのは以下の記述です。

* `Rendering users/index.html.erb within layouts/application`
* `Rendered users/index.html.erb within layouts/application`

`users/index.html.erb`というリソース名が出てきますね。`users/index.html.erb`の実体は、`app/views`内で以下のように定義されています。

```erb:app/views/users/index.html.erb
<p id="notice"><%= notice %></p>

<h1>Users</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= user.email %></td>
        <td><%= link_to 'Show', user %></td>
        <td><%= link_to 'Edit', edit_user_path(user) %></td>
        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New User', new_user_path %>
```

内容についての解説はRailsチュートリアルでもなされていません。しかし、以下の事実は、Railsチュートリアルでも言及されている通りに重要です。

* `@users`が、Rubyのインスタンス変数であるということ
  * `@users`が、少なくとも「Usersコントローラと`index.html.erb`ビューで共通に使える」というスコープを持つ変数であるということ
* `@users`が、Usersコントローラ内でも`index.html.erb`ビュー内でも使われているということ
* `@users`が、Usersコントローラと`index.html.erb`ビューで共通のものを指しているということ

# 演習

## 1. 図 2.11を参考にしながら、/users/1/edit というURLにアクセスしたときの振る舞いについて図を書いてみてください。 

前提として、Toyアプリケーションの「/users/1/edit」にアクセスした際、`rails server`は以下のようなメッセージを返します。

```
Started GET "/users/1/edit" for 172.17.0.1 at 2019-08-03 04:24:05 +0000
Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#edit as HTML
  Parameters: {"id"=>"1"}
  User Load (16.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Rendering users/edit.html.erb within layouts/application
  Rendered users/_form.html.erb (29.8ms)
  Rendered users/edit.html.erb within layouts/application (82.8ms)
Completed 200 OK in 531ms (Views: 414.0ms | ActiveRecord: 16.1ms)
```

このときの振る舞いを図にすると、以下のようになるはずです。

![Rails_Tutorial_Sec2 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/ee1ecc41-a659-3959-334a-b3aca7cab01a.png)

特筆すべき点は以下であろうと思います。

* `UsersController#edit`は、`show`, `edit`, `update`,`destroy`の各処理共通で事前に呼ばれるprivateメソッドである`set_user`以外は何もしていない
* モデルとして`User.all`ではなく、`User.find`が呼ばれている
* モデルはビューに`@users`ではなく`@user`を渡している
* `index.html.erb`ではなく、`edit.html.erb`が呼ばれている
* `edit.html.erb`から、さらに`_forms.html.erb`が呼ばれ、`_forms.html.erb`が`edit.html.erb`にHTMLを返している

## 2. 図示した振る舞いを見ながら、Scaffoldで生成されたコードの中でデータベースからユーザー情報を取得しているコードを探してみてください。

`UsersController`でいえば、`User.find`というメソッドが「データベースからユーザー情報を取得しているコード」ということになります。Toyアプリケーションにおける`User`モデルのクラス継承関係は`User < ApplicationRecord < ActiveRecord::Base`というものなので、最終的には`ActiveRecord::Base`というクラスの`find`メソッドが呼ばれることになるはずです。

ActiveRecordそのものの説明は本筋から外れるので割愛します。興味がありましたら、@penguin_noteさんの[Rails: ActiveRecord::Baseメソッドのまとめ - Qiita](https://qiita.com/penguin_note/items/adb0b9bf7c13c1b1d44d)等を読んでみてください。

## 3. ユーザーの情報を編集するページのファイル名は何でしょうか?

`app/views/users/edit.html.erb`ですね。同ファイルは、さらに内部で`app/views/users/_form.html.erb`というビューを呼び出しています。各々ソースは以下のとおりです。

```erb:app/views/users/edit.html.erb
<h1>Editing User</h1>

<%= render 'form', user: @user %>

<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>
```

```erb:app/views/users/_form.html.erb
<%= form_with(model: user, local: true) do |form| %>
  <% if user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name, id: :user_name %>
  </div>

  <div class="field">
    <%= form.label :email %>
    <%= form.text_field :email, id: :user_email %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>
```