Help us understand the problem. What is going on with this article?

Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #6 ユーザー登録画面, エラー日本語化編

こんな人におすすめ

  • プログラミング初心者でポートフォリオの作り方が分からない
  • Rails Tutorialをやってみたが理解することが難しい

前回:#5 Userモデル編
次回:#7 ログイン機能の下準備編

今回の流れ

  1. 今回のゴールを把握する
  2. ユーザー登録画面のビューを作る
  3. フォームのエラーを日本語化する
  4. flashを作る
  5. Usersコントローラーを作る
  6. ユーザー登録画面のテストを作る
  7. 本番環境を整える

※ この記事は、ポートフォリオを作る理由をweb系自社開発企業に転職するためとします。
※ 2020年4月1日、記事を大幅に更新しました。

今回のゴールを把握する

ゴールは、ユーザー登録画面を完成させることです。
対応するURLとUsersコントローラーのアクション、役割は以下の通りです。

URL アクション 役割
/signup new フォーム画面を表示する
/users create フォーム情報を送信する
/users/:id show ユーザーページを表示する

まずは、ユーザー登録画面のビューを作ります。
その際、フォームに不備がある場合のエラーも作ります。
続いて、フォームのエラーを日本語化します。
続いて、初めてのユーザーに登録完了を伝えるflashを作ります。
続いて、Usersコントローラーを作ります。
続いて、ユーザー登録失敗/成功時のテストを作ります。
最後に、デプロイを行うために本番環境を整えます。

以上です。

ユーザー登録画面のビューを作る

ユーザー登録画面のビューを作ります。
フォームに不備がある場合のエラーも作ります。
Rails Tutorial 7.2 ユーザー登録フォームとの変更点は以下の2つです。

  • labelではなくplaceholderを使う
  • form_forではなくform_withを使う

フォームを作成するform_forは非推奨になりました。
代わりにform_withを使います。

app/views/users/new.html.erb
<% provide(:title, '新規ユーザ作成') %>
<div class="container signup-container">
  <div class="row">
    <div class="col">
      <div class="signup-logo-img">
        <%= link_to image_tag('lantern_lantern_logo.png', width: 100), root_path, class: "logo-img" %>
      </div>
      <h1 class="signup-title">新規ユーザ作成</h1>
      <%= form_with(model: @user, url: signup_path, local: true) do |form| %>
        <%= render 'shared/error_messages' %>

        <div class="form-group">
          <%= form.text_field :name, class: 'form-control', placeholder: "名前" %>
        </div>

        <div class="form-group">
          <%= form.email_field :email, class: 'form-control', placeholder: "メールアドレス" %>
        </div>

        <div class="form-group">
          <%= form.password_field :password, class: 'form-control', placeholder: "パスワード" %>
        </div>

        <div class="form-group">
          <%= form.password_field :password_confirmation, class: 'form-control', placeholder: "パスワード(再入力)" %>
        </div>

        <div class="form-group">
          <%= form.submit "新規ユーザ作成", class: "btn btn-info btn-lg form-submit" %>
        </div>
      <% end %>
    </div>
  </div>
</div>
app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger alert-form-extend" role="alert">
      <%= @user.errors.count %>個のエラーがあります
    </div>
    <ul>
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>
app/assets/stylesheets/application.scss
// 中略
// signup

.signup-container {
  width: 100%;
  max-width: 330px;
  margin: auto;
  padding-top: 1rem !important;
}

.signup-logo-img {
  text-align: center;
}

.signup-title {
  text-align: center;
  font-weight: normal;
  font-size: 1.5rem;
  margin-bottom: 2rem;
}

.form-control {
  margin-bottom: 0;
}

.form-submit {
  width: 100%;
  margin-top: 1rem;
}

#error_explanation {
  color: red;
  ul {
    color: red;
    padding-bottom: 0;
  }
}

.field_with_errors .form-control {
  border-color: red;
}

.alert-form-extend {
  margin: 1.5rem 0 1rem;
}

フォームのエラーを日本語化する

フォームのエラーを日本語化します。
日本語化には、rails-i18nを使います。

Gemfile
+ gem 'rails-i18n'
shell
$ bundle install

続いて、設定を加えます。
特別に、理解する必要はありません。

config/application.rb
# 中略
module LanternApp
  class Application < Rails::Application
# 中略
# 以下2行を追加
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end
shell
$ mkdir config/locales/models
$ touch config/locales/models/ja.yml
locales/models/ja.yml
ja:
  activerecord:
    models:
      user: ユーザ
    attributes:
      user:
        name: 名前
        email: メールアドレス
        password: パスワード
        password_confirmation: パスワード(再入力)

モデル名と属性名に、それぞれ対応する日本語を与えます。
これにより、日本語化が可能になります。

最後に、サーバーを再起動します。
メッセージが日本語になりました。
lantern_lantern_signup_error.png
(唐突なスマホ画面をお許しください。)

参考になりました↓
[初学者]Railsのi18nによる日本語化対応
Railsのバリデーションエラーのメッセージの日本語化

flashを作る

初めてのユーザーに登録完了を伝えるflashを作ります。
Rails Tutorialではflashの実装にcontent_tagを使っています。
しかしRails 5.1からはtag(:br)よりtag.brを推奨するので、こちらを使います。

app/views/layouts/application.html.erb
<!-- 中略 -->
<body>
  <%= render 'layouts/header' %>
  <% flash.each do |message_type, message| %>
    <%= tag.div message, class: "alert alert-#{message_type}" %>
  <% end %>
  <%= yield %>
  <%= render 'layouts/footer' %>
  <%= debug(params) if Rails.env.development? %>
