Edited at

Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #6 サインアップ, エラー日本語化編


こんな人におすすめ


  • プログラミング初心者でポートフォリオの作り方が分からない

  • Rails Tutorialをやってみたが理解することが難しい

  • ポートフォリオを作成しながら勉強したい

前回:#5 Model spec, Herokuエラー編

次回:#7 ログイン下準備編


こんなことが分かる


  • サインアップの実装方法

  • form_forとform_withについて

  • エラーメッセージを日本語化する方法

一緒に勉強しよ:bow:


ついに実装、サインアップ

これまで導入やらテストが多かったけど、

いよいよサインアップのあれこれを実装するぞ!


まずはサインアップ画面

Tutorial 7.2 ユーザー登録フォームとの大きな変更点は2つ。


  • labelではなくplaceholderを使用

  • form_forではなくform_withを使用

placeholderを使ったのは好み。

フォームを作成する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;
}



登録失敗/成功時のE2Eテストを書く

Tutorial 7.3.4 失敗時のテストのようにテストをRequest/System specで書く。


bash

$ 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では画面が正しく遷移するか確認している。

コメントアウトが2つある。

1つ目はTutorialの記述によりあえて行なっていない。以下引用。


テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。


2つ目はfollow_redirect!をするとエラーが発生する。

これは直接リダイレクトする操作をブラウザ上に期待できないから。

(詳しくは#8で解説)

assert_templateは現在使用できない。

(おそらくCapybaraの対応が終了した)

代わりにリダイレクト先のdivを検証しよう。


それとマッチャにchangeを使用する際、expectの中身はblockでないといけない。

だからexpect{}ではなくexpect()で書くとこうなる。


bash

expected `User.count` not to have changed, but was not given a block


よくわかる解説↓

rspecでのsubjectの使い方について

使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」


エラーメッセージをrails-i18nで日本語化

これで終わりか!と思ったけどもう一個。

Tutorialではエラーメッセージが英語で出る。

日本人には分かりにくいのでrails-i18nで日本語化を試みる。


Gemfile

+ gem 'rails-i18n'



bash

$ 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


bash

$ 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が欲しい。

これもTutorialの通りに進めよう。

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


lantern_lantern_user_flash.png

よっしゃ!!!


後は本番環境を整えるだけ

後はTutorial 7.5 プロのデプロイ通りに進めるだけ。


config/environments/production.rb

# 中略

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


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



bash

$ touch ./procfile



./Procfile

web: bundle exec puma -C config/puma.rb



bash

$ rails test

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

これで決まりさ!

多分次回から急にむずかしくなるはずー:joy:


追記

[2019年8月16日]

記事改変しました。

前回:#5 Model spec, Herokuエラー編

次回:#7 ログイン下準備編