Sending emails based on intervals using Ruby on Rails

I would use DelayedJob for this ( assuming you are not sending large number of emails emails a day, i.e. 100's of thousands per day etc.)

class Email < ActiveRecord::Base
  belongs_to :campaign
  after_create :schedule_email_dispatch

  def schedule_email_dispatch
    send_at(campaign.created_at + self.days.days, :send_email)
  end

  def send_email
  end
end

Run the workers using the rake task:

rake jobs:work

Every time a new Email object is created a delayed job item is added to the queue. At the correct interval the email will be sent by the worker.

@campaign = Compaign.new(...)
@campaign.emails.build(:days => 1)
@campaign.emails.build(:days => 2)
@campaign.save # now the delay

In the example above, two delayed job entries will be created after saving the campaign. They are executed 1 and 2 days after the creation date of the campaign.

This solution ensures emails are sent approximately around the expected schedule times. In a cron job based solution, disptaching happens at the cron intervals. There can be several hours delay between the intended dispatch time and the actual dispatch time.

If you want to use the cron approach do the following:

class Email < ActiveRecord::Base
  def self.dispatch_emails
    # find the emails due for dispatch
    Email.all(:conditions => ["created_at <= DATE_SUB(?, INTERVAL days DAY)", 
             Time.now]).each do |email|
      email.send_email
    end
  end
end

In this solution, most of the processing is done by the DB.

Add email.rake file in lib/tasks directory:

task :dispatch_emails => :environment do
  Email.dispatch_emails
end

Configure the cron to execute rake dispatch_emails at regular intervals( in your case < 24 hours)


I would create a rake task in RAILS_ROOT/lib/tasks/email.rake

namespace :email do
  desc "send emails to contacts"
  task :send do
    Email.all.each do |email|
      # if start_date is a datetime or timestamp column
      contacts = Contact.all(:conditions => ["DATE(start_date) = ?", email.days.days.ago.to_date])
      # if start_date is a date column
      contacts = Contact.all(:conditions => { :start_date => email.days.days.ago.to_date })
      contacts.each do |contact|
        #code to send the email
      end
    end
  end
end

Then I would use a cronjob to call this rake task every day at 3 a.m.:

0 3 * * * app_user cd RAILS_APP_FOLDER && RAILS_ENV=production rake email:send