【Railsチュートリアル】第7章 ユーザー登録③

#7.3 ユーザー登録失敗
この節では…

* 無効なデータ送信を受け付けるユーザー登録フォームを作成する。
* ユーザー登録フォームを更新してエラーの一覧を表示する。

##7.3.1 正しいフォーム
1. createアクションでフォーム送信を受け取り
2. User.newを使って新しいユーザーオブジェクトを作成
3. ユーザーを保存（または保存に失敗）
4. 再度の送信用のユーザー登録ページを表示する


```
@user = User.new(params[:user])

#上のコードは下とほぼ同じ

@user = User.new(name: "Foo Bar", email: "foo@invalid", password: "foo", password_confirmation: "bar")
```

##7.3.2 Strong Parameters
paramsハッシュ全体を初期化するという行為はセキュリティ上、極めて危険。
なぜなら、Webサイトの管理者権限を奪い取ることができてしまうから。
Rails 4.0ではコントローラ層でStrong Parametersというテクニックを使うことで、必須のパラメータと許可されたパラメータを指定することができ、さらに、paramsハッシュをまるごと渡すとエラーが発生するようになる。

<br>
:user属性を必須とする。= require(:user)
名前、メールアドレス、パスワード、パスワードの確認の属性をそれぞれ許可する。= permit(:name, :email, :password, :password_confirmation)
```
params.require(:user).permit(:name, :email, :password, :password_confirmation)
```

```
# params[:user]ではなく、user_paramsという外部メソッドを使うのが慣習になっている。
# @user = User.new(params[:user]) 
@user = User.new(user_params)
```

### 演習 1
/signup?admin=1 にアクセスし、paramsの中にadmin属性が含まれていることをデバッグ情報から確認してみましょう。

```デバッグ情報
--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
  admin: '1'
  controller: users
  action: new
permitted: false
```

##7.3.3 エラーメッセージ
「問題が生じたためにユーザー登録が行われなかった」ということをユーザーにわかりやすく伝えるエラーメッセージを追加する。

```
<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      # @user.errorsのエラーの数を返せ。エラーが複数の場合は複数形にせよ（pluralizeヘルパーの機能）
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    # @user.errorsのエラーメッセージを1つずつmsgに代入せよ
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>
```

### 演習 1
最小文字数を5に変更すると、エラーメッセージも自動的に更新されることを確かめてみましょう。

```ruby:app/models/user.rb
class User < ApplicationRecord
  (中略)
  validates :password, presence: true, length: { minimum:5 }
end
```

### 演習 2
未送信のユーザー登録フォーム（図 7.13）のURLと、送信済みのユーザー登録フォーム（図 7.19）のURLを比べてみましょう。なぜURLは違っているのでしょうか? 考えてみてください。

```ruby:config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'
  get  '/help',    to: 'static_pages#help'
  get  '/about',   to: 'static_pages#about'
  get  '/contact', to: 'static_pages#contact'
  get '/signup',   to: 'users#new'
  resources :users
end
```

「未送信のユーザー登録フォームのURL」は`〜/signup`
`routes.rb`の`get '/signup'`から`new`アクションが実行されている。

「送信済みのユーザー登録フォームのURL」は`〜/users`
`routes.rb`の`resources :users`から`create`アクションが実行されている。

##7.3.4 失敗時のテスト
無効な送信をしたときの正しい振る舞いについてテストを書いていく。
新規ユーザー登録用の統合テストを生成。

```ruby:user_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path # getメソッドを使ってユーザー登録ページにアクセス
    assert_no_difference 'User.count' do # ユーザ数が変わらないかどうかを検証するテスト
      post users_path, params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    # 送信に失敗したときにnewアクションが再描画されるか検証するテスト
    assert_template 'users/new'
  end
end
```

###演習 1
リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。

```ruby:user_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path # getメソッドを使ってユーザー登録ページにアクセス
    assert_no_difference 'User.count' do # ユーザ数が変わらないかどうかを検証するテスト
      post users_path, params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    # 送信に失敗したときにnewアクションが再描画されるか検証するテスト
    assert_template 'users/new'
    # shared/error_messages
    # <div id="error_explanation">があるか検証するテスト
    assert_select 'div#error_explanation'
    # <div class="alert-danger">があるか検証するテスト
    assert_select 'div.alert-danger'
  end
end
```