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.


Tags: , , , , ,


About Stefano

Stefano Mancini is a co-founder of DevInterface.

After graduating in Computer Science, he first specialized in Java/J2EE development by participating in several international projects in the pharmaceutical and banking ambits.

Enthusiast of agile development, like SCRUM for project management and eXtreme Programming for code writing, he then moved to dynamic languages like Ruby and Python.

About DevInterface

We are an information and communication technology agency. Our mission is to provide web application development, design services and communication strategies. We specialize in building web applications with modern and efficient frameworks.

Related Post

4 Responses to “How to send email asynchronously using Devise and Rails3”

  1. dB. says:

    Thanks, this was helpful. For Devise::Invitable:

    module Devise
    module Models
    module Invitable
    alias_method :deliver_invitation_without_delay, :deliver_invitation
    handle_asynchronously :deliver_invitation
    end
    end
    end

  2. Anurag says:

    Awesome, worked like a charm! Thank you so much.

  3. Piotrek says:

    Thank you! Worked fantastic, practically out-of-the-box solution!

Leave a Reply

Insert code beetween <code lang="ruby"> and </code>

Copyright 2012 DevInterface s.n.c.

DevInterface Blog is proudly powered by WordPress