</body>

Usersコントローラーを作る

Usersコントローラーを作ります。
newとcreateアクションを編集します。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Lantern Lanternの世界へようこそ"
      redirect_to @user
    else
      render 'new'
    end
  end

  def show
    @user = User.find(params[:id])
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password, :password_confirmation)
    end
end

各アクションで各インスタンス変数@userを用意します。
@userの役割は、以下の通りです。

  • new → Userモデルを作る受け皿を作る
  • create → フォームの情報からUserモデルを作る
  • show → フォームから送信されたパラメーターからユーザーを特定する

セキュリティ上、フォームに余計な情報が送信される可能性があります。
そこで、user_paramsメソッドを作ります。
このメソッドは、フォームから受け取る情報をUserモデルの属性に限定します。
それにより、セキュリティ面を向上させます。

ユーザー登録が成功するとこのような画面になります。
lantern_lantern_user_flash.png

本番環境を整える

デプロイを行うために本番環境を整えます。
ここはTutorial 7.5 プロのデプロイ通りに進めます。

具体的にはアプリケーションサーバーにPumaを使うよう、設定しています。
サーバーに関しては#18で学習するので、現状理解しなくて大丈夫です。

まず、SSLを有効にします。

config/environments/production.rb
# 中略
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
# 中略

次に、Pumaを設定します。

config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count

preload_app!

rackup      DefaultRackup
port        ENV['PORT']     || 3000
environment ENV['RACK_ENV'] || 'development'

on_worker_boot do
  # Worker specific setup for Rails 4.1+
  # See: https://devcenter.heroku.com/articles/
  # deploying-rails-applications-with-the-puma-web-server#on-worker-boot
  ActiveRecord::Base.establish_connection
end

最後に、Herokuで使うアプリケーションサーバーをPumaに設定します。
その設定に、Procfileを使います。

shell
$ touch ./procfile
./Procfile
web: bundle exec puma -C config/puma.rb

それを終えたら、commitとpushを行います。

shell
$ git add -A
$ git commit -m "Use SSL and the Puma webserver in production"
$ git push
$ git push heroku
$ heroku run rails db:migrate

無事に本番環境が動いていたら成功です。

参考になりました↓
Herokuに必要なProcfileの書き方についてまとめておく

ユーザー登録画面のテストを作る

ユーザー登録失敗/成功時のテストを作ります。
Rails Tutorial 7.3.4 失敗時のテストを参考に、Request / System specで書きます。

shell
$ rails g rspec:request users_signup
$ touch spec/systems/users_signup_spec.rb
spec/requests/users_signups_spec.rb
require 'rails_helper'

RSpec.describe "UsersSignups", type: :request do
  describe "GET /signup" do
    it "is invalid signup information" do
      get signup_path
      expect {
        post signup_path, params: {
          user: {
            name: "",
            email: "user@invalid",
            password: "foo",
            password_confirmation: "bar"
          }
        }
      }.not_to change(User, :count)
    end

    it "is valid signup information" do
      get signup_path
      expect {
        post signup_path, params: {
          user: {
            name: "Example User",
            email: "user@example.com",
            password: "password",
            password_confirmation: "password"
          }
        }
      }.to change(User, :count).by(1)
    end
  end
end
spec/systems/users_signup_spec.rb
require 'rails_helper'

RSpec.describe "UsersSignups", type: :system do

  it "is invalid because it has no name" do
    visit signup_path
    fill_in '名前', with: ''
    fill_in 'メールアドレス', with: 'user@invalid'
    fill_in 'パスワード', with: 'foo'
    fill_in 'パスワード(再入力)', with: 'bar'
    click_on '新規ユーザ作成'
    expect(current_path).to eq signup_path
    expect(page).to have_selector '#error_explanation'
    # expect(page).to have_selector 'li', text: '名前を入力してください'
  end

  it "is valid because it fulfils condition of input" do
    visit signup_path
    fill_in '名前', with: 'Example User'
    fill_in 'メールアドレス', with: 'user@example.com'
    fill_in 'パスワード', with: 'password'
    fill_in 'パスワード(再入力)', with: 'password'
    click_on '新規ユーザ作成'
    # follow_redirect!
    expect(current_path).to eq user_path(1)
    expect(page).not_to have_selector '#error_explanation'
  end
end

Request specでは、もっぱらuserが追加されたかを確認します。
System specでは、画面が正しく遷移するか確認します。

assert_templateは現在使用できないので、リダイレクト先のdivを検証します。
マッチャにchangeを使用する際、expectの中身はblockである必要があります。
よって、expect()ではなく、expect{}で書きます。

コメントアウトを載せている理由は、以下の通りです。

  • 1つ目:テキストに対するテストは壊れやすい
    テキストはよく変更することがあるので、テストは壊れやすいです。この場合、flashが空でないかをテストするだけに留めます。

  • 2つ目:follow_redirect!をするとエラーが発生する
    直接リダイレクトする操作はブラウザ上で期待できないために、エラーが発生します(詳しくは#8で解説します)。

参考になりました↓
rspecでのsubjectの使い方について
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」

今回は以上です。


前回:#5 Userモデル編
次回:#7 ログイン機能の下準備編

aokyo17
rails tutorial → ポートフォリオing. 誰もが経験した初心びくびく20代1年目.. フォローはすぐ返したい厨。
https://komucha.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away