はじめに
アカウントを有効化するステップを新規登録の途中に差し込み、本当にそのメールアドレスの持ち主なのかどうかを確認できるようにする。
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メソッドを改良してみてください。また、うまく変更できれば、テストスイートは成功したままになっていることも確認してみてください。
def downcase_email
self.email.downcase!
end
GREEN
11.2 アカウント有効化のメール送信
Action Mailerライブラリを使ってUserのメイラーを追加する。
11.2.1 送信メールのテンプレート
$ 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も用意している。
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
# どこから送るか
layout 'mailer'
# デフォルトではどんなレイアウトを使うのか。
end
application_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: テストのドメインホストを設定する
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直後のログインの廃止(本人確認前にログインできないようにする)して、メールチェックしてもらえるように促す。
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になっている。