【Rails Tutorial12章】パスワード再設定機能の実装手順を整理してみました【前編】

##はじめに
Railsチュートリアル12章の内容を、少しでも理解の助けとなればと思い、割としっかり目に整理しました！
__備忘録です。__
<br>
##前提
Railsチュートリアル１〜11章までの内容が完了していること。
特に、アカウント有効化機能が実装されている事。
<br>
##内容
Railsチュートリアル12章の__パスワード再設定機能の実装手順__を、前中後半の3回に分けて整理おります。
前編である今回は、__パスワード再設定用のリソース作成__を行っていきます！

中編→[__パスワード再設定メール送信機能の実装__]()
後編→[__パスワード再設定機能の本実装__]()
<br>
##1.PasswordResetsリソースのモデリング。
　　●__新たなモデルは作らず、必要なデータだけをUserモデルに追加していく感じで進めていく。__
　　
　　●__さらにPasswordResetsをリソースとして扱うために、RESTfulなURLを追加していく。__
　　　　➡︎パスワードを再設定するには再設定用のフォームが必要なので、ビューを表示させるためのnewアクションとeditアクションを作成する必要がある。

　　　　➡︎加えてnewアクションに対応するcreateアクション、editアクションに対応するupdateアクション必要になるのでそれらを実装していく。

####1-1.PasswordResetsコントローラを作成する。
　　__1.始めに、パスワード再設定用のコントローラを作成しましょ。__
　　　　➡︎$ rails g controller PasswordResets new edit --no-test-framework
　　　　　　→newアクションとeditアクションも同時に生成する。
　　　　　　→ユーザー新規登録用の統合テストを代用してテストを記述していくので、テスト用のファイルは生成しないオプションを追加している。

　　__2.パスワード再設定用のリソースをルーティングへ追加していく。__
　　　　➡︎Userモデル内のパスワードを変更するためにフォームが必要となるので、new、create、edit、updateのルーティングを用意する。
　　　　　　→resources :password_resets, only: [:new, :create, :edit, :update]
　　　　　　→上記のコードにより提供されるRESTfulなルーティングは以下の通り。

|HTTPリク|URL|Action|名前付きルート|
|:---:|:---:|:---:|:---:|
|GET|/password_resets/new|new|new_password_reset_path|
|POST|/password_resets|create|password_resets_path|
|GET|/password_resets/(token)/edit|edit|edit_password_reset_url(token)|
|PATCH|/password_resets/(token)|update|password_reset_url(token)|

　　__3.パスワード再設定画面へのリンクを追加する。__
　　　　→ログイン画面のパスワード入力欄と共に、パスワード再設定用フォームのURLリンクを作成する。
　　　　　　→link_toでnew_password_resets_pathへ。

####1-2.新しいパスワードの設定。
　　__1.パスワード再設定のデータモデルには、トークン用の仮想的な属性と、トークンに対応するダイジェストを用意する。__
　　　　➡︎ユーザーへパスワード再設定用のリンクを送信した隙間から、第三者によりアカウントを盗まれる恐れがあるため、パスワードの再設定では必ずダイジェストを用意すること。

　　__2.パスワード再設定機能実装の際には、もう一つ注意しなければならないことがある。__
　　　　➡︎第三者からの攻撃を防ぐために、パスワード再設定用のリンクは、なるべく短時間で期限切れになるようにする必要があるということ。なので再設定用のメールに、送信時刻も記録する必要がある。

　　__3.Userモデルにパスワード再設定用の属性を追加する。__
　　　　➡︎reset_digest属性と、reset_sent_at属性の2つ。

　　　　➡︎コマンドを実行してマイグレーションに属性を追加。

　　　　➡︎属性の追加が完了したら、DBへ変更を反映する。

　　__4.新しいパスワードの再設定画面ビューを作成する。__
　　　　➡︎他のビュー同様、provideメソッドでタイトル挿入。

　　　　➡︎form_forメソッドを使ってキーを:password_resetとしたハッシュを、パスワードリセットリソースへ送信されるようにする。

　　　　➡︎labelメソッドを用意して値を:emailとし、入力欄と紐付け。

　　　　➡︎email_fieldメソッドを用意して値を:emailとする。
			
　　　　➡︎最後に、submitメソッドで送信ボタンを用意する。

>```
  1-2.3
　
  $ rails g migration add_reset_to_users reset_digest:string ¥
    reset_sent_at:datetime
　
  $ rails db:migrate
```

>```ruby:app/views/password_resets/new.html.erb
  #1-2.4
  <%= provide(:title, "パスワード再設定") %>
    <%= form_for(:password_reset, url: password_resets_path) do |f| %>
      <%= f.label :email %>
      <%= f.email_field :email %>
      <%= f.submit "送信" %>
    <% end %>
```
	
