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

# 11.3 アカウントを有効化する
AccountActivationsコントローラのeditアクションを書いていく。アクションへのテストを書き、しっかりとテストできていることが確認できたら、AccountActivationsコントローラからUserモデルにコードを移していく作業（リファクタリング）にも取り掛かかる。

## 11.3.1 authenticated?メソッドの抽象化
sendメソッドを使うと共通化できる。

**sendメソッド** とは？
渡されたオブジェクトに「メッセージを送る」ことによって、呼び出すメソッドを動的に決めることができるメソッド。

```
>> user = User.first

>> user.activation_digest
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"

>> user.send(:activation_digest)
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"

>> user.send("activation_digest")
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"
  # 文字列も渡すことができる

>> attribute = :activation
  # シンボルを代入

>> user.send("#{attribute}_digest")
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"
  # 式展開を使って渡す

```

### 演習 1
コンソール内で新しいユーザーを作成してみてください。新しいユーザーの記憶トークンと有効化トークンはどのような値になっているでしょうか? また、各トークンに対応するダイジェストの値はどうなっているでしょうか?

```
>> user = User.create(name: "sample", email: "sample@email.com", password: "sample", password_confirmation: "sample")
   (4.2ms)  SELECT sqlite_version(*)
   (0.1ms)  begin transaction
  User Exists? (1.8ms)  SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "sample@email.com"], ["LIMIT", 1]]
  User Create (8.4ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest", "activation_digest") VALUES (?, ?, ?, ?, ?, ?)  [["name", "sample"], ["email", "sample@email.com"], ["created_at", "2021-03-02 04:11:18.445221"], ["updated_at", "2021-03-02 04:11:18.445221"], ["password_digest", "$2a$12$.NT19iae7r0WhFR2OcikiemF5WD3QvT0aA4/LJUSZ9UHYOt4XB0T."], ["activation_digest", "$2a$12$jKN4cC3pqjMUIKs4/EvhIOOza0GueGUAKzsGgaMLixzoGHv/Moo5W"]]
   (5.5ms)  commit transaction
=> #<User id: 102, name: "sample", email: "sample@email.com", created_at: "2021-03-02 04:11:18", updated_at: "2021-03-02 04:11:18", password_digest: [FILTERED], remember_digest: nil, admin: nil, activation_digest: "$2a$12$jKN4cC3pqjMUIKs4/EvhIOOza0GueGUAKzsGgaMLixz...", activated: false, activated_at: nil>
```

### 演習 2
リスト 11.26で抽象化したauthenticated?メソッドを使って、先ほどの各トークン/ダイジェストの組み合わせで認証が成功することを確認してみましょう。

```
>> user.remember_token
=> nil
>> user.remember_digest
=> nil
>> user.activation_token
=> "DQKQr9KfehJzT6PzsjrEdQ"
>> user.activation_digest
=> "$2a$12$jKN4cC3pqjMUIKs4/EvhIOOza0GueGUAKzsGgaMLixzoGHv/Moo5W"
```

## 11.3.2 editアクションで有効化
editアクションを書いていく。

```
  # GET /account_acrivations/:id/edit
    user = User.find_by(email: params[:email])
      # emailを元にuserを探して変数userに代入
    if user && !user.activated? && user.authenticated?      (:activation, params[:id])
      # 左: nilかどうかを確認
      # 中: userがactivatedされていないかを確認
      # 右: :activation, params[:id]の2つで認証する
      user.update_attribute(:activated,    true)
      user.update_attribute(:activated_at, Time.zone.now)
      log_in user
        # ログイン
      flash[:success] = "Account activated!"
      redirect_to user
        # プロフィールページにアクセス
    else
      flash[:danger] = "Invalid activation link"
      redirect_to root_url
    end
  end
```

### 演習 1
コンソールから、11.2.4で生成したメールに含まれているURLを調べてみてください。URL内のどこに有効化トークンが含まれているでしょうか?

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

Hi test,

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

http://localhost:3000/account_activations/exKxGElXRHOQ6VyC0ia4MA/edit?email=test%40example.com
```

この部分がトークン: `account_activations/exKxGElXRHOQ6VyC0ia4MA/`

### 演習 2
先ほど見つけたURLをブラウザに貼り付けて、そのユーザーの認証に成功し、有効化できることを確認してみましょう。また、有効化ステータスがtrueになっていることをコンソールから確認してみてください。

```
>> user = User.find_by(name: "test")
  User Load (3.3ms)  SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "test"], ["LIMIT", 1]]
