Ruby on Rails Tutorial 12章

12.1.1

演習
この時点で、テストスイートが greenになっていることを確認してみましょう。
表 12.1の名前付きルートでは、_pathではなく_urlを使うように記してあります。なぜでしょうか? 考えてみましょう。ヒント: アカウント有効化で行った演習 (11.1.1.1) と同じ理由です。

2絶対パスでないといけないから


12.1.2
演習
リスト 12.4のform_forメソッドでは、なぜ@password_resetではなく:password_resetを使っているのでしょうか? 考えてみてください。

Railsが自動でURLを割り当ててくれるから

演習
試しに有効なメールアドレスをフォームから送信してみましょう (図 12.6)。どんなエラーメッセージが表示されたでしょうか?

コンソールに移り、先ほどの演習課題で送信した結果、(エラーと表示されてはいるものの) 該当するuserオブジェクトにはreset_digestとreset_sent_atがあることを確認してみましょう。また、それぞれの値はどのようになっていますか?

１

![railstutorialchapter1201No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368629/ab86bbef-9cdc-f729-10c4-e754716b547b.png)

２

```
>> user = User.find_by(email: "example@railstutorial.org")
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example@railstutorial.org"], ["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-02 05:51:57", password_digest: "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tG...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$W2QCwuEiGFjXs9q1aTvHqu9ksoxJPmZ6tQE8TmmuS5W...", reset_sent_at: "2019-05-02 05:51:57">
>> user.reset_digest
=> "$2a$10$W2QCwuEiGFjXs9q1aTvHqu9ksoxJPmZ6tQE8TmmuS5WPn7MvmhApe"
>> user.reset_sent_at
=> Thu, 02 May 2019 05:51:57 UTC +00:00

ちなみにサーバーのログ
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example@railstutorial.org"], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  SQL (1.5ms)  UPDATE "users" SET "reset_digest" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["reset_digest", "$2a$10$W2QCwuEiGFjXs9q1aTvHqu9ksoxJPmZ6tQE8TmmuS5WPn7MvmhApe"], ["updated_at", "2019-05-02 05:51:57.721207"], ["id", 1]]
   (6.3ms)  commit transaction
   (0.0ms)  begin transaction
  SQL (0.6ms)  UPDATE "users" SET "updated_at" = ?, "reset_sent_at" = ? WHERE "users"."id" = ?  [["updated_at", "2019-05-02 05:51:57.732073"], ["reset_sent_at", "2019-05-02 05:51:57.731546"], ["id", 1]]
  
```

12.2.1

演習
ブラウザから、送信メールのプレビューをしてみましょう。「Date」の欄にはどんな情報が表示されているでしょうか?
パスワード再設定フォームから有効なメールアドレスを送信してみましょう。また、Railsサーバーのログを見て、生成された送信メールの内容を確認してみてください。
コンソールに移り、先ほどの演習課題でパスワード再設定をしたUserオブジェクトを探してください。オブジェクトを見つけたら、そのオブジェクトが持つreset_digestとreset_sent_atの値を確認してみましょう。

1

![railstutoriialchapter12023_No-00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368629/88e090b4-331c-7e03-ca61-83417f1b2cc2.jpeg)

2

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

To reset your password click the link below:

https://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/password_resets/4oduySTFZbn_7NUZ3Ey0vw/edit?email=example%40railstutorial.org

This link will expire in two hours.

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

----==_mimepart_5ccb78403955f_129d244fd0c7932
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://dfa974f98d004f37899984bee93a2473.vfs.cloud9.us-east-2.amazonaws.com/password_resets/4oduySTFZbn_7NUZ3Ey0vw/edit?email=example%40railstutorial.org">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>



```

3

```
>> user = User.find_by(email: "example@railstutorial.org")
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example@railstutorial.org"], ["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-02 23:07:44", password_digest: "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tG...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$QtdXtxoGt9IjlNLbDREja.0XNQ6snBrkZO8krSIIPsS...", reset_sent_at: "2019-05-02 23:07:44">
>> user.reset_digest
=> "$2a$10$QtdXtxoGt9IjlNLbDREja.0XNQ6snBrkZO8krSIIPsS9W9FKJah62"
>> user.reset_sent_at
=> Thu, 02 May 2019 23:07:44 UTC +00:00
>> 

