Baru2 ini di project yg sedang saia kerjakan, saia diminta untuk mengimplementasikan automated email. Intinya mengirim email secara otomatis pada saat user melakukan sesuatu. Sekilas biasa saja, hanya menggunakan ActionMailer untuk mengirim email pada event tertentu, namun masalahnya ada beberapa email yang dikirim bukan pada saat event dijalankan, ada delay waktu bbrp hari sebelum email dikirim.
Untuk itu saia memutuskan untuk membuat mailer yang dapat dijadwalkan, selain dapat mengirimkan email pada tanggal dan waktu tertentu, email jg dikirimkan secara berkala pada jangka waktu yg ditentukan (asumsi saia akan dibutuhkan nantinya untuk email laporan berkala.)
Buat SchedulerPertama, install rufus-scheduler
gem install rufus-scheduler
lalu dalam folder config/initializers buat file ruby baru, misal "task_scheduler.rb"
scheduler = Rufus::Scheduler.start_new
scheduler.every("10m") do
QueuedEmail.deliver_all
end
yang akan menjalankan deliver_all pada model QueuedEmail setiap 10 menit.
Buat Model utk menyimpan jadwalgenerate model baru, misal queued_email
migration:
class CreateQueuedEmails < ActiveRecord::Migration
def self.up
create_table :queued_emails do |t|
t.string :from, :limit => 100
t.string :recipients, :limit => 100
t.string :subject, :limit => 100
t.text :content
t.integer :duration
t.datetime :send_at
t.datetime :last_sent_at
t.timestamps
end
end
def self.down
drop_table :queued_emails
end
end
model:
class QueuedEmail < ActiveRecord::Base
validates_presence_of :from
validates_length_of :from, :within => 6..100
validates_presence_of :recipients
validates_length_of :recipients, :within => 6..100
def recurring?
!self.duration.blank?
end
def deliver!
puts "Sending email: #{self.inspect}"
UserMailer.deliver_custom_mail self.attributes
if recurring?
puts "set sent at: #{Time.now.utc}"
self.update_attribute :last_sent_at, Time.now.utc
else
puts "destroy"
self.destroy
end
end
def self.deliver_all
mails = self.all :conditions => ["(send_at <= :current_time) AND (duration IS NULL OR duration = \"\" OR last_sent_at IS NULL OR TIME_TO_SEC(TIMEDIFF(:current_time, last_sent_at)) >= duration)", {:current_time => Time.now.utc}]
mails.each do |mail|
mail.deliver!
end
end
end
cara kerja:
- duration: bila diisi, setelah email dikirim, email tidak akan langsung dihapus, tetapi akan dikirimkan kembali setiap
duration detik
- send_at: menentukan tanggal dan waktu email akan dikirim atau dikirim pertama kalinya bila duration ditentukan.
- last_sent_at: tidak perlu diisi, digunakan secara internal untuk email berkala
- lainnya: rasanya sudah cukup jelas, sama seperti actionmailer
Tambahkan custom_mail di mailer Saia menggunakan UserMailer < ActionMailer::Base untuk mengirimkan email, tambahkan kode berikut di dalamnya:
def custom_mail(options)
@recipients = options["recipients"]
@from = options["from"]
@subject = options["subject"]
@sent_on = Time.now
@body[:content] = options["content"]
end
dan buat custom_mail.erb di viewnya:
<%= @content %>
Contoh penggunaanMisalkan untuk mengirimkan survey email 2 hari setelah user register. Tambahkan kode berikut di users_controller create:
# ....
# bila registrasi sukses daftarkan email untuk dikirim setelah 2 hari
QueuedEmail.create(
:send_at => 2.days.since(Time.now.utc),
:from => "\"Site Keren\" ",
:recipients => "#{@user.email}",
:subject => "Survey supaya keren" ,
:content => "Halo #{@user.name}, isi survey berikut supaya kamu makin keren gitu loh ....."
)
# ....
untuk email berkala, tentukan duration dalam detik, misal email yg dikirim tiap hari
:duration => 24.hours
Dan segitu saja, mungkin bukan implementasi yg terbaik, tp paling tidak bisa digunakan hehe..