【Railsチュートリアル】第11章 アカウントの有効化①

# はじめに
アカウントを有効化するステップを新規登録の途中に差し込み、本当にそのメールアドレスの持ち主なのかどうかを確認できるようにする。

# 11.1 AccountActivationsリソース
セッション機能（8.1）を使って、アカウントの有効化という作業を「リソース」としてモデル化する。

## 11.1.1 AccountActivationsコントローラ

```
resources :account_activations, only: [:edit]
```
URL「/account_activation/トークン/edit」にGETがリクエストされたらeditアクションを呼び出す。

### 演習 1
現時点でテストスイートを実行すると green になることを確認してみましょう。
確認のみなので省略。

### 演習 2
表 11.2の名前付きルートでは、_pathではなく_urlを使うように記してあります。なぜでしょうか? 考えてみましょう。ヒント: 私達はこれからメールで名前付きルートを使います。
メール本文のURLからアクセスするから。

## 11.1.2 AccountActivationのデータモデル
仮想的な属性を使ってハッシュ化した文字列をデータベースに保存するようにする。

### 演習 1
本項での変更を加えた後、テストスイートが green のままになっていることを確認してみましょう。

GREEN

### 演習 2
コンソールからUserクラスのインスタンスを生成し、そのオブジェクトからcreate_activation_digestメソッドを呼び出そうとすると（Privateメソッドなので）NoMethodErrorが発生することを確認してみましょう。また、そのUserオブジェクトからダイジェストの値も確認してみましょう。

```
>> user = User.new
   (4.5ms)  SELECT sqlite_version(*)
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil, remember_digest: nil, admin: nil, activation_digest: nil, activated: false, activated_at: nil>
>> user.create_activation_digest
Traceback (most recent call last):
        1: from (irb):2
NoMethodError (private method `create_activation_digest' called for #<User:0x00007f5eb1cd6088>)
Did you mean?  restore_activation_digest!

>> user.activation_digest
=> nil
```

### 演習 3
リスト 6.35で、メールアドレスの小文字化にはemail.downcase!という（代入せずに済む）メソッドがあることを知りました。このメソッドを使って、リスト 11.3のdowncase_emailメソッドを改良してみてください。また、うまく変更できれば、テストスイートは成功したままになっていることも確認してみてください。

```ruby:app/models/user.rb

def downcase_email
  self.email.downcase!
end
```

GREEN

# 11.2 アカウント有効化のメール送信
Action Mailerライブラリを使ってUserのメイラーを追加する。

## 11.2.1 送信メールのテンプレート

```:Userメイラーの生成
$ rails generate mailer UserMailer account_activation password_reset
```

$ rails generate [メイラー名][アクション名][アクション名]


app/views/user_mailer/account_activation.text.erb
app/views/user_mailer/account_activation.html.erb
ブラウザと違って、メールボックスによってHTMLを描画できないものもあるので、textも用意している。


```ruby:app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com'
    # どこから送るか
  layout 'mailer'
    # デフォルトではどんなレイアウトを使うのか。
end
```
`application_mailer.rb`ではメイラー全体の設定をする。


```ruby:app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer

  # Subject can be set in your I18n file at config/locales/en.yml
  # with the following lookup:
  #
  #   en.user_mailer.account_activation.subject
  #
  def account_activation
    @greeting = "Hi"
      # インスタンス変数を展開

    mail to: "to@example.org"
  end

  # 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
