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)
$ rails generate controller AccountActivations
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 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
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
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
After that, we will describe the process of sending emails.
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
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
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 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.

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.

Once you've created the template, you can preview it.
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
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
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
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
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
Finish by refactoring some of the code you created. (The contents of the refactoring code are omitted.)
Ruby on Rails tutorial Chapter 11 Account Activation https://railstutorial.jp/chapters/account_activation?version=6.0#cha-account_activation
Recommended Posts