LoginSignup
1
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-08-03

前提

Railsチュートリアル 第2章 - Toyアプリケーション - Usersリソースの挙動を変更するからの続きです。

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

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コントローラとは

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アーキテクチャを構成するすべてのアクションの一覧という表が公開されています。

Usersモデルとは

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

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を見てみましょう。

app/models/user.rb
class User < ApplicationRecord
end

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

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

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

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内で以下のように定義されています。

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

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

  • 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.erbedit.html.erbにHTMLを返している

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

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

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

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

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

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

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

<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>
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 %>
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0