=> #<User id: 103, name: "test", email: "test@example.com", created_at: "2021-03-02 04:42:10", updated_at: "2021-03-02 04:45:05", password_digest: [FILTERED], remember_digest: nil, admin: nil, activation_digest: "$2a$12$MmJY.TpGghnO.LdaF6GSPO8xQUWDIPzzl0L3ISc5FYU...", activated: true, activated_at: "2021-03-02 04:45:05">

>> user.activated
=> true
```

## 11.3.3 有効化のテストとリファクタリング
アカウント有効化の統合テストを追加する。

```ruby:test/integration/users_signup_test.rb
test "valid signup information with account activation" do
  get signup_path
    # signup_pathにアクセスする
  assert_difference 'User.count', 1 do
    # ユーザー数が1つ増えているか確認。増えたユーザーの情報は以下。
    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
    # メールを1つ送っているか確認
  user = assigns(:user)
    # バリデーションされていない@userにアクセスする
  assert_not user.activated?
    #  userが有効化されていないか確認
  log_in_as(user)
    # 有効化していない状態でログインしてみる
  assert_not is_logged_in?
    # ログインできなかったらtrue
  get edit_account_activation_path("invalid token", email: user.email)
    # edit_account_activation_pathにトークンとメールアドレスを渡してアクセス
  assert_not is_logged_in?
    # ログインできなかったらtrue
  get edit_account_activation_path(user.activation_token, email: 'wrong')
    # トークンは正しいが、メールアドレスが無効の場合でアクセス
  assert_not is_logged_in?
    # ログインできなかったらtrue
  get edit_account_activation_path(user.activation_token, email: user.email)
    # トークンもメールアドレスも有効の場合でアクセス
  assert user.reload.activated?
    # ユーザーが更新されたらtrue
  follow_redirect!
    # リダイレクトされる
  assert_template 'users/show'
    # users/showを描画しているか確認
  assert is_logged_in?
    # ログインできていたらtrue
  end
```

### 演習 1
リスト 11.35にあるactivateメソッドはupdate_attributeを２回呼び出していますが、これは各行で１回ずつデータベースへ問い合わせしていることになります。リスト 11.39に記したテンプレートを使って、update_attributeの呼び出しを１回のupdate_columns呼び出しにまとめてみましょう。これでデータベースへの問い合わせが１回で済むようになります（注意！update_columnsは、モデルのコールバックやバリデーションが実行されない点がupdate_attributeと異なります）。また、変更後にテストを実行し、 green になることも確認してください。

```ruby:app/models/user.rb
# アカウントを有効にする
def activate
  update_columns(activated: true, activated_at: Time.zone.now)
end
```

### 演習 2
現在は、/usersのユーザーindexページを開くとすべてのユーザーが表示され、/users/:idのようにIDを指定すると個別のユーザーを表示できます。しかし考えてみれば、有効でないユーザーは表示する意味がありません。そこで、リスト 11.40のテンプレートを使って、この動作を変更してみましょう9 。なお、ここで使っているActive Recordのwhereメソッドについては、13.3.3でもう少し詳しく説明します。

```ruby:app/controllers/users_controller.rb
def index
  @users = User.where(activated: true).paginate(page: params[:page])
end

def show
  @user = User.find(params[:id])
  redirect_to root_url and return unless @user.activated?
end
```

# 11.4 本番環境でのメール送信
サンプルアプリケーションの設定を変更し、production環境で実際にメールを送信できるようにする。

### 演習 1
### 演習 2

`$ heroku push`時に`Precompiling assets failed.`が出て、解決できなかったので後に回します。

### エラーメモ
たぶんここで引っかかってるけど、調べてもよくわからない。

```
$ git push heroku
.
.
.
remote:        SyntaxError: /tmp/build_bb915e0f/config/environments/production.rb:84: syntax error, unexpected '\n', expecting =>
.
.
.
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
remote: 
remote:  !     Push failed
.
.
.
```

# さいごに
メール認証難しかったです！