Sending Devise emails with Delayed_job
I use Delayed_job on Ducky Guidance to send our email notifications in the background. I recently switched our authentication gem from authlogic to Devise to take advantage of the baked in password reset functionality (and to allow us to use other built in features down the road as well).
After migrating our users from authlogic to Devise, the first order of business was to set up Delayedjob to send password reset emails in the background. After scouring the web, the most common advice I found was to have Devise utilize delayedjob to do the following:
1 module Devise
2 module Models
3 module Confirmable
4 handle_asynchronously :send_confirmation_instructions
5 end
6
7 module Recoverable
8 handle_asynchronously :send_reset_password_instructions
9 end
10
11 module Lockable
12 handle_asynchronously :send_unlock_instructions
13 end
14 end
15 end
(Forked from robotmay via Stack Overflow)
This code utilizes delayed_job's :handle_asynchronously wrapper, I was
met with mixed results. More often than not, I was met with
NoMethodErrors due to nil objects. The issue was hard to track down,
as I could get it to work sometimes, depending on where the above
snippet was located. I had it working consistently on my laptop by
adding the snippet to the end of my config/initializers/devise.rb
file, but when I tried it on our staging server, the nil objects reared
their heads again.
There is another way to have Devise send its email in the background, and that is by using a custom mailer with Devise. There is a configuration parameter in the Devise initializer that will allow you to set the class you want to use for emails. I went about using the following custom emailer:
1 class PasswordResetMailer < Devise::Mailer
2 def reset_password_instructions(record)
3 Delayed::Job.enqueue PasswordResetJob.new(record.id)
4 end
5 end
The above code inherits from Devise::Mailer and overrides the
reset_password_instructions method (which is the only one I care about
right now). I used Delayed_job to enque a custom job called
PasswordResetJob.
1 class PasswordResetJob < Struct.new(:id)
2 def perform
3 user = User.find(id)
4 Devise::Mailer.reset_password_instructions(user).deliver
5 end
6 end
The job takes the user's id, retrieves that record, and uses the default Devise mailer to reset the password and send the user the reset instructions.
After setting up the custom mailer and job, I just had to configure
devise to use that mailer. I added the following line to my
config/initializers/devise.rb:
1 Devise.setup do |config|
2 #...
3 config.mailer = "PasswordResetMailer"
4 #...
5 end
And was on my way. The mailer works perfectly, sending password resets in the background without error.