紙一重の積み重ね

アラフォーのエンジニアがなれる最高の自分を目指して、学んだことをこつこつ情報発信するブログです。

【12章】Ruby on Railsチュートリアル演習まとめ&解答例【12.2 パスワード再設定のメール送信】

はじめに

Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版の 12章 12.2 パスワード再設定のメール送信の演習まとめ&回答例です。

個人の解答例なので、誤りがあればご指摘ください。

動作環境

  • cloud9
  • ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
  • Rails 5.0.0.1

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

本章での学び

【Mailer】Userメイラーの実装

自動生成されたメール送信処理のメソッドpassword_resetの中身を実装する。

  # Subject can be set in your I18n file at config/locales/en.yml
  # with the following lookup:
  #
  #   en.user_mailer.password_reset.subject
  #
  def password_reset
    @greeting = "Hi"

    mail to: "to@example.org"
  end

上記のコードは削除して、下記の通り編集する。

  def password_reset(user)
    @user = user
    mail to: user.email, subject: "Password reset"
  end

【Mailer】メールテンプレートの実装

自動生成されているtextメールのテンプレートの中身を実装する。

To reset your password click the link below:

<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>

This link will expire in tow hours.

If you did not request your password to be reset, please ignore this email and your password will stay as it is.

自動生成されているhtmlメールのテンプレートの中身を実装する。

<h1>Password reset</h1>

<p>To reset your password click the link below:</p>

<%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %>

<p>This link will expire in two hours.</p>

<p>
  If you did not request your password to be reset, please ignore this email and your password will stay as it is.
</p>

【Mailer】プレビュー処理の実装

自動生成されたプレビュー処理の中身を実装する。

  # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset
  def password_reset
    UserMailer.password_reset
  end

上記のコードを削除して、下記のとおり実装する。

  def password_reset
    user = User.first
    user.reset_token = User.new_token
    UserMailer.password_reset(user)
  end

動作確認前の注意点

本項目の実行環境は、developであるため、実際にメールは送信されない。 (production環境でなければ、実際にメールは飛ばない) 私はdevelop環境であることを失念していたので、メールが飛ばないのはなぜかと、小一時間悩みました。。。

プレビュー処理の動作確認

下記のURLにアクセスして動作確認を行う。 https://xxxxxxx/rails/mailers/user_mailer/password_reset

textメールのプレビューができることを確認。

image

htmlメールのプレビューができることを確認。

image

パスワード再設定メール送信処理の動作確認

ブラウザを起動し、有効なメールアドレスを入力して、パスワード再設定メールを送信する。 動作確認のためのメールアドレスは、10分メールアドレスを使うと便利。

image

ブラウザ上で、パスワード再送信メールが送信できたメッセージが表示されたことを確認。

image

コンソールログの確認

入力したメールアドレス宛に、パスワード再設定メールが送信されていることを確認。

Sent mail to hsa09793@tqosi.com (8.3ms)
Date: Sun, 09 Jul 2017 01:24:46 +0000
From: noreply@example.com
To: hsa09793@tqosi.com
Message-ID: <596185de75f24_dcf20b491898558@yokoyan-rails-tutorial-4550834.mail>
Subject: Password reset
Mime-Version: 1.0
Content-Type: multipart/alternative;
 boundary="--==_mimepart_596185de74823_dcf20b491898458";
 charset=UTF-8
Content-Transfer-Encoding: 7bit


----==_mimepart_596185de74823_dcf20b491898458
Content-Type: text/plain;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

To reset your password click the link below:

https://rails-tutorial-yokoyan.c9users.io/password_resets/UDO5Wja1Uh_eiltpVaPxpw/edit?email=hsa09793%40tqosi.com

This link will expire in tow hours.

If you did not request your password to be reset, please ignore this email and your password will stay as it is.


----==_mimepart_596185de74823_dcf20b491898458
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style>
      /* Email styles need to be inline */
    </style>
  </head>

  <body>
    <h1>Password reset</h1>

<p>To reset your password click the link below:</p>

<a href="https://rails-tutorial-yokoyan.c9users.io/password_resets/UDO5Wja1Uh_eiltpVaPxpw/edit?email=hsa09793%40tqosi.com">Reset password</a>

<p>This link will expire in two hours.</p>

<p>
  If you did not request your password to be reset, please ignore this email and your password will stay as it is.
</p>

  </body>
</html>

演習1

ブラウザから、送信メールのプレビューをしてみましょう。「Date」の欄にはどんな情報が表示されているでしょうか?

UTC時間が表示される。 日本時間から9時間ずれて表示される。

演習2

パスワード再設定フォームから有効なメールアドレスを送信してみましょう。また、Railsサーバーのログを見て、生成された送信メールの内容を確認してみてください。

前述の「コンソールログの確認」参照。

演習3

コンソールに移り、先ほどの演習課題でパスワード再設定をしたUserオブジェクトを探してください。オブジェクトを見つけたら、そのオブジェクトが持つreset_digestとreset_sent_atの値を確認してみましょう。

演習2で使用したユーザーのメールアドレスで検索。 reset_digestと、reset_sent_atに値が入っていることを確認。