end
```
`user_mailer.rb`ではメイラーでは何をするのかを設定する。


### 演習 1
コンソールを開き、CGIモジュールのescapeメソッド（リスト 11.15）でメールアドレスの文字列をエスケープできることを確認してみましょう。このメソッドで"Don't panic!"をエスケープすると、どんな結果になりますか?

```
>> CGI.escape("Don't panic!")
=> "Don%27t+panic%21"
```
**エスケープ**：使えない文字列を使える文字列に変換する。

## 11.2.2 送信メールのプレビュー
メールのメッセージをその場でプレビューすることができるメールプレビューを設定する。

development.rb: 開発環境用 / test.rb: テスト環境用 / production.rb : 本番環境用

```
host = 'localhost:3000' # ローカル環境用
config.action_mailer.default_url_options = { host: host, protocol: 'http' }
```
cloud9を使っていないので、上記を選択。
host〜をコピペすると、自分が開発しているhost（ドメイン名）にGETリクエストが送れないためにエラーが起きます。


### 演習 1
Railsのプレビュー機能を使って、ブラウザから先ほどのメールを表示してみてください。「Date」の欄にはどんな内容が表示されているでしょうか?

アクセスした日時が表示される。

## 11.2.3 送信メールのテスト
メールプレビューのテストも作成して、プレビューをダブルチェックできるようする。

リスト 11.21: テストのドメインホストを設定する

```ruby:test/mailers/user_mailer_test.rb

require 'test_helper'

class UserMailerTest < ActionMailer::TestCase

  test "account_activation" do
    user = users(:Michael)
      # users(:Michael)をuserに代入
    user.activation_token = User.new_token
      # トークン情報を生成してuser.activation_tokenに代入
    mail = UserMailer.account_activation(user)
      # UserMailer.account_activation(user)をmail変数に代入

    # ここから確認のテスト
    assert_equal "Account activation", mail.subject
      # mail.subject（件名）を確認
    assert_equal [user.email], mail.to
      # mail.to（送り先）を確認
    assert_equal ["noreply@example.com"], mail.from
      # mail.from（送り元）を確認
    assert_match user.name,               mail.body.encoded
      # mail.body（本文）にuser.name（名前）が入っているか確認
    assert_match user.activation_token,   mail.body.encoded
      # mail.body（本文）にuser.activation_token（トークン）が入っているか確認
    assert_match CGI.escape(user.email),  mail.body.encoded
      # mail.body（本文）にuser.emailの「＠」が
      # CGI.escape（エスケープ処理）されているものが入っているか確認
  end
end
```

### 演習 1
この時点で、テストスイートが green になっていることを確認してみましょう。
動作確認のみなので省略。

### 演習 2
リスト 11.20で使ったCGI.escapeの部分を削除すると、テストが red に変わることを確認してみましょう。
動作確認のみなので省略。

## 11.2.4 ユーザーのcreateアクションを更新
ユーザー登録を行うcreateアクションにコードを追加し、メイラーをアプリケーションで実際に使えるようにする。
signup直後のログインの廃止（本人確認前にログインできないようにする）して、メールチェックしてもらえるように促す。

```ruby:app/controllers/users_controller.rb

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
      # app/models/user.rb
      # attr_accessor :remember_token, :activation_token
      # before_save   :downcase_email

      # def create_activation_digest
      #  self.activation_token  = User.new_token
      #   self.activation_digest = User.digest(activation_token)
      # end

    if @user.save

      # app/models/user.rb
      # before_create :create_activation_digest

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

### 演習 1
新しいユーザーを登録したとき、リダイレクト先が適切なURLに変わったことを確認してみましょう。その後、Railsサーバーのログから送信メールの内容を確認してみてください。有効化トークンの値はどうなっていますか?

適切なURL: root_url

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

Hi moutoon,

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

http://localhost:3000/account_activations/iXav_kpcTA4krSc-3LR3DA/edit?email=moutoonm342%40gamil.com
```

### 演習 2
コンソールを開き、データベース上にユーザーが作成されたことを確認してみましょう。また、このユーザーはデータベース上にはいますが、有効化のステータスがfalseのままになっていることを確認してください。

```
>> user = User.find_by(name: "moutoon")
   (0.1ms)  begin transaction
  User Load (5.8ms)  SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "moutoon"], ["LIMIT", 1]]
=> #<User id: 101, name: "moutoon", email: "moutoonm342@gamil.com", created_at: "2021-03-02 02:28:47", updated_at: "2021-03-02 02:28:47", password_digest: [FILTERED], remember_digest: nil, admin: nil, activation_digest: "$2a$12$9y5elJ64pu1EC1COuJfdU.95jcOd0TdqpDyHz7W5m1M...", activated: false, activated_at: nil>
```

activated: falseになっている。