Ruby on Railsチュートリアル第6版 第12章まとめ

Ruby on Railsチュートリアル第6版の第12章のまとめです。

個人的に気になったところ、難しかったところ、わからなかったところを中心にまとめていきます。基本的に自分の理解を助けることと、復習しやすくすることを目的とした記事ですが、今Ruby on Railsチュートリアルをやってる人に役立つ情報があるかもしれません。

僕の学習の順番は、1周目はRailsチュートリアル第4版(Rails5.1)を使い、2周目で第6版を使うという感じになってます。これは1周目が終わったところで第6版のWebテキスト版がリリースされたからです。

ちなみに、2周目は解説動画を購入してやってます。オススメです。

第12章 パスワードの再設定

第12章 パスワードの再設定

第12章ではパスワードを忘れたときに再設定できるようにします。第9章、第11章と同じような実装になるので、いまいち理解できてなかったら、復習してから第12章に取り掛かるというのも1つの手かなと思います。

12.1 PasswordResetsリソース

パスワードの再設定はPasswordResetsリソースを作って実装していきます。

いつものように、ブランチを作って進みます。

12.1.1 PasswordResetsコントローラ

PasswordResetsコントローラを作りますが、今回はコントローラの単体テストを統合テストでカバーしてくため、テストを生成しないようにします。そのために、新しいオプションが出てきます。

パスワードの再設定で使う4つのリソース(new、create、edit、update)に対応したルーティングを設定します。名前付きルートをちゃんと覚えてないので、これから厳しくなりそうだと感じました。

Railsチュートリアル後にオリジナルアプリケーションを作るときは、ディスプレイの横にでも表を貼っとこうかなと考えてます。

ここのところは特に問題ないと思います。

12.1.2 新しいパスワードの設定

パスワードの再設定でもUserモデルに新しい属性(データベースのカラム)を追加するので、ついでに第11章でのミスを修正しました。

第11章でのミスは、activated_at属性のデータ型の指定で「dattetime」になってたことです。その前にschema.rbを確認してみたところ、データ型はtimeになってたみたいです。

create_table "users", force: :cascade do |t|
  t.string "name"
  t.string "email"
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.string "password_digest"
  t.string "remember_digest"
  t.boolean "admin", default: false
  t.string "activation_digest"
  t.boolean "activated", default: false
  t.time "activated_at"
  t.index ["email"], name: "index_users_on_email", unique: true
end

(db/schema.rb)

マイグレーションファイルを直して、マイグレーションをやり直しました。

rails db:migrate:reset

rails db:migrate:resetできなかったのでrails db:resetした」によると、このコマンドはデータベースを削除して、マイグレーションを実行するというもののようです。

更新されたschema.rbが下になります。

create_table "users", force: :cascade do |t|
  t.string "name"
  t.string "email"
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.string "password_digest"
  t.string "remember_digest"
  t.boolean "admin", default: false
  t.string "activation_digest"
  t.boolean "activated", default: false
  t.datetime "activated_at"
  t.string "reset_digest"
  t.datetime "reset_sent_at"
  t.index ["email"], name: "index_users_on_email", unique: true
end

(db/schema.rb)

「db:seed」もやってサンプルユーザーも入れておきました。

パスワード再設定用(というより、再設定申請用?)のビューを作りますが、ここでform_withメソッドが出てきます。その書き方は第8章のところでまとめました。

演習の答えにも関連してくるので詳しくは書きませんが、ユーザー登録フォームと書き方が違うので注意が必要です。

12.1.3 createアクションでパスワード再設定

PasswordResetsコントローラのcreateアクションを書いていきます。

コードは第11章で出てきたものに似てるので、第11章が理解できてれば大丈夫だと思います。どこに書かれてるかが違いますが。

ここで完全に流れを理解しようとすると、

def send_password_reset_email
  UserMailer.password_reset(self).deliver_now
end

(app/models/user.rb)

で止まると思います。password_rest(user)メソッドがまだ実装されてないからです。これは次で実装します。

12.2 パスワード再設定のメール送信

アカウントの有効化でもやったメール送信です。

12.2.1 パスワード再設定のメールとテンプレート

アカウントの有効化と同じように、メール送信の設定とテンプレートを作ります。

ここでpassword_reset(user)メソッドが出てくるので、先に書いたコードを合わせて読み解くことができるようになります。

12.2.2 送信メールのテスト

ここもアカウントの有効化と同じような感じなので、大丈夫だと思います。

12.3 パスワードを再設定する

メール送信部分が完成したので、次はパスワードの再設定を実装します。

12.3.1 editアクションで再設定

editアクションは編集フォームに関するアクションです。まずは編集フォームを作ります。

ここでは、form_withメソッドの引数としてmodelが使われてます。ユーザー情報の更新なので、Userモデルが使えるからです。

hidden_field_tagを使って隠しフィールド(フォームには表示されないフィールド)を設定します。使い方は他のfield_tagと同じ感じですね。

次にeditアクションに移りますが、updateアクションと同じ処理をするため、処理をメソッド化して、beforeフィルターを使ってeditアクションが実行される前にそのメソッドが実行されるようにします。

メソッド自体はそんなに難しくないと思いますが、unlessが混乱要因ですよね。

unless A && B, unless A || Bの理解の仕方」によると、

unless A && B

これはAとBがfalseの場合だけtrueになるそうです。どちらかでもtrueであればfalseを返します。

なので、

unless (@user &&
        @user.activated? &&
        @user.authenticated?(:reset, params[:id]))
  redirect_to root_url
end

(app/controllers/password_resets_controller.rb)

