【Rails Tutorial11章】アカウント有効化機能の実装手順を整理してみました【中編】

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

前編→[__AccountActivationsリソースの作成__](https://qiita.com/kurawo___D/items/233ed8b4f8749c95df9e)
後編→[__アカウント有効化機能の実装__](https://qiita.com/kurawo___D/items/3f1873530741b4601ef3)
<br>

##1.アカウント有効化用のメールを送信する機能の実装
　　●__データのモデル化が完了したので、次はアカウント有効化メールの送信に必要な機能を記述していく__。

　　●__メールを送信する機能を実装するには、Action Mailerライブラリを使って、Userのメイラーを作成する必要がある__。
　　　　➡︎メイラーは、Usersコントローラのcreateアクションで、ユーザーへ有効化リンクをメール送信させるために使う。

　　　　➡︎メイラーはコントローラのアクションと似ており、メールのテンプレートを、ビューと同じ要領で定義できる。
　　　　　　→有効化トークンと有効化するアカウントのアドレスのリンクは、このテンプレートの中で使う。
			
####1-1.送信メールのテンプレートを作成していく
　　●まず、__Userメイラーを作成__する。
　　　　➡︎$ rails g mailer UserMailer account_activation password_reset
　　　　　　→UserMailerはメイラー名で、その後には必要となるメソッドを入力する。
　　　　　　→password_resetメソッドは、後にパスワード再設定機能を実装する際に使うので、序でに作成しておく。

　　　　➡︎メイラーを生成すると、ついでにビューのテンプレートがファイルごとに2つ生成される。
　　　　　　→1つはテキストメール用のテンプレートで、1つはHTMLメール用のテンプレート。
		
　　●生成されたメイラーをカスタマイズして、有効化メールで使えるようにする。
　　　　➡Applicationメイラー
　　　　　　→app/mailers/application_mailer.rb
　　　　　　→デフォルトでfromアドレスが設定されていると思うが、これを削除して、実際に使えるメールアドレスに書き換える。

　　　　➡Userメイラー
　　　　　　→app/mailers/user_mailer.rb
　　　　　　→コマンドを実行した際に作成したメソッドの内、account_activationメソッドをカスタマイズしていく。
　　　　　　　　→まずメソッド名をaccount_activation(user)とし、ユーザーのデータを取得できるよう明示的に引数を記述してあげる。
　　　　　　　　　　→取得したユーザーは、ビューで利用できるように@userインスタンス変数へ代入されるようにする。
　　　　　　　　　　→ユーザーへメールが送信されるよう処理を記述し、件名も付け加えておく。
　　　　　　　　　　　　→mail to: user.email, subject: "件名"

　　●__ユーザーへ送信するための有効化リンクを作成する準備をする__。
　　　　➡︎ユーザーをメールアドレスで検索し、有効化トークンを認証できるよう処理を記述していく。

　　　　➡︎そのためには、リンクにメールアドレスとトークンの両方を組み込む必要がある。
　　　　　　→__アカウントを有効化するリンクにトークンを組み込む方法__。
　　　　　　　　→AccountActivationsリソースにて有効化はモデル化したので、有効化トークンは名前付きルートの引数で使われることになる。
　　　　　　　　→つまり、edit_account_activation_url(@user.activation_token)となる。
　　　　　　　　→上記の名前付きルートは、下記の形式のURLを生成する。
　　　　　　　　　　→http://www.example.com/account_activations/(new_tokenメソッドで生成されたランダム文字列)/edit
　　　　　　　　　　→ちなみに上記のトークン部分はユーザーIDと同じ働きをするため、AccountActivationsコントローラのeditアクション内で、params[:id]とすれば参照するできる。
　　　　　　→というわけで、トークンの組み込み完了。
　　　　　　→__今度は、有効化リンクにメールアドレスも組み込む__。
　　　　　　　　→リンクにメールアドレスを組み込むには、クエリパラメータを使う必要がある。
　　　　　　　　　　→クエリパラメータとは、URLの末尾で「?」に続けてキーと値のペアを記述したもの。
　　　　　　　　　　→要するに、http://<省略>/edit?email=user%40example.comみたいな感じ。
　　　　　　　　　　→「%40」の意味は＠と一緒。URLでは＠記号が使えないので、エスケープと呼ばれる手法により変換している。
　　　　　　　　→上記のようにURLにクエリパラメータを組み込むには、名前付きルートにてユーザーのメールアドレスを参照できるようなハッシュを記述してあげればOK。
　　　　　　　　→どういうことかというと、edit_account_activation(@user.activation_token, email:@user.email)とすればOK。
　　　　　　　　→上記のように名前付きルートでクエリパラメータを書き加えると、Railsが自動的にエスケープを行ってくれため、正常なリンクとして動作してくれる。
　　　　　　　　→さらに、editアクションでメールアドレスを参照するためにparams[:email]とする際は、自動的にスケープを解除してくれる。便利。

　　　➡︎︎︎これで、リンクを作成する準備が完了した。

　　●テンプレートビューにて、リンクを作成する。
　　　　➡︎通常のERb同様ビューは自由にカスタマイズできるので、挨拶文にユーザー名を含め、有効化リンクを記述することにする。

　　　　➡︎︎︎アカウント有効化のテキストビューにて。
　　　　　　→挨拶とユーザーの名前を表示する記述をする。
　　　　　　→ERB内でeditへの名前付きルートを使って、有効化トークンとメールアドレスを組み込んだリンクを作成する。

　　　　➡︎アカウント有効化のHTMLビューにて。
　　　　　　→挨拶とユーザーの名前を表示する記述をする。
　　　　　　→ERb内でlink_toタグで、上記同様editへの名前付きルートを使って、有効化トークンとメールアドレスを組み込んだリンクを作成する。

>```ruby:ターミナル
  #1-1.Userメイラーの作成
  $ rails g mailer UserMailer account_activation password_reset
```

>```ruby:app/mailers/application_mailer.rb
  #1-1.Applicationメイラーのカスタマイズ。
  class ApplicationMailer > ActionMailer::Base
    default from: "実際に有効化メール送信で使うメアド"
    layout 'mailer'
  end
```

>```ruby:app/mailers/user_mailer.rb
  #1-1.Userメイラーのカスタマイズ。
  class UserMailer < ApplicationMailer
　    
    def account_activation(user)
      @user = user
      mail to: user.email, subject "件名"
　
    #省略
　 
  end
```

>```ruby:app/views/user_mailer/account_activation.text.erb
  #1-1.アカウント有効化のテキストビュー。
  こんにちは、<%= @user.name %>
  下記のリンクをクリックして、アカウントを有効化してください。
  <%= edit_account_activation_url(@user.activation_token, 
                                  email: @user.email) %>
```

>```ruby:app/views/user_mailer/account_activation.html.erb
  #1-1.アカウント有効化のHTMLビュー。
  こんにちは、<%= @user.name %>
  下記のリンクをクリックして、アカウントを有効化してください。
  <%= link_to "有効化する", edit_account_activaation_url(@user.activation_token,
                                                       email: @user.email) %>
```
											
####1-2.送信メールのプレビュー
　　●メールメッセージをその場で簡単にプレビューする。
　　　　➡︎アプリケーションのdevelopment環境の設定に手を加える必要がある。
　　　　　　→config/environments/development.rbファイルへ移動する。
　　　　　　→ホスト名は、各々のdevelopment環境に合わせて変更する必要があるので注意。
　　　　　　　　→例えば、ローカル環境で開発しているなら「'localhost:3000'」に変更しなければ動作しない。
　　　　　　→developmentサーバーを再起動して、設定を読み込む。

　　　　➡アカウント有効化のプレビューメソッドを定義する。
　　　　　　→test/mailers/previews/user_mailer_preview.rbファイルへ移動する。
　　　　　　→account_activationメソッドはこのままでは動作しないため、動作させるための処理を記述していく必要がある。
　　　　　　　　→まず、開発用DBの最初のユーザーを取得するためのuser変数を定義する。
　　　　　　　　→さらに、ビューのテンプレートでアカウント有効化のトークンが必要となるため、ユーザーの有効化トークンにUser.new_tokenを代入する処理を記述する必要がある。
　　　　　　　　　　→有効化トークンは、仮の属性なのでDBのユーザーは実際にはこの値を持っているわけではない。
　　　　　　　　→上記で取得したユーザーをUserMailer.account_activationメソッドの引数に渡す。

　　　　➡︎以上のコードを実装したことで、指定のURLでアカウント有効化メールをプレビューできるようになった。

>```ruby:config/environments/development.rb
  #1-2.development環境のメール設定。
  Rails.application.configure do
    .
    .
    .
    config.action_mailer.raise_delivery_errors = true
    config.action_mailer.delivery_method = :test
    host = '各々のホストのURLを記述'
    config.action_mailer.default_url_options = { host: host, protocol: 'https' }
    .
    .
    .
  end
```

>```ruby:test/mailers/previews/user_mailer_preview.rb
  # Preview all emails at http://localhost:3000/rails/mailers/user_mailer
  class UserMailerPreview < ActionMailer::Preview
　
  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/account_activation
　
  #1-2.アカウント有効化のプレビューメソッド定義。
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
  end
  .
  .
  .
end
```

			
####2-3.送信メールのテストを書いていく
　　●メールプレビューのテストを作成する。
　　　　➡︎test/mailers/user_mailer_test.rbに、Userメイラーの簡単なテスト例が自動生成されているので利用する。

　　　　➡︎assert_matchというメソッドがとても便利で、正規表現によって文字列をテストすることができる。
　　　　　　→例.assert_match 'foo', 'foobar'  #true

　　　　➡︎assert_matchメソッドを使い、名前、有効化トークン、エスケープ済みメールアドレスが本文に含まれているかどうかをテストする。
　　　　　　→ちなみに、CGI.escapse(use.email)とすることで、テスト用のユーザーのメールアドレスをエスケープすることができる。
　　　　　　→それから、このCGI.escape部分を削除すると、テストはパスしない。

　　　　➡︎加えてテストコードのfixtureユーザーに、有効化トークンを取得させる記述をする。そうしないと空白になる。
　　　　➡︎テストRED。

　　　　➡︎このテストがパスするには、テストファイル内のドメイン名を正しく設定する必要がある。
　　　　　　→config/environments/test.rbへ移動。
　　　　　　→テストのドメインホストを、host: '正しいドメイン名'とする。

　　　　➡︎テストGREEN。

>```ruby:test/mailers/user_mailer_test.rb
  #1-3.現在までのメール機能実装に関するテストを書いていく。RED。
  require 'test_helper'
  class UserMailerTest < ActionMailer::TestCase
　
  test "account_activation" do
    user = users(:michael)
    user.activation_token = User.new_token  #ここで、有効化トークンを取得しないと空白になるので注意。
    mail = UserMailer.account_activation(user)
    assert_equal "Account activation", mail.subject
    assert_equal [各々が設定したメールアドレスを記述], mail.to
    assert_equal ["noreply@example.com"], mail.from
    assert_match user.name,               mail.body.encoded
    assert_match user.activation_token,   mail.body.encoded
    assert_match CGI.escape(user.email),  mail.body.encoded
  end
end
```

>```ruby:config/environments/test.rb
  #1-3.テストのドメインホストを設定する。
  Rails.application.configure do
    .
    .
    .
    config.action_mailer.delivery_method = :test
    config.action_mailer.default_url_options = { host: '正しいホストを記述' }
    .
    .
    .
  end
```
				
####2-4.ユーザーのcreateアクションを更新。
　　●createアクション内のユーザー登録機能に、アカウント有効化機能を追加する。
　　　　➡︎app/controllers/users_controller.rbファイルへ移動。
　　　　　　→ユーザーの新規登録が完了しユーザーが保存された後、そのユーザーへアカウント有効化メールを送信する処理を記述する。
　　　　　　　　→UserMailer.account_activation(@user).deliver_now
　　　　　　→ユーザーへメールを確認するよう促すフラッシュを表示させ、リダイレクト先はルートURLに変更。
　　　　　　→この時同時に、登録が完了するとログインする機能を削除する。

　　●上記の変更により、テストスイートがREDになった。
　　　　➡︎integration/users_signup_test.rbへ移動。
　　　　　　→失敗するテストを一旦コメントアウト。
　　　　　　　　→新規登録後にログインさせないようにしたので、ユーザープロフページが表示されているかどうかの検証と、ユーザーのログインを検証する記述をコメントアウトにする。
　　　　　　　　→assert_template 'users/show'と、assert is_logged_in?
　　　　　　→アカウント有効化機能を実装した後、テストを書くのでそこで修正する。

>```ruby:app/controllers/users_controller.rb
  class UsersController < ApplicationController
    .
    .
    .
    def create
      @user = User.new(user_params)
      if @user.save
        #1-4.新規登録後、ユーザーへアカウント有効化のメールを送信する。(ログインは削除)
        UserMailer.account_activation(@user).deliver_now
        flash[:info] = "Please check your email to activate your account."
        redirect_to root_url
      else
        render 'new'
      end
    end
    .
    .
    .
end
```

##最後に
　中編の__アカウント有効化用メール送信機能の実装__が完了しました。
　前編→[AccountActivationsリソースの作成](https://qiita.com/kurawo___D/items/233ed8b4f8749c95df9e)
　後編→[__アカウント有効化機能の実装__](https://qiita.com/kurawo___D/items/3f1873530741b4601ef3)

##参考
　[Railsチュートリアル 第11章アカウントの有効化](https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation)