chickensmitten chickensmitten - 5 months ago 15
Ruby Question

Mailer unable to access reset_token in User model

faced with an issue where @user.reset_token returns nil.

app/views/user_mailer/password_reset.html.erb

<%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %>


Reset_token is declared in User model, whereby this problem happens when I try to use a sidekiq worker. Refer to code below.

app/models/user.rb

class User < ActiveRecord::Base

attr_accessor :reset_token

def User.new_token
SecureRandom.urlsafe_base64
end

def send_password_reset_email
PasswordResetWorker.perform_async(self.id)
end

private

def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end


app/workers/password_reset_worker.rb

class PasswordResetWorker
include Sidekiq::Worker
sidekiq_options retry: false

def perform(user_id)
user = User.find(user_id)
UserMailer.password_reset(user).deliver
end
end


app/mailers/user_mailer.rb

class UserMailer < ActionMailer::Base
default from: "noreply@example.com"

def password_reset(user)
@user = user
mail to: user.email, subject: "Password Reset"
end
end


This problem DOES NOT happen when I do not use workers

app/models/user.rb

def send_password_reset_email
UserMailer.password_reset(self).deliver
end


Would like to know what can I replace "@user.reset_token" with? Let me know if you need more info. Thanks in advance.

Answer

You're not storing the reset_token in the database - you're storing the reset_digest.

When you don't use workers, you're storing the reset_token in the User instance, then passing that same User instance to your mailer - hence the reset_token is still available.

When you use workers, your worker only has the User's ID, so it's reloading the User instance from the database. Because the reset_token isn't being stored in the database, it's coming back nil.

Either you should be saving the reset_token in the database, or your password email should be using reset_digest in the URL.