Reputation: 496
I want to trigger mail to be sent one hour before an appointment comes up. I am using the at field from the @appointment
instance variable.
class AppointmentController < ApplicationController
def create
if DateTime.now + 1.hour > @appointment.at
AppointmentReminder.send_appointment_email(@appointment).deliver_now
end
end
end
This works if the appointment was created within an hour, but if the appointment was created in the future... then our poor customer won't be notified. Is there a mechanism where Rails can automatically deliver the email at the right time? I don't want to use a cronjob or rake task.
Upvotes: 1
Views: 2905
Reputation: 935
I will share how I have done it. It works just fine.
First, install whenever gem.
You should have your mailer. Here is mine:
class WeeklyDigestMailer < ApplicationMailer
default :from => "[email protected]"
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.weekly_digest_mailer.weekly_promos.subject
#
helper ApplicationHelper
def weekly_promos(suscriptor, promos)
@promos = promos
mail(:to => "<#{suscriptor.email}>", :subject => "Mercadillo digital semanal")
end
end
Of course, you need to style your view.
Then, you create a rake task (in lib/tasks). Just like this:
desc 'send digest email'
task send_weekly_email: :environment do
@promociones = Promo.where("validez > ?", Time.zone.now).order("created_at DESC")
if (@promociones.count > 0)
@suscriptors = Suscriptor.where(email_confirmation: true)
@suscriptors.each do |suscriptor|
WeeklyDigestMailer.weekly_promos(suscriptor, @promociones).deliver_now
end
end
end
Finally, you configure your schedule with whenever gem. As I want to send the mails all thrusdays at 9 am, I just put it:
every :thursday, at: '9:00 am' do # Use any day of the week or :weekend, :weekday
rake "send_weekly_email"
end
One important point: since you are using a rake task, use deliver_now instead of deliver_later because if the task finish before all emails have been sent, the rest will be undelivered.
That's all.
Upvotes: 0
Reputation: 8247
You could use whenever to run a block of code on a schedule. Say, ever 5 minutes, looks for appointments that are starting within the next hour and send an email.
To prevent multiple servers from sending an email, you could have a status on the appointment to keep track of if the email has been sent.
Then, using postgres, you can use this SQL to grab records to send and use the database to decide which server is going to send out a specific email:
Email.find_by_sql("WITH updated AS (
UPDATE emails SET status = 'processing' where lower(status) = 'new' RETURNING id
)
SELECT *
FROM emails
WHERE id IN (SELECT id FROM updated)
order by id asc
")
Upvotes: 0
Reputation: 321
As you tagged your question as related to rails 4.2 then Active Job exactly what you need.
Upvotes: 0
Reputation: 19879
I'd recommend looking at background processing systems like Sidekiq or Sucker Punch which can be configured to perform jobs "later".
This way when the appointment is created you can schedule the job to execute at the correct time. You'll need to add checks to make sure when the job finally runs that it's still legitimate, etc.
Upvotes: 1