これまでのあらすじ
「このRailsチュートリアルが終わったら俺結婚するんだ」その言葉を残して、Railsチュートリアルを更新することは二度となかった。
~HAPPY END
はい、ということでRailsチュートリアル第十二章をやっていきます。前回はアカウント有効化という主にメール周りの機能をいじったりしました。第十二章も第十一章とほぼほぼ同じ内容らしいのでサクッと終わらせていきたいところ。
なにはともあれガワをつくります
$ rails generate controller PasswordResets new edit --no-test-framework $ rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime
今回はテスト生成しないオプションを使うとのこと。オプション系はRailsガイド見てもどこに何かいてるかよくわかんないからrails generate controller --help
で確認するのが手っ取り早いのかもしれない。
追記箇所など | 説明 |
---|---|
config/routes.rb | パスワード再設定用リソースを追加 |
app/views/sessions/new.html.erb | パスワード再設定画面のリンク追加 |
app/views/password_resets/new.html.erb | パスワード再設定画面 |
app/views/password_resets/edit.html.erb | パスワード再設定のフォーム |
app/views/user_mailer/password_reset.text.erb | パスワード再設定のテンプレート (テキスト) |
app/views/user_mailer/password_reset.html.erb | パスワード再設定のテンプレート (HTML) |
app/controllers/password_resets_controller.rb | パスワード再設定のcreateアクション追加 パスワード再設定のeditアクション追加 パスワード再設定のupdateアクション追加 |
app/models/user.rb | パスワード再設定用メソッドを追加 |
app/mailers/user_mailer.rb | パスワード再設定のリンクをメール送信 |
test/mailers/previews/user_mailer_preview.rb | パスワード再設定のプレビューメソッド |
それでこんなん作れます。


テストをやっていきます
最初につくったやつは結合テストで補うということなのでテストを書いていきます。
$ rails generate integration_test password_resets
そんでこう。
# test/integration/password_resets_test.rb require 'test_helper' class PasswordResetsTest < ActionDispatch::IntegrationTest def setup ActionMailer::Base.deliveries.clear @user = users(:michael) end test "パスワードリセット" do get new_password_reset_path assert_template 'password_resets/new' # メールアドレスが無効 post password_resets_path, params: { password_reset: { email: "" } } assert_not flash.empty? assert_template 'password_resets/new' # メールアドレスが有効 post password_resets_path, params: { password_reset: { email: @user.email } } assert_not_equal @user.reset_digest, @user.reload.reset_digest assert_equal 1, ActionMailer::Base.deliveries.size assert_not flash.empty? assert_redirected_to root_url # パスワード再設定フォームのテスト user = assigns(:user) # メールアドレスが無効 get edit_password_reset_path(user.reset_token, email: "") assert_redirected_to root_url # 無効なユーザー user.toggle!(:activated) get edit_password_reset_path(user.reset_token, email: user.email) assert_redirected_to root_url user.toggle!(:activated) # メールアドレスが有効で、トークンが無効 get edit_password_reset_path('wrong token', email: user.email) assert_redirected_to root_url # メールアドレスもトークンも有効 get edit_password_reset_path(user.reset_token, email: user.email) assert_template 'password_resets/edit' assert_select "input[name=email][type=hidden][value=?]", user.email # 無効なパスワードとパスワード確認 patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "barquux" } } assert_select 'div#error_explanation' # パスワードが空 patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "", password_confirmation: "" } } assert_select 'div#error_explanation' # 有効なパスワードとパスワード確認 patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "foobaz" } } assert is_logged_in? assert_not flash.empty? assert_redirected_to user # パスワード再設定が成功したらnil assert_nil user.reload['reset_digest'] end end
すごいごちゃごちゃしてて切り分けたいけどこうするのが一般的なのですかね?
演習の方は以下。
# app/models/user.rb class User < ApplicationRecord attr_accessor :remember_token, :activation_token, :reset_token before_save :downcase_email before_create :create_activation_digest ### 略 ### # パスワード再設定の属性を設定する def create_reset_digest self.reset_token = User.new_token update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now) end end
# test/integration/password_resets_test.rb require 'test_helper' class PasswordResetsTest < ActionDispatch::IntegrationTest def setup ActionMailer::Base.deliveries.clear @user = users(:michael) end test "トークンの期限切れ" do get new_password_reset_path post password_resets_path, params: { password_reset: { email: @user.email } } @user = assigns(:user) @user.update_attribute(:reset_sent_at, 3.hours.ago) patch password_reset_path(@user.reset_token), params: { email: @user.email, user: { password: "foobar", password_confirmation: "foobar" } } assert_response :redirect follow_redirect! assert_match "期限が切れました", response.body end end
大体日本語化させてるせいでassert_match
のヒントがヒントになっていない感じになってしまった。
内容としてはそんな感じ。特に詰まる内容はないと思われ。
第十二章の感想
ほぼ第十一章を踏襲した息抜き回(?)だった。新しい学びがあるとしたら期限切れメソッドの作り方とかでしょうか。
# app/models/user.rb class User < ApplicationRecord ### 略 ### # パスワード再設定の期限が切れている場合はtrueを返す def password_reset_expired? reset_sent_at < 2.hours.ago end end
これ。
そろそろ終わりが見えてきました。次回第十三章なんですが割と内容が長いから前後編にしようかどうか悩み中です。ネット見てて思うんですけど、初心者の学習法「progate→dotinstall→railsチュートリアル」みたいなのがここにきてもよくわかんない。挫折するから二週目、三週目が必要と言うアドバイスもよくわからないんですよね。逆引き用のサンプルとして使うにしてもあのサイトごちゃごちゃしてて見づらいし、うーんって感じです。これを完走したらそこら辺の分からないっていう分からなさが理解できるようになるんでしょうか?
次回へ続く……。