これは1つでもfalseがあればunless内のコード(redirect_to)が実行されることになります。逆に言えば、すべてがtrueの場合だけunless内のコード(redirect_to)が実行されないということです。

  1. @userが存在してる
  2. @userが有効化されてる
  3. @userが認証された

この3つの条件をすべて満たすかどうかをチェックしてるメソッドということですね。

それがeditアクションの前に実行され、1つでも条件を満たさなかったらroot_urlにリダイレクトされ、すべて満たしたらeditアクションのデフォルトrender(editビュー)が実行されます。

12.3.2 パスワードを更新する

パスワードを更新するためにupdateアクションを作ります。

ここはややこしいですね。その原因の1つが英語にあります。メソッド名にexprireが出てきますが、この意味がわかるとちょっとわかりやすくなります。

パスワード再設定には期限を付けるわけですが、「有効期限内か?」という評価をしたくなります。でも、ここでは「有効期限が切れてないか?」の評価をします。

で、expireですが、「有効期限が切れる」という意味があります。

なので、check_expirationメソッドは「有効期限切れのチェック」で、password_reset_exprired?メソッドは「有効期限が切れてないか?」のチェックというわけです。

あとはaddメソッドですね。これは、

addメソッドを使って、特定の属性に関連するエラーメッセージを手動で追加できます。このメソッドは属性とエラーメッセージを引数として受け取ります。

7.3 errors.add – Active Record バリデーション – Railsガイドv6.0

というものです。メソッド名からもわかるようにエラーメッセージを追加するメソッドみたいですね。

気になるのは引数です。「:password」と「:blank」の2つを渡すだけで、「Password can’t be blank」と表示されます。引数がシンボルになってるので、何かしらのデフォルト処理があるんだと思います。

ということで、シンボルと文字列の組み合わせを試してみました。

@user.errors.add(:password, "blank")

この場合は「Password blank」と表示されました。

@user.errors.add("password", :blank)

こうすると「Password can’t be blank」になりました。

@user.errors.add("password", "blank")

これだと「Password blank」です。

ここからわかることは、第2引数を「:blank」にすると自動的に「can’t be blank」と表示してくれるということですね。第1引数は文字列でもシンボルでも頭文字を大文字にして表示してくれるという感じです。

ちなみに、引数を「:blank」だけにしたらエラーになりました。

ここにきて、「updateメソッドって何だっけ?」という恥ずかしい疑問が出てきました。ちょっと混乱気味ですね。

@user.update(user_params)

あらためて調べたらデータベースを更新するメソッドでした。

最後の調べ物は、

def update
  if params[:user][:password].empty?
    @user.errors.add(:password, :blank)
    render 'edit'
  elsif @user.update(user_params)
    log_in @user
    flash[:success] = "Password has been reset."
    redirect_to @user
  else
    render 'edit'
  end
end

(app/controllers/password_resets_controller.rb)

これのelseのところです。

updateアクションは、パスワードが空だったときの処理、更新ができたときの処理、それ以外の処理、という順番でコードが書かれてます。

それ以外の処理、つまりelse内のコードは、

無効なパスワードであれば失敗させる (失敗した理由も表示する)

Ruby on Railsチュートリアル第6版

これへの対応となってます。パスワードを1文字で試してみたら、短すぎると言われたので、これはバリデーションに引っかかってると気づきました。Userモデルで設定したバリデーションですね。

あとはuser_paramsメソッドですが、第10章でも出てきたので大丈夫だと思います。

時間比較のところは自分が覚えやすいように理解すればいいかなと思います。僕は数直線的なイメージで、時間経過するほど数字が大きくなる直線上に比較対象の時間を並べる感じの理解をしました。

12.3.3 パスワードの再設定をテストする

パスワード再設定のテストを書きます。

今回はすごく長いテストですが、丁寧に読んでいけば何をやってるかはわかると思います。

ただ、これを何も見ずに書けと言われたらかなり苦労しそうです。まずは何をテストする必要があるかのリストアップとか、そういう感じのことをやらないと難しそうです。

このテストでHTTPリクエストが何度も出てきますが、名前付きルートをちゃんと覚えてなくて、ミスに気づけないという事態に遭遇しました。ちゃんと覚えないとダメですね。

12.4 本番環境でのメール送信(再掲)

本番環境のメール送信の設定は第11章でやってあれば必要ありません。

ということで、いつものように、コミット、マージ、プッシュ、デプロイです。

これで完成と思ったら、また本番環境のメール送信で止まりました。ログを見てみたらまた凍結されてる様子。ということで、第11章に書いたのと同じ方法で解除しました。

参考にしたのは「Railsチュートリアル11章 SendGrid 凍結問題を解決する」です。

まとめ

第12章はこれまで学んできたことを使って実装してく感じという印象です。理解できてればそこまで難しくないけど、理解してなかったらかなり難しいということになりそうです。

unlessの使い方はおそらくまたわからなくなるはずなので、何度も見返しながら自然と使えるようになって行けたらなと思ってます。

第12章で出てきたテストの長さには驚きました。コピペしたくなるほどです。

Railsチュートリアルをコピペで進める人もいるみたいですが、僕はちゃんとコードを書いた方がいいと思って、HTMLとかCSSとかのRuby on Railsと直接関係ないところ以外は全部書いてます。

HTMLに埋め込まれたRubyはケースバイケースにしてますが。

第13章はマイクロポストの実装。それっぽい感じになってくるので楽しみな反面、1周目の苦労を思い出して憂うつな感じもあります。

2周目でどれだけ理解できるようになってるか確認しながら進めようと思います。

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
SNS 開発を題材にした大型チュートリアル。プロダクト開発の 0→1 を創りながら学びます。電子書籍や解説動画、質問対応、法人向けサービスも提供しています。