In a previous series on using workling and starling for asychronous processing, I described how to setup background tasks. Here is a quick way to use this for emails without a lot of changes to your application.
First, create lib/asynch_mail.rb:
# Makes an actionmailer class queue its emails into a workling queue
# instead of sending them sycnhronously
#
# From now on all MyMailer.deliver_whatever_email calls create an entry in
# the MailerWorker.deliver_mail corresponding queue and should be processed
# by a worker. If you still want to deliver mail sycnhronously add a bang to the method call:
# MyMailer.deliver_whatever_email!
#
module AsynchMail
def self.included(base)
base.class_eval do
class < < self
alias_method :orig_method_missing, :method_missing
# Catch deliver method calls to turn them into asynch calls
def method_missing(method_symbol, *parameters)
case method_symbol.id2name
when /^deliver_([_a-z]\w*)\!/
orig_method_missing(method_symbol, *parameters)
when /^deliver_([_a-z]\w*)/
mail = self.send("create_#{$1}", *parameters)
MailerWorker.asynch_deliver_mail(:class => self.name, :mail => mail)
else
orig_method_missing(method_symbol, *parameters)
end
end
end
end
end
end
Then, create app/workers/mailer_worker.rb:
class MailerWorker < Workling::Base
def deliver_mail(options)
Object.const_get(options[:class]).deliver(options[:mail])
end
end
Now, all you need to do is include AsynchMail in your mailer class.
class MyMailer < ActionMailer::Base
include AsynchMail
end
That’s it! No other changes to all your mail calls are required. All will automatically become asynchronous. If your application sends a lot of emails, I recommend you do this. There is no reason to make your users wait for an email to be sent.
The only downside with this is that now emailing happens outside of Mongrel. This means that link_to does not have access to the HOST. As a result, you can can’t easily use the route helpers to generate urls. You will need to build your urls manually. At one point, I had some code that got around this, but it stopped working when I upgraded to Rails 2.x. In my app, it was easier to fix the urls than make the other work. If someone can provide this, it would great.
Leave a Reply