Problem that the attribute of User model becomes nil in ActionMailer

Execution environment

What I was trying to do

Send an email when creating a new user

app/controllers/users_controller.rb


class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.activation(@user).deliver_later
      redirect_to root_url
    else
      render "new"
    end
  end
end

Pass the user object

app/mailers/user_mailer.rb


class UserMailer < ApplicationMailer
  def activation(user)
    @user = user
    mail(to: @user.email, subject: 'Please activate your account')
  end
end

Send activation token

app/views/user_mailer/activation.html.slim


p= "Hello,#{@user.name}Mr."

Click the p link to activate your account
= link_to "Activate account", edit_account_activation_url(@user.activation_token, email: @user.email)

Activation token is created at user create

app/models/user.rb


class User < ApplicationRecord
  attr_accessor :activation_token
  before_create :create_activation_digest

  private
    def create_activation_digest
      self.activation_token = SecureRandom.urlsafe_base64
      self.activation_digest = BCrypt::Password.create(activation_token)
    end
end

Error that occurred

ActionView::Template::Error (No route matches {:action=>"edit", :controller=>"account_activations", :email=>"address", :id=>nil}, possible unmatched constraints: [:id]):

@ user.activation_token has become nil

Investigate the error

If you look at @ user.object_id and @ user.activation_token

44860
"I2BCoTsomoNhqNF0q_zRKw"
[ActiveJob] -----

  User Load (0.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 70], ["LIMIT", 1]]

[ActiveJob] -----
44980
nil

It's a different object and ʻactivation_token is also nil`!

Cause of error

deliver_later

If you send an email with deliver_later, you will see that it is looking for a user from the database before running ʻUserMailer`.

  User Load (0.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 70], ["LIMIT", 1]]

The value of the ʻactivation_token attribute cannot be referenced in the ʻuser object obtained from the database after saving because it only has the @ user when @ user.save is executed.

Solution

Use deliver_now

app/controllers/users_controller.rb


class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.activation(@user).deliver_now
      redirect_to root_url
    else
      render "new"
    end
  end
end

If you send with deliver_now, the argument will be passed as it is, so it succeeds!

37900
"2kQLrYZjinTiJFKE3jdPqQ"
37900
"2kQLrYZjinTiJFKE3jdPqQ"

Summary of deliver_now and deliver_later

deliver_now Send an email immediately

deliver_late ʻSend an email later using Active Job`

https://railsguides.jp/active_job_basics.html#action-mailer

https://api.rubyonrails.org/classes/ActionMailer/MessageDelivery.html#method-i-deliver_later

Recommended Posts

Problem that the attribute of User model becomes nil in ActionMailer
The problem that the contents of params are completely displayed in the [Rails] view
The problem that the mount directory of Docker Desktop with WSL2 as the back end becomes strange in Windows Terminal (unsolved as of 2020/08/29?)
About the problem of deadlock in parallel processing in gem'sprockets' 4.0
[Rails] Register by attribute of the same model using Devise
Determine that the value is a multiple of 〇 in Ruby
What is the representation of domain knowledge in the [DDD] model?
Solved the problem that all the data in the table was displayed
Follow function association memorandum (understand the description of the User model)
A program that counts the number of words in a List
Explanation of the FizzBuzz problem
Fixed the problem that the connection factory name of TcpConnectionOpenEvent issued by TcpConnection that set the interceptor becomes unknown.
The problem that XML attributes are sorted arbitrarily in Android Studio
Form that receives the value of the repeating item in Spring MVC
I want to change the value of Attribute in Selenium of Ruby
This and that of the JDK
A memorandum of the FizzBuzz problem
Expression used in the fizz_buzz problem
Order of processing in the program
Incident (rails) that is logged out when user editing of the application
This and that of the implementation of date judgment within the period in Java
Sample program that returns the hash value of a file in Java
Ignore parameters that do not exist in the model with ObjectMapper # readValue
[Java] java.lang.NoClassDefFoundError: Addressing the problem of falling in org / jsoup / safety / Whitelist
I get got errors: User must exist in the unit model test