```

12.3.1

演習
12.2.1.1で示した手順に従って、Railsサーバーのログから送信メールを探し出し、そこに記されているリンクを見つけてください。そのリンクをブラウザから表示してみて、図 12.11のように表示されるか確かめてみましょう。
先ほど表示したページから、実際に新しいパスワードを送信してみましょう。どのような結果になるでしょうか?

1

![RailsTUtorialchapter1203No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368629/9cb0343f-d38d-0cc3-1d19-e9469b91d0d6.png)

2
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368629/e06b2c95-324e-c37d-8939-0ef858bc08f0.png)


12.3.2

演習
12.2.1.1で得られたリンク (Railsサーバーのログから取得) をブラウザで表示し、passwordとconfirmationの文字列をわざと間違えて送信してみましょう。どんなエラーメッセージが表示されるでしょうか?
コンソールに移り、パスワード再設定を送信したユーザーオブジェクトを見つけてください。見つかったら、そのオブジェクトのpassword_digestの値を取得してみましょう。次に、パスワード再設定フォームから有効なパスワードを入力し、送信してみましょう (図 12.13)。パスワードの再設定は成功したら、再度password_digestの値を取得し、先ほど取得した値と異なっていることを確認してみましょう。ヒント: 新しい値はuser.reloadを通して取得する必要があります。

1

![railsTutorialchapter1204_No-00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368629/c4b74f02-3785-9474-e134-296c996b2424.jpeg)

2

```
>> user = User.find_by(email: "example@railstutorial.org")
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example@railstutorial.org"], ["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:42:31", password_digest: "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tG...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31">
>> user.password_digest=> "$2a$10$BskqDOUon6zJxBUHJnmf7.gpoDL.cyl1MzNBV4gR2tGGcCLafcp2O"
>> user.reload
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2019-04-21 11:43:41", updated_at: "2019-05-04 11:47:38", password_digest: "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzu...", remember_digest: nil, admin: true, activation_digest: "$2a$10$JDqeO5RqJf.oRt1Uly.SGelGym2U6Emi4AUU.ErmhZU...", activated: true, activated_at: "2019-04-21 11:43:40", reset_digest: "$2a$10$beh3nZlvKzWaUaqcGRjyfuH2A6bpT4irIhpZGuD4rI6...", reset_sent_at: "2019-05-04 11:42:31">
>> user.password_digest
=> "$2a$10$0J3/6mpDJ2x8vFb2OdNBZOskHIcpGnaLjbkh0eI9Yzut2pjqYfddy"
>> 

```

13.3.3

演習
リスト 12.6にあるcreate_reset_digestメソッドはupdate_attributeを２回呼び出していますが、これは各行で１回ずつデータベースへ問い合わせしていることになります。リスト 12.20に記したテンプレートを使って、update_attributeの呼び出しを１回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが１回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。ちなみにリスト 12.20にあるコードには、前章の演習 (リスト 11.39) の解答も含まれています。
リスト 12.21のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐 (リスト 12.16) を統合テストで網羅してみましょう (12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます (なお、大文字と小文字は区別されません)。
２時間経ったらパスワードを再設定できなくする方針は、セキュリティ的に好ましいやり方でしょう。しかし、もっと良くする方法はまだあります。例えば、公共の (または共有された) コンピューターでパスワード再設定が行われた場合を考えてみてください。仮にログアウトして離席したとしても、２時間以内であれば、そのコンピューターの履歴からパスワード再設定フォームを表示させ、パスワードを更新してしまうことができてしまいます (しかもそのままログイン機構まで突破されてしまいます!)。この問題を解決するために、リスト 12.22のコードを追加し、パスワードの再設定に成功したらダイジェストをnilになるように変更してみましょう5。
リスト 12.18に１行追加し、１つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。

1

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

2

``` 

test "expired token" do
～中略～
 assert_match /expired/i, response.body
end
```

3

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

4

```password_resets_test.rb
   assert_match /expired/i, response.body
   
   
       patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "foobaz" } }
   assert_nil user.reload.reset_digest
    assert is_logged_in?
    assert_not flash.empty?
    assert_redirected_to user
    

```


12.4
演習
production環境でユーザー登録を試してみましょう。ユーザー登録時に入力したメールアドレスにメールは届きましたか?
メールを受信できたら、実際にメールをクリックしてアカウントを有効化してみましょう。また、Heroku上のログを調べてみて、有効化に関するログがどうなっているのか調べてみてください。ヒント: ターミナルからheroku logsコマンドを実行してみましょう。
アカウントを有効化できたら、今度はパスワードの再設定を試してみましょう。正しくパスワードの再設定ができたでしょうか?

１スパム扱いされているらしくメールが届かない
２サーバーのログから実行