[Rails tutorial] A memorandum of "Chapter 11 Account Activation"

Introduction

As a memorandum about Chapter 11 Account Activation of the Rails tutorial, organize it in your own way. (* There is no description about the test)

Preparation

Create a controller for account activation.

$ rails generate controller AccountActivations

Edit the route.

Later, we will need the named root of edit, so we will add resources to the root file so that we can use the named root.

routes.rb


Rails.application.routes.draw do
  -
  -
  resources :account_activations, only:[:edit]
end

Add 3 columns.

Add the three columns needed for the account activation process to the table. First, create a migration file.

$ rails g migration add_activation_to_users 

Edit the migration file, add the following 3 columns, and execute rails db: migrate. ・ Activation_digest ・ Activated ・ Activate_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

Add a virtual column.

Use attr_accessor to add a virtual column, as you did in Chapter 9 when you added a remember_token column.

user.rb


attr_accessor :remember_token, :activation_token

Add a callback.

Use before_create to add a method to create activation_token and activation_digest in the database before creating a user object.

user.rb


before_create :create_activation_digest

  #Create an activation token and digest in the database.
  def create_activation_digest
    self.activation_token = User.new_token
    self.activation_digest = User.digest(activation_token)
  end

Email sending process

After that, we will describe the process of sending emails.

Create a mailer

Can be created with the rails generate command. rails g mailer [mailer name] [mailer method name (action name)] ..

The created file is as follows. ・ Mailer file -Two view templates (text format and HTML format) ・ Test file ・ Preview file

$ 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

Edit application-wide settings.

Open app/mailers/application_mailer.rb. Set the sender address of the email.

application_mailer.rb


aclass ApplicationMailer < ActionMailer::Base
  default from: '[email protected]' #Specify the sender address of the email.
  layout 'mailer'
end

Write the mail sending process

Open the mailer file with app/mailers/[mailer name] .rb. In the mailer file, the file that creates the sending process using the mail method. Write the mail method for each method and add the sending process.

The main options available in the mail method are: from: Sender email address subject: Email subject to: E-mail destination address etc...

app/mailers/user_mailer.rb


class UserMailer < ApplicationMailer
  def account_activation(user)
    @user = user   #Create an instance variable
    mail(
      to: user.email,
      subject: "Account Activation"
    ) 
  end
end

Edit the view template.

Edit the view of the emails sent. (Both text and HTML files) In the view, write the URL required to activate the account.

ruby: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) %>

Looking at the preview, it looks like the following. image.png

ruby: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) %>

Looking at the preview, it looks like the following. image.png

Once you've created the template, you can preview it.

Edit the Create action

Next, rewrite the Create action in the users_controller.rb file.

[Change before]

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

[After change] -UserMailer.account_activation (@user) .deliver_now #deliver_now ↑ Let the user send an email via the account_activation method of UserMailer (deliver_now is used when you want to send an email now)

-Since login is not completed at this point, you should not have a session, so delete log_in @user and change the redirect destination to the root page instead of the user's detail page.

users_controller.rb


  def create
    @user = User.new(user_params)
    if @user.save
      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

Edit the autheticate method.

Next, rewrite the authenticated method described in user.rb. In the process of account activation, the process of comparing tokens and digests occurs. This process has already been done in "Chapter 9 Advanced Login" (method to compare remember_token and remember_digest). However, in the code written in Chapter 9, only remember_token and remember_digest are compared, so it is necessary to rewrite the code so that activation_token and activation_digest can also be compared.

Use the send method for that.

#behavior of send method
>> 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"

Use this to rewrite the authenticated method.

[Change before]

user.rb


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

[After change] -Use the send method to make the code so that both remember and activation can be inserted.

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

Rewrite the current_user method.

By rewriting the authenticated? Method, you need to rewrite the code that was using the authenticated? Method. [Change before]

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

[After change] -Rewrite the part inside () of authenticated? Method. 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

Add edit action to account__activations_controller.rb.

Then add the edit action to the account__activations controller. First, perform user authentication (authenticate method edited earlier), which is the core of account activation. Also, check here that the logical value of the activated column is false.

    @user = User.find_by(email: params[:email])
    if @user && [email protected]? && @user.authenticated?(:activation, params[:id])

Once confirmed, update the information in the activated and activated_at columns to give the user a session and activate the account. Then redirect to the user's detail page.

account_activations_controller.rb