####1-3.createアクションでパスワードを再設定する機能を実装する。
　　➡︎フォームから送信されるメアドをキーにDBからユーザーを取得して、パスワード再設定用トークンと送信時の時刻でDBの内容を更新させる機能を実装していく。

　　__1.パスワード再設定用のcreateアクションの中身を記述していく。__
　　　　➡︎app/controllers/password_resets_controller.rbファイルへ移動。
　　　　　　→フォームから送信されたメアドを小文字に変換して、そのメアドを持つユーザーをDBから取得し、インスタンス変数に代入する処理を記述。
　　　　　　→上記で取得した@userが存在する場合の条件式を記述。
　　　　　　　　→パスワード再設定用のトークンより、DBのパスワードリセットダイジェスト属性とトークン作成時刻を更新させる処理を記述する。
　　　　　　　　　　→create_reset_digestメソッドを使う(2で定義する)
　　　　　　　　→ユーザーに対して、パスワード再設定用リンク付きのメールを送信する処理を記述する。
　　　　　　　　　　→send_password_reset_emailメソッドを使う(3で定義する)
　　　　　　　　→同時にルートURLへリダイレクトさせる処理を記述。
　　　　　　　　→フラッシュメッセージをユーザーに表示させる記述も行う。
　　　　　　→ユーザーが無効な場合はnewページへ転送し、flash.nowメソッドでメッセージを表示させる処理を記述する。

　　__2.Userモデルにパスワードを再設定するためのメソッドを追加する。__
　　　　➡︎app/models/user.rbファイルへ移動する。
　　　　　　→まずはパスワード再設定用の仮想属性を追加する。
　　　　　　　　→attr_accessorに:reset_tokenを追加すればOK。
　　　　　　→パスワード再設定用のメソッドを定義する(1で適用)。
　　　　　　　　→メソッド名をcreate_reset_digestとする。
　　　　　　　　　　→仮想の属性に、ランダムな文字列を代入する処理を記述する。
　　　　　　　　　　　　→self.reset_token = User.new_token
　　　　　　　　　　→取得したトークンをハッシュ化して、パスワードリセットダイジェストを更新するための処理を記述する。
　　　　　　　　　　　　→update_attribute(:reset_digest, User.digest(reset_token))
　　　　　　　　　　→タイムスタンプを更新する処理を記述する。
　　　　　　　　　　　　→update_attribute(:reset_sent_at, Time.zone.now)
　　　　　　　　　　→しかし、update_columnsメソッドを使えば、上記の2つの更新は、DBへの呼び出しを一回で済ませることができる。
　　　　　　　　　　　　→update_columns(reset_digest: User.digest(reset_token), :reset_sent_at: Time.zone.now)

　　__3.同じくUserモデルに、パスワードを再設定するためのメールを送信するメソッドを定義する(1で適用)。__
　　　　➡︎メソッド名をsend_password_reset_emailとする。
　　　　　　→メイラーを呼び出し、パスワードリセット用のメールを送信させる処理を記述する。
　　　　　　　　→UserMailer.password_reset(self).deliver_now(なおこのメソッドは中編で定義していく)

>```ruby:app/controllers/password_resets_controller.rb
  #1-3.1
  class PasswordResetsContoroller < ApplicationController
    .
    .
    .
    def create
      @user = User.find_by(email: params[:password_reset][:email].downcase)
      if @user
        @user.create_reset_digest  #2で定義
        @user.send_password_reset_email  #3で定義
        flash[:info] = "パスワード再設定リンク付きのメールを送信しました"
        redirect_to root_url
      else
        flash.now[:danger] = "メールアドレスが不正です"
        render 'new'
      end
    end
    .
    .
    .
  end
```

>```ruby:app/models/user.rb
  class User < ApplicationRecord
    #1-3.2
    attr_accessor :remember_token, ..., :reset_token
    .
    .
    .
    #1-3.2
    def create_reset_digest
      self.reset_token = User.new_token
      update_attribute(:reset_digest, User.digest(reset_token))
      update_attribute(:reset_sent_at, Time.zone.now)
    end
　
    #1-3.3
    def send_password_reset_email
      UserMailer.password_reset(self).deliver_now
    end
  end
```

　　➡︎createアクションへの記述は以上で完了したが、まだこのままではメールの送信ができないので、パスワード再設定のメイラーメソッドを定義する必要があるので、中編でその実装を行っていく。

##最後に
　以上で前編の__パスワード再設定用のリソース作成__が完了しました。
　中編→[__パスワード再設定メール送信機能の実装__]()
　後編→[__パスワード再設定機能の本実装__]()

##参考
　[Railsチュートリアル 第12章パスワードの再設定](https://railstutorial.jp/chapters/password_reset?version=5.1#cha-password_reset)