Railsチュートリアル7章まとめ。RESTモデルとユーザ登録画面フォーム作成とテスト

Railsチュートリアル 7章を終えました。
6章ではユーザモデルを作り、7章ではユーザ登録画面を作成していきます。
その要約です

railstutorial.jp

リソース

ProgateのRailsコース内ではルーティングの際は基本的には
get '/help', to: 'static_pages#help'
のように、URLを指定した際に、コントローラのクラスおよびメソッドを指定して、ビューも同様にフォルダと表示するテンプレートを指定しました。

ここでルーティングにresources :usersと追加すると自動的に下記が追加されます。

HTTPリクエス URL アクション 名前付きルート 用途
GET /users index users_path すべてのユーザーを一覧するページ
GET /users/1 show user_path(user) 特定のユーザーを表示するページ
GET /users/new new new_user_path ユーザーを新規作成するページ (ユーザー登録)
POST /users create users_path ユーザーを作成するアクション
GET /users/1/edit edit edit_user_path(user) id=1のユーザーを編集するページ
PATCH /users/1 update user_path(user) ユーザーを更新するアクション
DELETE users/1 destroy user_path(user) ユーザーを削除するアクション

Railsチュートリアル表 7.1: リスト 7.3のUsersリソースが提供するRESTfulなルートより


例えば、HTTPリエクストのGETメソッドでURLが/usersであれば、indexアクションと結びつきます。
また、テストコードの中で'get users_path'と記載すれば、indexアクションを呼べる。といった具合にアクションをレイアウトのなかで使用することができるようになります。


また、$ rails generate scaffold Usersのようにscaffoldでプロジェクトを生成した際も自動でresources :usersとルーティングが生成されます。

(progateからアプリを作った際にresourcesがわからなかった)

REST

REST(REpresentational State Transfer)とはRailsチュートリアルでは

アプリケーションを構成するコンポーネント (ユーザーやマイクロポストなど) を「リソース」としてモデル化することを指します。これらのリソースは、リレーショナルデータベースの作成/取得/更新/削除 (Create/Read/Update/Delete: CRUD) 操作と、4つの基本的なHTTP requestメソッド (POST/GET/PATCH/DELETE) の両方に対応しています。

要約すると、データベース操作もできるし、データベース操作とHTTP requestメソッドを対応させているデータ群のようです。
例えばデータベース作成(Create)とHTTPメソッド(POST)を対応させる。ということのようですね。

表 7.1のようなデータ群を一気に生成するためにresources :usersというのを使えばいいです。

フォーム

ユーザ登録フォーム
ユーザ登録フォーム

上記のようなユーザ登録フォームを作成するためにRailsではform_forヘルパーメソッドが用意されています。

Railsでこんな感じでフォームを書いていきます

        <%= form_for(@user, url: signup_path) 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 %>

実際にアクセスしてhtmlで出力するとこうなります(hidden項目は削除しています)

        <form class="new_user" id="new_user" action="/signup" accept-charset="UTF-8" method="post">
        

        <label for="user_name">Name</label>
        <input class="form-control" type="text" name="user[name]" id="user_name" />

        <label for="user_email">Email</label>
        <input class="form-control" type="email" name="user[email]" id="user_email" />

        <label for="user_password">Password</label>
        <input class="form-control" type="password" name="user[password]" id="user_password" />

        <label for="user_password_confirmation">Confirmation</label>
        <input class="form-control" type="password" name="user[password_confirmation]" id="user_password_confirmation" />

        <input type="submit" name="commit" value="Create my account" class="btn btn-primary" data-disable-with="Create my account" />
</form>

簡易解説

ここで重要なのはラベルは<%= f.label :name %>とすればhtmlで<label for="user_name">Name</label>ラベルと中身が作成できます。

テキスト入力については<%= f.text_field :name %><input id="user_name" name="user[name]" type="text" />とtypeが自動で指定されます。
これは<%= f.email_field :email %>でも同様です。
パスワード入力についても自動でtypeを指定します

<%= f.password_field :password %><input id="user_password" name="user[password]" type="password" />

で、もうお気づきかもですが、htmlのname属性がどれもuser[(指定したfield)]となっています。

これはfor文<%= form_for(@user) do |f| %>で引数に@userとすることでrailsはuser[]とnameを指定してくれます。
@userについてはコントローラで作成します。

