LoginSignup
0
0

More than 3 years have passed since last update.

【Rails tutorial】「第11章 アカウントの有効化」の備忘録

Last updated at Posted at 2020-12-30

はじめに

Rails tutorialの第11章 アカウントの有効化ついて備忘録として、自分なりに整理しておく。
(※テストに関する記載はなし)

準備

アカウント有効化用のコントローラを作成する。

$ rails generate controller AccountActivations

routeを編集する。

後々、editの名前付きルートが必要になるので名前付きルートを使用できるようにする為に、ルートファイルにresourcesを追加する。

routes.rb
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

add_activation_to_users.rb
  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を使用して仮想カラムを追加する。

user.rb
attr_accessor :remember_token, :activation_token

コールバックを追加する。

before_createを使用して、ユーザーオブジェクトを作成(create)する前にデータベースにactivation_tokenactivation_digestを作成するメソッドを追加しておく。

user.rb
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を開く。
メールの送信元アドレスを設定する。

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

app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
  def account_activation(user)
    @user = user   #インスタンス変数を作成
    mail(
      to: user.email,
      subject: "Account Activation"
    ) 
  end
end

ビューのテンプレートを編集する。

送信されるメールのビューを編集していく。(textファイルとHTMLファイルの両方)
ビューには、アカウントを有効化する為に必要なURLを書き込む。

account_activation.text.erb
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) %>

プレビューで確認すると以下のような感じ。
image.png

account_activation.html.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(@user.activation_token, email: @user.email) %>

プレビューで確認すると以下のような感じ。
image.png

テンプレートを作成したら、プレビューで確認することができる。

Createアクションを編集する

次はusers_controller.rbファイルの Createアクションを書き換えていく。

[変更前]

users_controller.rb
  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
UserMaileraccount_activationメソッドを介して、ユーザーにメールを送信させる (deliver_nowは今すぐメールを送信したい場合に使用する)

・この時点でログインは完了していないので、セッションを持たせてはいけない為log_in @userは削除し、リダイレクト先もユーザーの詳細ページではなくrootページに変更しておく。

users_controller.rb
  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_tokenremember_digestを比較するメソッド)。
しかし、9章の時に書いたコードでは、remember_tokenremember_digestしか比較をしないので、activation_tokenactivation_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メソッドを書き換えていく。

[変更前]

user.rb
def authenticated?(remember_token)
  if remember_digest.nil?
   false
  else
   BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

[変更後]
sendメソッドを使用して、rememberactivationの両方を入れられるようなコードにする。

user.rb
  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?メソッドを使用していたコードを書き換える必要がある。
[変更前]

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])
      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])

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])
      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持たしアカウントを有効化させる。
そして、ユーザーの詳細ページにリダイレクトさせる。

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])
      @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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0