Sending emails in the background with cron and rake

Thu, 16 Apr 2009 | Comments

Many web applications have to run periodic tasks and it’s really important to decouple these tasks from the clients request thread. One well-proven way to manage these is to use cron to execute the tasks. I had to do this recently to send reminder emails to authors in StatusHub and thought I’d share how easy it was.

First, create a rake task to provide an easy interface to the tasks. This makes it easy to test them out or manually run them. I created a file in lib/tasks called cron.rake:

namespace :cron do
  desc "Send email reminders to all authors on upcoming and overdue reports"
  task :send_reminders => :environment do
    Report.send_reminders
  end
end

Here, Report is one of my models and send_reminders is a class method on that:

def self.send_reminders
  Report.due_drafts.find_each do |report|
    Mailer.deliver_report_due report
  end
end

The Mailer model is a subclass of ActionMailer::Base and has methods for each type of message you want to send. This one looks like:

def report_due(report)
  recipients report.author.email
  from       "StatusHub<support@statushub.com>"
  subject    "Your report entitled '#{report.title}' is #{report.overdue? ? 'overdue' : 'due'}"
  sent_on    Time.now
  content_type "text/plain"
  body       :report => report, :url => design_report_url(report, :host => report.author.account.host)
end

Calling Mailer.deliver_report_due invokes the report_due method and then renders report_due.html.erb into the body of the email. You can now test the task with:

$ rake cron:send_reminders

On the production server, the job can now be automated by adding the following like to your crontab (edit with crontab -e):

00 04 * * * cd /app/statushub/current && /usr/bin/rake RAILS_ENV=production cron:send_reminders