There may be situtations where
our web applications will need to send hundreds of emails together. Eg:
Importing email addresses from gmail address book and sending
invitations to all emails from imported list.
In such
situtations, if we try to send the mails directly from our rails
application, sending of all these emails will take some time, and the
application waits till all the emails are sent. It would be preferable
if we could queue the emails, and send them at a later time, rather
than making the application wait for all emails to be sent.
Simple solution - queuing up emails in database, and assigning a cron job to send them in batches.
Model-
- require 'invitation'
-
- class QueuedMail < ActiveRecord::Base
- serialize :args
-
- def self.send_email
- find(:all, :order=> "priority desc, id desc", :limit=>50).each do |mail|
- mailer_class = mail.mailer.constantize
- mailer_method = ("deliver_" + mail.mailer_method).to_sym
- mailer_class.send(mailer_method, *mail.args)
- mail.destroy
- end
- true
- end
-
- def self.add(mailer, method, args, priority)
- QueuedMail.create(:mailer=>mailer.to_s, :mailer_method=>method.to_s, :args => args, :priority=> priority)
- end
- end
#need to require all models whose objects will be serialized and saved in QueuedMail.args. This is to enable smooth deserialization
require 'invitation'
class QueuedMail < ActiveRecord::Base
serialize :args
def self.send_email
find(:all, :order=> "priority desc, id desc", :limit=>50).each do |mail|
mailer_class = mail.mailer.constantize
mailer_method = ("deliver_" + mail.mailer_method).to_sym
mailer_class.send(mailer_method, *mail.args)
mail.destroy
end
true
end
def self.add(mailer, method, args, priority)
QueuedMail.create(:mailer=>mailer.to_s, :mailer_method=>method.to_s, :args => args, :priority=> priority)
end
end
Migration- class CreateQueuedEmails < ActiveRecord::Migration
- def self.up
- create_table :queued_mails do |t|
- t.column :mailer , :string
- t.column :mailer_method, :string
- t.column :args, :text
- t.column :priority, :integer, :default=> 0
- end
- end
-
- def self.down
- drop_table "queued_emails"
- end
- end
class CreateQueuedEmails < ActiveRecord::Migration
def self.up
create_table :queued_mails do |t|
t.column :mailer , :string
t.column :mailer_method, :string
t.column :args, :text
t.column :priority, :integer, :default=> 0
end
end
def self.down
drop_table "queued_emails"
end
end
Queueing an email - Controller- Example 1
-
- args = [subscriber.email,"admin@abc.com",newsletter.subject,newsletter.body]
- QueuedMail.add("UserMailer","queued_email", args, 0)
-
- Example 2
-
- invitation = Invitation.create(:from_id => sender.id, :to_email => email)
- QueuedMail.add("UserMailer","invite_new_user",[invitation],1)
Example 1
args = [subscriber.email,"admin@abc.com",newsletter.subject,newsletter.body]
QueuedMail.add("UserMailer","queued_email", args, 0)
Example 2
invitation = Invitation.create(:from_id => sender.id, :to_email => email)
QueuedMail.add("UserMailer","invite_new_user",[invitation],1)
Sending queued emails - Crontab- */5 * * * * cd /home/unni/public_html/abc/current && ruby script/runner "QueuedMail.send_email"
*/5 * * * * cd /home/unni/public_html/abc/current && ruby script/runner "QueuedMail.send_email"
I have taken the idea from http://romaniaonrails.com/queue-emails-ruby-rails-actionmailer-cron-job with slight modifications.