yokoyan:~/workspace/sample_app (password-reset) $ rails console --sandbox
Running via Spring preloader in process 3587
Loading development environment in sandbox (Rails 5.0.0.1)
Any modifications you make will be rolled back on exit
>> 
?> user = User.find_by(email: "hsa09793@tqosi.com")
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "hsa09793@tqosi.com"], ["LIMIT", 1]]
=> #<User id: 103, name: "mail_test", email: "hsa09793@tqosi.com", created_at: "2017-07-09 01:06:40", updated_at: "2017-07-09 01:24:46", password_digest: "$2a$10$F42JIypvQYXBt6V2pe7A4ONXQ4PImXqR0GSK7ioCrj5...", remember_digest: nil, admin: false, activation_digest: "$2a$10$V4Z1Rgva0TA.FJqv7/SWAO/vfjX0sBSpgLpKjx8itK7...", activated: nil, activated_at: nil, reset_digest: "$2a$10$dsfjyWCpuX.9I4c7zbWYieBp4nxc.fzipFaYHyaHcP8...", reset_sent_at: "2017-07-09 01:24:46">
>> 
?> user.reset_digest
=> "$2a$10$dsfjyWCpuX.9I4c7zbWYieBp4nxc.fzipFaYHyaHcP83yMQRBtFaO"
>> 
?> user.reset_sent_at
=> Sun, 09 Jul 2017 01:24:46 UTC +00:00

12.2.2 送信メールのテスト

本章での学び

【test】パスワード再設定用メイラーメソッドのテストを作成

11章でコメントアウトしていたテストコードを実装する。

  • michaelユーザの情報を取得
  • パスワード再設定トークンを生成
  • パスワード再設定メールを送信
  • メールオブジェクトの件名に、"Password reset"が含まれているかチェック
  • メールオブジェクトの宛先が、ユーザのemailと一致しているかチェック
  • メールオブジェクトのfromアドレスが、noreply@example.comと一致しているかチェック
  • テストユーザのパスワード再設定トークンが、エンコードされたメール本文内に存在するかチェック
  • テストユーザのアドレスがエスケープされて、エンコードされたメール本文に含まれているかチェックする

上記を踏まえて実装する。

  test "password_reset" do
    user = users(:michael)
    user.reset_token = User.new_token
    mail = UserMailer.password_reset(user)
    assert_equal "Password reset", mail.subject
    assert_equal [user.email], mail.to
    assert_equal ["noreply@example.com"], mail.from
    assert_match user.reset_token, mail.body.encoded
    assert_match CGI.escape(user.email), mail.body.encoded
  end

動作確認

テストコードがgreenになることを確認。

yokoyan:~/workspace/sample_app (password-reset) $ rails test
Running via Spring preloader in process 5896
Started with run options --seed 1346

  47/47: [===================================================================================================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.88912s
47 tests, 205 assertions, 0 failures, 0 errors, 0 skips

演習1

メイラーのテストだけを実行してみてください。このテストは greenになっているでしょうか?

メイラーのテストだけ実行する。 rails test test/ディレクトリ名/ファイル名 greenになることを確認。

yokoyan:~/workspace/sample_app (password-reset) $ rails test test/mailers/user_mailer_test.rb
Running via Spring preloader in process 6095
Started with run options --seed 2562

  2/2: [=====================================================================================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.67604s
2 tests, 16 assertions, 0 failures, 0 errors, 0 skips

演習2

リスト 12.12にある2つ目のCGI.escapeを削除すると、テストが redになることを確認してみましょう。

エスケープ処理をコメントアウトする。

#    assert_match CGI.escape(user.email), mail.body.encoded
    assert_match user.email, mail.body.encoded

テストがredになることを確認。

yokoyan:~/workspace/sample_app (password-reset) $ rails test test/mailers/user_mailer_test.rb
Running via Spring preloader in process 6187
Started with run options --seed 5175

 FAIL["test_password_reset", UserMailerTest, 0.6262194160372019]
 test_password_reset#UserMailerTest (0.63s)
        Expected /michael@example\.com/ to match # encoding: US-ASCII
        "\r\n----==_mimepart_596197d92db13_182bcc70e015864\r\nContent-Type: text/plain;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\nTo reset your password click the link below:\r\n\r\nhttp://example.com/password_resets/IWAHQV67Gyu6BAQUaz9OoA/edit?email=michael%40example.com\r\n\r\nThis link will expire in tow hours.\r\n\r\nIf you did not request your password to be reset, please ignore this email and your password will stay as it is.\r\n\r\n\r\n----==_mimepart_596197d92db13_182bcc70e015864\r\nContent-Type: text/html;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\n<!DOCTYPE html>\r\n<html>\r\n  <head>\r\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n    <style>\r\n      /* Email styles need to be inline */\r\n    </style>\r\n  </head>\r\n\r\n  <body>\r\n    <h1>Password reset</h1>\r\n\r\n<p>To reset your password click the link below:</p>\r\n\r\n<a href=\"http://example.com/password_resets/IWAHQV67Gyu6BAQUaz9OoA/edit?email=michael%40example.com\">Reset password</a>\r\n\r\n<p>This link will expire in two hours.</p>\r\n\r\n<p>\r\n  If you did not request your password to be reset, please ignore this email and your password will stay as it is.\r\n</p>\r\n\r\n  </body>\r\n</html>\r\n\r\n----==_mimepart_596197d92db13_182bcc70e015864--\r\n".
        test/mailers/user_mailer_test.rb:25:in `block in <class:UserMailerTest>'

  2/2: [=====================================================================================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.63150s
2 tests, 16 assertions, 1 failures, 0 errors, 0 skips

テスト実行後は元に戻しておく。

おわりに

パスワード再設定メールの総身諸利を実装することができました。 まだ、production環境にデプロイしていないため、実際にパスワード再設定メールが飛ぶかどうかの確認ができていません。(ここは、12.4 本番環境でのメール送信 (再掲)で実施) 今回、develop環境でメールが飛ばないことを失念しており、はまってしまったのでみなさんもご注意ください。。。