ユーザー登録に失敗した場合の処理
ユーザー登録に失敗した場合は、renderメソッドを使って新規登録ページに戻り、エラーメッセージを表示する。
renderを使うことで、newアクションの@user = User.newは実行されず、以前送信した内容がフォーム内に保持される。
エラーメッセージはパーシャルを使って分けておくことにする。
フォーム内にはエラーメッセージ用パーシャルへのrenderを書く。
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
各フォームについているform_controlクラスは、bootstrap用である。
エラーメッセージ用のパーシャルは次のようである。
app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
コントローラにまたがって共通で使うパーシャルはsharedディレクトリに入れておく。
ここで、any?メソッドはempty?メソッドの逆であり、要素が一つでもある場合はtrue、ない場合はfalseを返す。
@user.errors.countは、エラーの個数を返す。
pluralizeメソッドは、第一引数に整数を、第二引数に英単語をとり、整数値に応じて英単語を複数化する。
不規則活用にも使える。
>> helper.pluralize(1, "error")
=> "1 error"
>> helper.pluralize(5, "error")
=> "5 errors"
エラーが起こり、newページに戻されると、エラーメッセージが表示される。
また、Railsはfield_with_errorsクラスを持つdivタグによって、フォームのラベルとフォームそのものを囲んでくれる。
これによってcssを使ってエラーを起こしたフォームの枠を赤くしたりできる。
cssは以下のよう。
/* forms */
.
.
.
#error_explanation {
color: red;
ul {
color: red;
margin: 0 0 30px 0;
}
}
.field_with_errors {
@extend .has-error;
.form-control {
color: $state-danger-text;
}
}
has_errorはbootstrapのcssクラスである。
登録エラー時のテスト
新規ユーザー登録用の統合テストを生成する。
$ rails generate integration_test users_signup
登録エラー時のテストを書く。
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_template 'users/new'
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
end
end
①まずsignup_pathにGETリクエストを送信する。行き先はnewアクションのnewビューである。
②newビューが表示されていることを確認する。
③assert_no_differenceを使って、ユーザー登録操作の前後でユーザー数が変わらないことを確認する。ユーザー登録が失敗すればユーザー数は変わらないはずである。
④users_pathにPOSTリクエストを送信する。postメソッドの第二引数にはparamsハッシュを指定する。
(paramsは変数でありハッシュでもあるらしい...よく分からない)
⑤ユーザー登録が失敗し、renderメソッドによってnewページに戻っていることを確認する。
必要であれば
⑥エラーメッセージが表示されていることを確認する。
assert_select 'div#<CSS id for error explanation>'
assert_select 'div.<CSS class for field with error>'
URLの修正
登録エラーでnewページに戻ると、URLが/signupから/usersに変わる。
これはusersリソースのRESTfulなルーティングによるもの。
フォームの送信は/usersのPOSTリクエストに送られて、createアクションに到達する。
その後登録エラーでnewページに戻るが、リダイレクトではなく再読み込みしていないので、URLはPOSTリクエストがされた時のままになる。
(という認識であってるかな?)
これを修正するためにルーティングを変更する。
URLが/signupのPOSTリクエストを作る(行き先はcreateアクション)。
Rails.application.routes.draw do
.
.
.
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
resources :users
end
フォームの送信先もこのルーティングに変更する。
<%= form_for(@user, url: signup_path) do |f| %>
.
.
.
<% end %>
テストのPOSTリクエスト部分を修正する。また、form_forから生成されるformタグのaction属性(送信先のURLを値にとる)が正しいURLになっていることを確認する。
test "invalid signup information" do
get signup_path
assert_template 'users/new'
assert_no_difference 'User.count' do
post signup_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'form[action="/signup"]'
end