How to send email asynchronously using Devise and Rails3


Hello everyone.

I’d show a workaround to send email asynchronously using Devise and Rails3.

Suppose we have already up and running our application with Devise and delayed_job correctly installed.

A first attempt was to add in config/initializers the following file (devise_async.rb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#devise_async.rb
module Devise
module Models
module Confirmable
handle_asynchronously :send_confirmation_instructions
end

module Recoverable
handle_asynchronously :send_reset_password_instructions
end

module Lockable
handle_asynchronously :send_unlock_instructions
end
end
end

This workaround has worked in part: the send method has been properly enqueued in the database, but when delayed_job tries to fire the job, the following error is raised:

1
2

User#send_confirmation_instructions_without_delay failed with NoMethodError: undefined method 'send_confirmation_instructions_without_delay' for #<User:0x000000032f87c8> - 1 failed attempts
[Worker(host:stefano-desktop pid:13153)]

As you can see, the job is trying to call the wrong send method: send_confirmation_instructions_without_delay.

At this point, I’ve implemented an even more dirty hack, overriding Devise’s methods using the syntax specified by intridea to send emails in the background:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

#devise_async.rb
module Devise
module Models
module Confirmable
# Send confirmation instructions by email
def send_confirmation_instructions
generate_confirmation_token! if self.confirmation_token.nil?
::Devise.mailer.delay.confirmation_instructions(self)
end
end

module Recoverable
# Resets reset password token and send reset password instructions by email
def send_reset_password_instructions
generate_reset_password_token!
::Devise.mailer.delay.reset_password_instructions(self)
end
end

module Lockable
# Send unlock instructions by email
def send_unlock_instructions
::Devise.mailer.delay.unlock_instructions(self)
end
end
end
end

This solution, however, is too tied to the implementation of Devise and is therefore not a good one (besides being really really dirty).

The latest idea, which represents the solution I’ve used is implemented as follows: use the alias_method in this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#devise_async.rb
module Devise
module Models
module Confirmable
alias_method :send_confirmation_instructions_without_delay, :send_confirmation_instructions
handle_asynchronously :send_confirmation_instructions
end

module Recoverable
alias_method :send_reset_password_instructions_without_delay, :send_reset_password_instructions
handle_asynchronously :send_reset_password_instructions
end

module Lockable
alias_method :send_unlock_instructions_without_delay, :send_unlock_instructions
handle_asynchronously :send_unlock_instructions
end
end
end

This latest hack works a treat; is not the best but let you send mail with Devise asynchronously.

If you have any better solutions, do not hesitate to share.