class AccountActivationsController < ApplicationController
  def edit
    @user = User.find_by(email: params[:email])
    if @user && [email protected]? && @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] = "Your account is activated!"
    else
      flash[:danger] = "Invalid link. The account cannot be activated."
      redirect_to root_url
    end
  end
end

Edit the create action of sessions_controller.

You need to add code if the logical value in the activated column of your account is true.

if @user.activated?

[Change before]

  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] = 'The email and password combination is invalid.'
      render 'new'
    end
  end

[After change]

  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] = "Your account has not been activated. Please check your email for account activation."
        redirect_to root_url
      end
    else
      flash.now[:danger] = "The email and password combination is invalid."
      render "new"
    end
  end

Finally

Finish by refactoring some of the code you created. (The contents of the refactoring code are omitted.)

References

Ruby on Rails tutorial Chapter 11 Account Activation https://railstutorial.jp/chapters/account_activation?version=6.0#cha-account_activation

Recommended Posts

[Rails tutorial] A memorandum of "Chapter 11 Account Activation"
Rails Tutorial Memorandum (Chapter 3, 3.1)
Rails Tutorial Memorandum (Chapter 3, 3.3.2)
[Rails Struggle/Rails Tutorial] Summary of Rails Tutorial Chapter 2
[Rails Tutorial Chapter 5] Create a layout
rails tutorial Chapter 6
rails tutorial Chapter 1
Rails tutorial memorandum 1
rails tutorial Chapter 7
rails tutorial Chapter 5
rails tutorial Chapter 10
rails tutorial Chapter 9
rails tutorial Chapter 8
11.2 Send Account Activation Email: Rails Tutorial Notes--Chapter 11
Rails Tutorial Chapter 14 was completed, and it took a total of 155.5 hours.
A summary of only Rails tutorial setup related
Rails Tutorial Chapter 10 Notes
Cloud9 is out of memory: Rails tutorial memorandum
Rails Tutorial Chapter 3 Notes
rails tutorial About account activation in production environment
Rails Tutorial Chapter 3 Learning
[Ruby on Rails] A memorandum of layout templates
Rails Tutorial Chapter 4 Notes
Rails Tutorial Chapter 4 Learning
Rails Tutorial Chapter 1 Learning
Rails Tutorial Chapter 2 Learning
Rails Tutorial Chapter 8 Notes
Rails Tutorial Records and Memorandum # 0
[Rails Tutorial Chapter 4] Rails-flavored Ruby
[Rails] Implementation of tutorial function
rails tutorial chapter 10 summary (for self-learning)
A memorandum of the FizzBuzz problem
Chewing Rails Tutorial [Chapter 2 Toy Application]
A memorandum of personal phpenv install
Rails Tutorial (4th Edition) Memo Chapter 6
[Rails] Get a unique record from the table joined by the join method of Active Record (Rails Tutorial Chapter 14)
Rails Tutorial 6th Edition Learning Summary Chapter 10
Rails Tutorial 6th Edition Learning Summary Chapter 7
Rails Tutorial 6th Edition Learning Summary Chapter 4
rails tutorial
Rails Tutorial 6th Edition Learning Summary Chapter 9
Rails Tutorial 6th Edition Learning Summary Chapter 6
Rails Tutorial 6th Edition Learning Summary Chapter 5
rails tutorial
rails tutorial
rails tutorial
Is the path helper (_path) returning a relative path? Absolute pass? : Rails tutorial memorandum
Rails Tutorial 6th Edition Learning Summary Chapter 2
Rails Tutorial Chapter 0: Preliminary Basic Knowledge Learning 5
rails tutorial
Rails Tutorial 6th Edition Learning Summary Chapter 3
rails tutorial
rails tutorial
Rails Tutorial 6th Edition Learning Summary Chapter 8
(Giri) A local government employee in his twenties works on a Rails tutorial [Chapter 11]
Decompose "$ printf" install: --no-document \ nupdate: --no-document \ n ">> ~ / .gemrc" in Chapter 1 of the Rails Tutorial
(Giri) A local government employee in his twenties works on a Rails tutorial [Chapter 1]
(Giri) A local government employee in his twenties works on a Rails tutorial [Chapter 14]
(Giri) A local government employee in his twenties works on a Rails tutorial [Chapter 12]
(Giri) A local government employee in his twenties works on a Rails tutorial [Chapter 5]
[Ruby on Rails] Rails tutorial Chapter 14 Summary of how to implement the status feed