【第11章】Railsチュートリアル 5.1(第4版)　アカウントの有効化

#はじめに
個人的な理解・備忘録を目的としてます。
筆者自身は[動画版Railsチュートリアル](https://railstutorial.jp/#screencast)で進めているため、アプリ作成中コード・ブランチ名などの若干の違いがありますので参考程度に流し見して頂けたら嬉しいです。
理解不足のため、何かありましたらコメント等ご指摘してくださると幸いです(^_^;)

#11.0 本章の目標
本章では、アカウントを有効化するステップを新規登録の途中に差し込むことで、本当にそのメールアドレスの持ち主なのかどうかを確認できるようにする。（仮に誰かがミスって登録しても大丈夫なように）

例．ECサイト登録した場合のユーザー（ユ）とサーバー（サ）のやりとり
1. （ユ）→（サ）　「このemailで登録で」
2. （サ）→（ユ）　「メール送ったから確認して」
3. （ユ）→（サ）　「来たメールのURLクリックしたよ」
4. 【NG】（サ）→（ユ）　「違うからもう一回だね」
4. 【OK】（サ）→（ユ）　「登録するね（DB書き込みするか）」

＜実装内容＞
(1) 有効化トークンやダイジェストを関連付けておいた状態に
(2) 有効化トークンを含めたリンクをユーザーにメールで送信
(3) ユーザーがそのリンクをクリックすると有効化

また、ユーザーがパスワードを忘れたときにパスワードを再設定できるようにする。

アカウント有効化の段取りは以下の手順（第9章のユーザーの登録に近い）
1. ユーザーの初期デフォルトの状態は「有効化されていない」(unactivated) にしておく
2. ユーザー登録(Signup)が行われたときに、有効化トークンと、それに対応する有効化ダイジェストを生成する
3. 有効化ダイジェストはDBに保存しておき、Signupしたユーザーには(有効化トークンをリンクに仕込んで)メールアドレスにメールを送信
4. ユーザーがメールのリンクをクリックしたら（本人だったら）、アプリケーションはメールアドレスをキーにしてDBでユーザーを探し、DB内に保存しておいた有効化ダイジェストと比較してトークンを認証する
5. ユーザーを認証できたら、ユーザーのステータスを「有効化されていない」から「有効化済み」(activated) に変更する
（認証できなければhomeを表示）

| 検索キー | string | digest | authentication |
|:--|:--|:--|:--|
| email | password | password_digest | authenticate(password) |
| id | remember_token | remember_digest | authenticated?(:remember, token) |
| email | activation_token | activation_digest | authenticated?(:activation, token) |
| email | reset_token | reset_digest | authenticated?(:reset, token) |
（[公式](https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation)より参考）


機能としては１０章の時点で基本的な機能実装は完成している。
本章（第１１章）+第１２章では応用的な部分の実装になる。


#11.1 AccountActivationsリソース
まずはGitで新機能用のトピックブランチを作成

```
$ git checkout -b account-activation
```
　
ユーザーを有効にしたときの変更後のデータモデル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/57554451-d498-be52-d624-19fbc1804e6a.png)
（[公式](https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation)より参考）

今回使うのは赤点線部分で、
**activation_digest**　→　有効化トークンと一致させるもの
**activated**　　　　　 →　有効化されてるか否かを真偽値(1 or 0)で、デフォでは偽(0)、「if user.activated?」などで使う
**activated_at**　　　　→　いつ有効化されたか（有効化の機構でなくログとして残す程度）

##11.1.1 AccountActivationsコントローラ
AccountActivationsリソースを作るために、AccountActivationsコントローラを生成する

```
$ rails generate controller AccountActivations
```

ルーティングにアカウント有効化用のリソース resources行(editアクション)を追加

```config/routes.rb

resources :users
resources :account_activations, only: [:edit] 
# (ユーザから) GET /account_activations/:id/edit
# params[:id] <== 有効化トークン入れる
# Controller: params[:id]
```

##11.1.2 AccountActivationのデータモデル

マイグレーションをコマンドラインで実行して先のデータモデル３つを追加すると、3つの属性が新しく追加される

```
$ rails generate migration add_activation_to_users activation_digest:string activated:boolean activated_at:datetime

同じ内容の２行版（>はシェル側の挿入で入力しない。上の連続が安定かも）
$ rails generate migration add_activation_to_users \
> activation_digest:string activated:boolean activated_at:datetime

Running via Spring preloader in process 23013
      invoke  active_record
      create    db/migrate/[timestamp ex. 2020...]_add_activation_to_users.rb
```
　
アカウント有効化用の属性とインデックスを追加するマイグレーション。デフォルト：falseを追加

