#はじめに
Rails tutorialの第11章 アカウントの有効化
ついて備忘録として、自分なりに整理しておく。
(※テストに関する記載はなし)
#準備
###アカウント有効化用のコントローラを作成する。
$ rails generate controller AccountActivations
###routeを編集する。
後々、editの名前付きルートが必要になるので名前付きルートを使用できるようにする為に、ルートファイルにresourcesを追加する。
Rails.application.routes.draw do
-
-
resources :account_activations, only:[:edit]
end
###カラムを3つ加する。
アカウント有効化の処理に必要な3つのカラムをテーブルに追加する。
まずはマイグレーションファイルを作成する。
$ rails g migration add_activation_to_users
マイグレーションファイルを編集し、以下3つのカラムを追加し、rails db:migrate
を実行する。
・activation_digest
・activated
・activated_at
def change
add_column :users, :activation_digest, :string
add_column :users, :activated, :boolean, default: false
add_column :users, :activated_at, :datetime
end
###仮想カラムを追加する。
第9章でremember_token
カラムを追加した時のように、attr_accessor
を使用して仮想カラムを追加する。
attr_accessor :remember_token, :activation_token
###コールバックを追加する。
before_create
を使用して、ユーザーオブジェクトを作成(create)する前にデータベースにactivation_token
とactivation_digest
を作成するメソッドを追加しておく。
before_create :create_activation_digest
#有効化トークンとダイジェストをデータベースに作成する。
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
#メールの送信処理
以降はメールの送信処理について記載していく。
###メイラーを作成する
rails generate
コマンドで作成可能。
rails g mailer [メイラー名] [メイラーのメソッド名(アクション名)]..
作成されるファイルは以下の通り。
・メイラーファイル
・ビューのテンプレート2つ(テキスト形式とHTMl形式)
・テストファイル
・プレビューファイル
$ rails g mailer UserMailer account_activation password_reset
create app/mailers/user_mailer.rb
invoke erb
create app/views/user_mailer
create app/views/user_mailer/account_activation.text.erb
create app/views/user_mailer/account_activation.html.erb
create app/views/user_mailer/password_reset.text.erb
create app/views/user_mailer/password_reset.html.erb
invoke test_unit
create test/mailers/user_mailer_test.rb
create test/mailers/previews/user_mailer_preview.rb
###アプリケーション全体の設定を編集する。
app/mailers/application_mailer.rb
を開く。
メールの送信元アドレスを設定する。
aclass ApplicationMailer < ActionMailer::Base
default from: 'noreply@example.com' #メールの送信元アドレスを指定。
layout 'mailer'
end
###メールの送信処理を書き込む
app/mailers/[メイラー名].rb
でメイラーファイルを開く。
メイラーファイルでは、mailメソッド
を利用して、送信処理を作成するファイル。
それぞれのメソッドに対してmailメソッドを書き込み、送信処理を加えていく。
mailメソッドで利用可能な主なオプションは以下の通り。
from:送信元メールアドレス
subject:メールの件名
to:メールの送信先アドレス
etc...
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user #インスタンス変数を作成
mail(
to: user.email,
subject: "Account Activation"
)
end
end
###ビューのテンプレートを編集する。
送信されるメールのビューを編集していく。(textファイルとHTMLファイルの両方)
ビューには、アカウントを有効化する為に必要なURLを書き込む。
Hi <%= @user.name %>,
Welcome to the Sample App! Click on the link below to activate your account:
<%= edit_account_activation(@user.activation_token, email: @user.email) %>
<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(@user.activation_token, email: @user.email) %>
テンプレートを作成したら、プレビューで確認することができる。
###Createアクションを編集する
次はusers_controller.rb
ファイルの Createアクション
を書き換えていく。
[変更前]
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
[変更後]
・UserMailer.account_activation(@user).deliver_now #deliver_now
↑UserMailer
のaccount_activationメソッド
を介して、ユーザーにメールを送信させる (deliver_nowは今すぐメールを送信したい場合に使用する)
・この時点でログインは完了していないので、セッションを持たせてはいけない為log_in @user
は削除し、リダイレクト先もユーザーの詳細ページではなくrootページに変更しておく。
def create
@user = User.new(user_params)
if @user.save
UserMailer.account_activation(@user).deliver_now
flash[:info] = "アカウントを有効化する為にメールを確認してください"
redirect_to root_url
else
render "new"
end
end
###autheticateメソッドを編集する。
次にuser.rb
に記載していたauthenticatedメソッド
を書き換えていく。
アカウント有効化の処理の中で、トークンとダイジェストを比較する処理が発生する。この処理は「第9章 発展的なログイン」で既に行っている(remember_token
とremember_digest
を比較するメソッド)。
しかし、9章の時に書いたコードでは、remember_token
とremember_digest
しか比較をしないので、activation_token
とactivation_digest
も比較できるようにコードを書き換える必要がある。
その為に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"
これを利用して、authenticatedメソッド
を書き換えていく。
[変更前]
def authenticated?(remember_token)
if remember_digest.nil?
false
else
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
[変更後]
・sendメソッド
を使用して、remember
とactivation
の両方を入れられるようなコードにする。
def authenticated?(attribute, token)
digest = self.send("#{attribute}_digest")
if digest.nil?
false
else
BCrypt::Password.new(digest).is_password?(token)
end
end
###current_userメソッドを書き換える。
authenticated?メソッド
を書き換えたことによって、authenticated?メソッド
を使用していたコードを書き換える必要がある。
[変更前]
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
@current_user = user
end
end
end
[変更後]
・authenticated?メソッド
の()内の部分を書き換える。
if user && user.authenticated?(cookies[:remember_token])
↓
if user && user.authenticated?(:remember, cookies[:remember_token])
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
###account__activations_controller.rbにeditアクションを追加する。
続いて、account__activationsコントローラー
にeditアクション
を追加する。
まずアカウント有効化の核となるユーザー認証(先ほど編集したauthenticateメソッド)を行っていく。
また、activated
カラムの論理値がfalse
であることもここで確認する。
@user = User.find_by(email: params[:email])
if @user && !@user.activated? && @user.authenticated?(:activation, params[:id])
確認ができたらactivated
カラムとactivated_at
カラムの情報を更新し、ユーザーにsession
持たしアカウントを有効化させる。
そして、ユーザーの詳細ページにリダイレクトさせる。
class AccountActivationsController < ApplicationController
def edit
@user = User.find_by(email: params[:email])
if @user && !@user.activated? && @user.authenticated?(:activation, params[:id])
@user.update_attribute(:activated, true)
@user.update_attribute(:activated_at, Time.zone.now)
log_in(@user)
redirect_to user_url(@user)
flash[:success] = "アカウントが有効になりました!"
else
flash[:danger] = "無効なリンクです。アカウントが有効化できません。"
redirect_to root_url
end
end
end
###sessions_controllerのcreateアクションを編集する。
アカウントのactivated
カラムの論理値がtrueになっている場合のコードを追加する必要があります。
if @user.activated?
[変更前]
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
if params[:session][:remember_me] == '1'
remember(@user)
else
forget(@user)
end
redirect_to user_url(@user)
else
flash.now[:danger] = 'メールとパスワードの組み合わせが無効です。'
render 'new'
end
end
[変更後]
def create
@user = User.find_by(email: params[:session][:email].downcase)
if @user && @user.authenticate(params[:session][:password])
if @user.activated?
log_in(@user)
if params[:session][:remember_me] == '1'
remember(@user)
else
forget(@user)
end
redirect_to user_url(@user)
else
flash[:warning] = "アカウントが有効化されていません。アカウント有効化についてはメールをご確認ください。"
redirect_to root_url
end
else
flash.now[:danger] = "メールとパスワードの組み合わせが無効です。"
render "new"
end
end
#最後に
作成したコードをいくつかリファクタリングして終わり。
(リファクタリングのコードの内容は省略する。)
#参考文献
Ruby on Rails チュートリアル
第11章 アカウント有効化
https://railstutorial.jp/chapters/account_activation?version=6.0#cha-account_activation