さらにhtml上部で<form class="new_user" id="new_user" action="/signup" accept-charset="UTF-8" method="post">

とactionとmethodを指定しているのでボタン押下時にpost '/signup'とルーティングを指定してあげると、指定したメソッドが動きます

@userをフォームの引数に指定してあげることで、あとはRailsが@userについてあとはよろしくやってくれます

エラーメッセージ

フォーム画面の中に<%= render 'shared/error_messages' %>という、エラーメッセージ表示箇所があります。
この中ではエラーが発生した際はエラーを編集して表示する画面になります。

<% 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 %>

この中でエラーメッセージ定義箇所、<%= pluralize(@user.errors.count, "error") %>エラーカウントの数によって第二引数のerrorがなんと単数形か複数形に勝手になってくれます。

このpluralizeメソッドは複数形の場合に単にsをつけるだけでなく、例えば<%= pluralize(5, "woman") %>とすると
"5 women"という具合に返してもくれます。すげぇ

Strong Parameters

フォーム送信時にparams[;user]には入力したnameやemail等が入っています

ここで登録時に@user = User.new(params[:user]) とするとセキュリティ上の観点からエラーとなります
params[:user] の中にadmin属性などがある場合、curlコマンドをつかって
POST時にそれをtrueにすることが容易となり、アドミン権限を簡単に取得できるためです


ここでは、paramsハッシュでは:user属性を必須とし、名前、メールアドレス、パスワード、パスワードの確認の属性をそれぞれ許可し、それ以外を許可しないようにしたいと考えています。


そのためにはコントローラ層でStrong Parametersというテクニックを使うことが推奨されています。Strong Parametersを使うことで、必須のパラメータと許可されたパラメータを指定することができます。下記が例です。

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

flash

ユーザ登録を行なったりして、次画面に遷移した時に成功した旨のメッセージを出したい。
っといたように遷移した時だけメッセージを出して2回目以降のページ読み込みの際にメッセージを出さない時は
flashという変数が使えます。

具体的にはユーザ登録が終わると下記のような"Welcome to the Sample App!"というメッセージが一回だけでます

flash
flash

実装方法についてはコントローラで
flash[:success] = "Welcome to the Sample App!"
と定義することで、view側でflashをハッシュとして使用でき、
<% flash.each do |message_type, message| %>
とするとmessage_typeにはsuccess、messageには"Welcome to the Sample App!"が格納されますので、それを使用することができます。

テスト

フォーム画面の実装が済んだらテストを行います。入力テストの際はRailsにはフォーム用のテストを書くことができます。
これにより手でいちいち画面を入力せずに自動でテストが行えるようになります。

テスト作成

新規ユーザー登録用の統合テストを生成するところから始めていきます。コントローラーの慣習である「リソース名は複数形」に因んで、統合テストのファイル名はusers_signupとします。

$ rails generate integration_test users_signup

登録失敗時のテスト

まずはユーザ登録失敗時のテストを行うことにします。ここでは、失敗する入力を行い前後でユーザ数が変わっていないことを見ます

で具体的にはまず

①ユーザ登録画面にアクセスしget signup_path
②formが存在することassert_select 'form[action="/signup"]'
③失敗するデータを送信しその前後でユーザ数が変わらないことassert_no_difference 'User.count'
④失敗した時の画面遷移先が正しいことassert_template 'users/new'
⑤失敗した旨のメッセージが出ることassert_select "div#error_explanation"

などとすればいいかと思います

  test "invalid signup information" do
    get signup_path
    assert_select 'form[action="/signup"]'
    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'
    assert_select "body" do
      assert_select "div#error_explanation"
   end
end

あとは$ rails testで動かしてあげればいいです。

成功時のテスト

次にユーザ登録時の成功時のテストです。

①ユーザ登録画面にアクセスしget signup_path
③ユーザ登録に成功するデータを送信しその前後でユーザ数が変わること assert_difference 'User.count', 1 do
④次画面に遷移すること
flashが表示すること

  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    follow_redirect!
    assert_template 'users/show'
    #flash有無検査。empty?を使う
    assert_not flash.empty?
  end

ここでfollow_redirect!というのが出ていますが、このメソッドは、POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッドです。

あとは$ rails testで動かしてあげればいいです。

参照

この記事を書く時にの参照先です。ありがとうございます。 www.masalog.site