```db/migrate/[timestamp]_add_activation_to_users.rb

class AddActivationToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :activation_digest, :string
    add_column :users, :activated, :boolean, default: false
    add_column :users, :activated_at, :datetime
  end
end
```

マイグレーションを実行

```
$ rails db:migrate
```

Userモデルにアカウント有効化のコードを追加する

```app/models/user.rb

class User < ApplicationRecord
  attr_accessor :remember_token, :activation_token
  before_save   :downcase_email
  before_create :create_activation_digest


# メールアドレスをすべて小文字にする
  def downcase_email
    self.email = self.email.downcase
  end
  
  # 有効化トークンとダイジェストを作成および代入する
  def create_activation_digest
    self.activation_token  = User.new_token
    self.activation_digest = User.digest(activation_token)
  # @user.activation_digest => ハッシュ値が入る
  end
```

サンプルユーザーの生成とテストのため、テスト時のサンプルとユーザーを事前に有効化する。なお、`Time.zone.now`はRailsの組み込みヘルパー(特に **.zone**)であり、サーバーのタイムゾーンに応じたタイムスタンプを返す。

```
$ ruby -e "puts Time.now"
2020-01-22 02:34:53 +0000
```

サンプルユーザーを最初から有効に

```db/seeds.rb

User.create!(name:  "Example User",
             email: "example@railstutorial.org",
             password:              "foobar",
             password_confirmation: "foobar",
             admin:     true,
             activated: true,
             activated_at: Time.zone.now)

99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  User.create!(name:  name,
              email: email,
              password:              password,
              password_confirmation: password,
              activated: true,
              activated_at: Time.zone.now)
end
```

fixtureのユーザーも有効(activated: true)、タイムスタンプの追加

```test/fixtures/users.yml

michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  admin: true
  activated: true
  activated_at: <%= Time.zone.now %>

archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

lana:
  name: Lana Kane
  email: hands@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

malory:
  name: Malory Archer
  email: boss@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

<% 30.times do |n| %>
user_<%= n %>:
  name:  <%= "User #{n}" %>
  email: <%= "user-#{n}@example.com" %>
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>
<% end %>
```

DBを初期化&サンプルデータを再度生成し直し、変更を反映

```
$ rails db:migrate:reset
$ rails db:seed
```
ここまでで、有効化トークンをダイジェスト（ハッシュ値）にして、DBに保存する流れを実装できた。


#11.2 アカウント有効化のメール送信
データのモデル化が終わったので、今度はアカウント有効化メールの送信に必要なコードを追加する。ここでは**Action Mailerライブラリ**を使ってUserのメイラーを追加していく。
これはUsersコントローラのcreateアクションで有効化リンクをメール送信するために用いるもので、構成がコントローラのアクションとよく似ているため、メールのテンプレートをビューと同じように定義できる。このテンプレートの中に有効化トークンとメールアドレス (= 有効にするアカウントのアドレス) のリンクを含める。

##11.2.1 送信メールのテンプレート
メイラーは、モデルやコントローラと同様に`rails generate`で生成できる。

```
$ rails generate mailer UserMailer account_activation password_reset

※補足
rails generate 
mailer（単数：controller等） 
UserMailer（その名前つける） 
account_activation password_reset　（メソッド２つ）
```

生成されたUserメイラー

```app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer

  def account_activation
    @greeting = "Hi"
    mail to: "to@example.org" #=> return mail boject
    
  end

  def password_reset
    @greeting = "Hi"

    mail to: "to@example.org"
  end
end
```

コントローラーのアクションっぽいが、メソッドと呼ぶ方がいい(らしい)。
読み込んだテキストがそれぞれビューとして呼び出される。
　

アカウント有効化メイラーのテキストビュー (自動生成)
app/views/user_mailer/account_activation.text.erb

```erb


User#account_activation

<%= @greeting %>, find me in app/views/user_mailer/account_activation.text.erb
```
account_activationメソッドの@greetingの文面「Hi!」が送り付けられる。
　

アカウント有効化メイラーのHTMLビュー (自動生成)
app/views/user_mailer/account_activation.html.erb

```erb

<h1>User#account_activation</h1>

<p>
  <%= @greeting %>, find me in app/views/user_mailer/account_activation.html.erb
</p>
```

