XP on Rails Extreme Programming Blog

4Mar/112

How to send email asynchronously using Devise and Rails3

ORIGINAL POST

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):

#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:

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’e methods using the syntax specified by intridea to send emails in the background:

#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:

#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!

About stefano

Independent Information Technology and Services Professional
Comments (2) Trackbacks (0)
  1. Hi Stefano,
    For me your first attempt worked. Do you know why it didn’t work for you? As in why was it looking for send_confirmation_instructions_without_delay instead of send_confirmation_instructions. Which version of devise and delayed_job are you using?
    Thanks

  2. yes first solution also works for me


Leave a comment


No trackbacks yet.