生成されたApplicationメイラー

```app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end
```

MVCのモデル的な解釈としてActionメイラーは、
Mail（文面）   →　View
Mailer 　　　→　controller
のような位置付けになっている。
（コンソールでメイラーの親クラスを追うとコントローラーに繋がる）

生成されたApplicationメイラー

```app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com'
  layout 'mailer'
end
```

fromアドレスのデフォルト値を更新したアプリケーションメイラー

```app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: "noreply@example.com"
  layout 'mailer'
end
```

　
アカウント有効化リンクをメール送信する

```app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer

  def account_activation
    @greeting = "Hi"
    # (旧)　mail to: "to@example.org" #=> return mail boject
    #=> app/views/user_mailer/account_activation.text.erb
    #=> app/views/user_mailer/account_activation.html.erb
    
    mail to: @user.email #=> mail object
    
    # https://hogehoge.com/account_activation/:id/edit
    # :id <= @user.activation_tokenを入れる
    
  end
  
  # def account_activation(user)
  #   @user = user
  #   mail to: user.email, subject: "Account activation"
  # end

  def password_reset
    @greeting = "Hi"
    mail to: "to@example.org"
  end
end
```

メールのテンプレートを実際に有効化メール（返信用）

作りたいメールアドレスはこれ

```
account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com
```
`q5lt38hQDc_959PVoo6b7A`はnew_tokenメソッドで生成されたもので、URLで使えるようにBase64でエンコードされている。

###エンコード

エンコードとは、**「URL（ホームページの住所）で使えない文字を別の表現（形式）に置き換えること」**。チュートリアルでは`Base64`（64進数、すべてのデータをアルファベット(a~z, A~z)と数字(0~9)、一部の記号(+,/)の64文字）のエンコード形式で行う。エンコードしたデータを元の形に戻す行為は「デコード」と呼ぶ。

```
 あ　　　　　→　エンコード　→　%E3%81%82
%E3%81%82　→　デコード　　→　あ
```

＜参考＞
[エンコード (encode)](https://wa3.i-3-i.info/word135.html)
[URLエンコード・デコード](https://tech-unlimited.com/urlencode.html)
[base64ってなんぞ？？理解のために実装してみた](https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4)


気をつけるべきは、メールのURLが`@`でなく`%40`担っていること。これは、「エスケープ」と呼ばれる手法で、通常URLでは扱えない文字を扱えるようにするために変換されている。


このメールによってRailsサーバーで行いたいのは以下の処理。

---
①ユーザーをメールアドレス(finde_byで@user.email)で検索
②有効化トークンを認証(authenticated?で@user.activation_token)

---

　　
アカウント有効化のテキストビュー
app/views/user_mailer/account_activation.text.erb

```erb

Hi <%= @user.name %>,

Welcome to the Sample App! Click on the link below to activate your account:

<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
```

アカウント有効化のHTMLビュー
app/views/user_mailer/account_activation.html.erb

```erb

<h1>Sample App</h1>

<p>Hi <%= @user.name %>,</p>

<p>
Welcome to the Sample App! Click on the link below to activate your account:
</p>

<%= link_to "Activate", edit_account_activation_url(@user.activation_token,
                                                    email: @user.email) %>
```

ちなみに、コンソールを開いてCGIモジュールのescapeメソッドでメールアドレスや"Don't panic!"の文字列を確認すると、以下のようにエスケープできている。

```
CGI.escape('foo@example.com')
 => "foo%40example.com" 
2.6.3 :002 > CGI.escape("Don't panic!")
 => "Don%27t+panic%21" 
```


##11.2.2 送信メールのプレビュー

テンプレートの実際の表示を簡単に確認するために、**メールプレビュー**という手法を使う。***Railsでは、特殊なURLにアクセスするとメールのメッセージをその場でプレビューすることができる***ので、メールを実際に送信しなくてもok。
これを利用するためには、アプリケーションのdevelopment環境の設定を変更する。筆者の環境ではクラウドIDEのため下記、ローカルなら*`localhost:3000`*。

```config/environments/development.rb

# Don't care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = true #=> デフォfalseから変更
  config.action_mailer.delivery_method = :test #=> rails場でメールをログとして出す（実際には送らない）
  host = '******.vfs.cloud9.ap-northeast-1.amazonaws.com/' # sample_app(preview)のドメイン,https://の後
  config.action_mailer.default_url_options = { host: host, protocol: 'https' }
```

Userメイラープレビュー アカウント有効化のプレビューメソッド

```test/mailers/previews/user_mailer_preview.rb

 # Preview this email at
 # http://localhost:3000/rails/mailers/user_mailer/account_activation
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user) #=> mail object
  end
```

先ほどのリンクにメイラーを付け足してプレビューで検索確認。指定のURLでアカウント有効化メールをプレビューできるようになった。（サーバーは再起動）

```
******.vfs.cloud9.ap-northeast-1.amazonaws.com/rails/mailers/user_mailer/account_activation
```
---
メール

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/e35c2e7d-e33a-81a1-0d4d-2808b80ddcc6.png)

Accountをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/7cd08249-55f0-5909-618b-60a2b5998fd4.png)


---
先ほどのtext(メール添付SS下部リンクは省略)。
account_activations/******(平文)/edit?email=example%40railstutorial.org

<img width="553" alt="スクリーンショット 2020-01-28 18.33.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/b874569e-db2b-60c8-a3cd-f69ee12acfc9.png">

---

##11.2.3 送信メールのテスト

Railsによって自動生成されているテスト例を使い、メールプレビューのテストも作成してプレビューをダブルチェックする。




```test/mailers/user_mailer_test.rb

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 [user.email], 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
```

テストファイル内のドメイン名を正しく設定するため、テストのドメインホストを設定する。

```config/environments/test.rb

config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'example.com' }
```

これでテストし、通過。
※　メーラーは書き方が少し異なる。

```

$ rails test:mailers
```

##11.2.4 ユーザーのcreateアクションを更新

ユーザー登録にアカウント有効化を追加する。あとはユーザー登録を行うcreateアクションに数行追加するだけで、メイラーをアプリケーションで実際に使えるようになる。
前回はユーザーのプロフィールページにリダイレクトしていたが、アカウント有効化を実装するうえでは無意味な動作のため、リダイレクト先をルートURLに変更する。
　
ユーザー登録にアカウント有効化を追加する。

```app/controllers/users_controller.rb

def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.account_activation(@user).deliver_now
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
```

アカウント作成　→　ログインすると、サーバー（ターミナル）でメールの文面が確認できる。
これが、**11.2.3**であった:testのもの。

```
config.action_mailer.delivery_method = :test
```

![スクリーンショット 2020-01-29 1.40.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/760f7182-9fb4-435e-3293-4d519e15f050.png)

ただしテストでは、singnupで引っ掛かりが起きる。これはリダイレクト先をプロフィールページからルートURLに変更し、かつユーザーは以前のようにログインしないようにしているため（機能的に問題なくてもNG）。

失敗するテストを一時的にコメントアウトする

```test/integration/users_signup_test.rb

# assert_template 'users/show'
# assert is_logged_in?
```

#11.3 アカウントを有効化する
ここからは先ほどのやりとりのうち、下記を実装。
3. （ユ）→（サ）　「来たメールのURLクリックしたよ」
4. 【NG】（サ）→（ユ）　「違うからもう一回だね」
4. 【OK】（サ）→（ユ）　「登録するね（DB書き込みするか）」


##11.3.1 authenticated?メソッドの抽象化

**authenticated?**メソッドは現在、**remember_digest**の記憶トークン対応のみのため、これでは今回実装したい有効化トークンも含めて２つ書く手間でDRYとはいえない。
そのため、今回は**send**メソッドを使い**動的ディスパッチ**（メソッドの実行直前に実行するメソッドを呼び出すこと）として抽象化する。

これだけだとよくわからないのでコンソールを開く。

```
2.6.3 :001 > a = "hogehoge"
 => "hogehoge" 
2.6.3 :002 > a.send("length")
 => 8 
2.6.3 :003 > a.length
 => 8 
```

sendはメソッド名を文字列にできる（文字列なので式展開ができる）。つまり

```
hensuu = "activation"
.send("#{hensuu}_digest") #=> akj3a3%4au#(前回)
hensuu = "remember"
.send("#{hensuu}_digest") #=> nil（今回 まだない）
```

のような切り替えができる。
`シンボル表記（:activation）`でもok

抽象化されたauthenticated?メソッド。引数を追加したり

```app/models/user.rb

# トークンがダイジェストと一致したらtrueを返す
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end
```

このままではREDのため、
sessions_helperのcurrent_user内でauthenticated?メソッドを抽象化

```app/helpers/sessions_helper.rb
# 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.signed[:user_id]) #=> signedで復号化
      user = User.find_by(id: user_id)
      #旧(引数不足) if user && user.authenticated?(cookies[:remember_token])
      if user && user.authenticated?(:remember, cookies[:remember_token])
```

Userテストでも

```test/models/user_test.rb

test "authenticated? should return false for a user with nil digest" do
    assert_not @user.authenticated?(:remember, '')
  end
```

これでテストは通過

##11.3.2 editアクションで有効化

editアクションの実装。paramsハッシュで渡されたメールアドレスに対応するユーザーを認証（有効かどうか？）。


```app/controllers/account_activations_controller.rb

class AccountActivationsController < ApplicationController
  def edit
    user = User.find_by(email: params[:email])
    if user && !user.activated? && user.authenticated?(:activation, params[:id])
      #Success => Signup
      user.update_attribute(:activated,    true)
      user.update_attribute(:activated_at, Time.zone.now)
      log_in user
      flash[:success] = "Account activated!"
      redirect_to user
    else
      #Failure
      flash[:danger] = "Invalid activation link"
      redirect_to root_url
    end
  end
end
```

ユーザーの有効化が役に立つためには、ユーザーが有効である場合にのみログインできるようにログイン方法を変更する必要がある。
そのためには、user.activated?がtrueの場合にのみログインを許可し、そうでない場合はルートURLにリダイレクトしてwarningで警告を表示する。

```app/controllers/sessions_controller.rb

def create
    user = User.find_by(email:params[:session][:email])
   if user && user.authenticate(params[:session][:password])
      if user.activated? #=>上かつアクティベーションしてるか？
        log_in user
        params[:session][:remember_me] == '1' ? remember(user) : forget(user)
        redirect_back_or user
      else #=> してなかったら
        message  = "Account not activated. "
        message += "Check your email for the activation link."
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end
```

アクティベーションしてないユーザーがログインしようとすると、通知が出るように。
![スクリーンショット 2020-01-30 12.33.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/20b6ed14-384c-2609-a851-f8455e33a5bd.png)


##11.3.3 有効化のテストとリファクタリング

アカウント有効化の統合テストを追加する。内容のベースは「正しい情報でユーザー登録を行った場合」のテスト。

ユーザー登録のテストにアカウント有効化を追加する。

```test/integration/users_signup_test.rb

  def setup
    ActionMailer::Base.deliveries.clear
  end

省略

test "valid signup information with account activation" do #=> アカウント有効化を追加
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    
    assert_equal 1, ActionMailer::Base.deliveries.size
    user = assigns(:user)
    assert_not user.activated?
    # 有効化していない状態でログインしてみる
    log_in_as(user)
    assert_not is_logged_in?
    # 有効化トークンが不正な場合
    get edit_account_activation_path("invalid token", email: user.email)
    assert_not is_logged_in?
    # トークンは正しいがメールアドレスが無効な場合
    get edit_account_activation_path(user.activation_token, email: 'wrong')
    assert_not is_logged_in?
    # 有効化トークンが正しい場合
    get edit_account_activation_path(user.activation_token, email: user.email)
    assert user.reload.activated?
    follow_redirect! # assert_template 'users/show'
    assert_template 'users/show'
    assert is_logged_in? # assert is_logged_in? #=> signup 終えた人はログインも終わってるか？
  end
```

###assignsメソッド
assignsメソッドとは、「**コントローラーのインスタンス変数をテストするメソッド**」。引数にはインスタンス変数(@user)をシンボル型(:user)で渡して使う。

参考
[Rspecの基礎](https://qiita.com/chopesu_se/items/a6a1fcff34d370eeb728)

これでテストは通過

次はユーザー操作の一部をコントローラからモデルに移動する。
流れとしては、
①activateメソッドを作成してユーザーの有効化属性を更新
②send_activation_emailメソッドを作成して有効化メールを送信

Userモデルにユーザー有効化メソッドを追加する

```app/models/user.rb

private

# アカウントを有効にする
  def activate
    update_attribute(:activated,    true)
    update_attribute(:activated_at, Time.zone.now)
  end

# 有効化用のメールを送信する
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end
```

ユーザーモデルオブジェクト経由でアカウントを有効化する

```app/controllers/account_activations_controller.rb

def edit
    user = User.find_by(email: params[:email])
    if user && !user.activated? && user.authenticated?(:activation, params[:id])
      #Success => Signup
      #不要 user.update_attribute(:activated,    true)
      #不要 user.update_attribute(:activated_at, Time.zone.now)
      user.activate
      log_in user
      flash[:success] = "Account activated!"
      redirect_to user
```

ユーザーモデルオブジェクトからメールを送信する

```app/controllers/users_controller.rb

# POST /users
  def create
    @user = User.new(user_params)
      if @user.save
        # Sucess
        @user.send_activation_email #=>signup後にactivation_emailを送りつける
        #不要 UserMailer.account_activation(@user).deliver_now
        flash[:info] = "Please check your email to activate your account."
        redirect_to root_url
      else
```

これでテストは通過


#11.4 本番環境でのメール送信

##SendGridの使い方
本番環境からメール送信するために、「SendGrid」というHerokuアドオン（Webサービス）を利用してアカウントを検証する。このサービスの利用のためにはクレジットカードの登録が必要（1日のメール数が最大400通までという制限があるが無料）。利用の場合は下記リンクにアクセスして入力する。
https://heroku.com/verify

参考
[heroku へクレジットカード登録](https://wp.developapp.net/?p=5250)

![スクリーンショット 2020-01-30 14.04.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/eb924f4b-fa5a-3774-71af-9bcef84b3773.png)

アドオンをアプリケーションに追加するには、次のコマンドを実行。

```rb
$ heroku addons:create sendgrid:starter

> heroku: Press any key to open up the browser to login（ログインしたくば、任意のキーを押されよ）　
→　Enter押す
> Opening browser to https://cli-auth.heroku.com/...
 ›   Warning: Cannot open browser.
→　リンクからログインする（下記SS）
> Logging in... done
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/e6197978-38e2-eb8a-efa1-cdf6f3f99f1c.png)

ログインして利用可能状態になった。

アプリケーションでSendGridアドオンを使うには、
1. production環境のSMTPに情報を記入
2. 本番Webサイトのアドレスをhost変数に定義
が必要。

Railsのproduction環境でSendGridを使う設定

```config/environments/production.rb

Rails.application.configure do
  .
  .
  .
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  host = '<自分のドメイン>.herokuapp.com'
  config.action_mailer.default_url_options = { host: host }
  ActionMailer::Base.smtp_settings = {
    :address        => 'smtp.sendgrid.net',
    :port           => '587',
    :authentication => :plain,
    :user_name      => ENV['SENDGRID_USERNAME'],
    :password       => ENV['SENDGRID_PASSWORD'],
    :domain         => 'heroku.com',
    :enable_starttls_auto => true
```

自分のドメインはheokuのアプリ開くかpersonalなどのページから確認。
ちなみに、SendGridの環境変数などを知りたい時は、

```
$ heroku config:get SENDGRID_USERNAME
> app686...@heroku.com(数字は適当)
$ heroku config:get SENDGRID_PASSWORD
>haiuekuryha436798
```

などで、herokuのスタータープランなどで作ってくれたメール等確認できる。

※　注意
ここで重要なのが、

***user_name***と***password***のハッシュに実際の値を記入しないこと

ソースコードに直接機密情報を書き込むのは危険であり（ソースコードがハックされたらout）、そのような情報は環境変数に記述し、そこからアプリケーションに読み込む必要がある。

ここで一応テスト。
テストは通過

ここからはいつも通り、

```
$ git add -A
$ git commit -m "Finish ch11"
$ git checkout master
$ git merge account-activation
$ rails t
$ git push heroku master
$ heroku pg:reset DATABASE
> 左赤字（アプリ名〇〇_app）
$ heroku run rails db:migrate
$ heroku run rails db:seed
```

本番環境でアカウント作成して

![スクリーンショット 2020-01-30 15.03.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/d678f576-5780-56d6-8dc1-c20a4ae8e9b6.png)


ちゃんと自分のGmailにメールが送信された

![スクリーンショット 2020-01-30 15.08.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/559cbc05-21be-70fb-136f-cce447d8a68d.png)

`Activate`をクリックでログインに成功。

![スクリーンショット 2020-01-30 15.12.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/c6845c40-1bb3-b16d-3136-21377eec08ce.png)

ちなみに忘れたりしてもう一度`Activate`をクリックすると既に有効化されているため、下記のような表示になる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/873d0876-5d02-cada-b868-e494f8db4f1d.png)

終了。
　

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

[Ruby on Rails チュートリアル 第11章 アカウント有効化(AcctionMailer Activation)やSendGridの使い方など](https://qiita.com/bitcoinjpnnet/items/3a544d9183f4008f6aa2)