Reminder emails from Rails with SideKiq
Pre-text
Imagine you have an Rails app, that already uses SideKiq to queue the sending of emails. You need to add to this app and create a Rails model to represent a room booking.
At different stages of the booking you should do different things.
-
When a booking is in ‘provisioned’, send a email to warning the room owner
-
When a booking is ‘confirmed’, informed the booker their booking is confirmed.
-
A booking can be altered before it becomes ‘live’
-
2 hours before the start time of the booking send the booker a reminder email.
Ruby
To help with th different states of a RoomBooking I have implemented the workflow gem, so workflow for RoomBooking, might look like:
workflow do
state :draft do
event :provision, :transitions_to => :provisional
end
state :provisional do
event :confirm, :transitions_to => :confirmed
on_entry do
RoomBookingMailer.delay.inform_owner_of_booking(self.id)
end
end
state :confirmed do
event :activate, :transitions_to => :active
on_entry do
RoomBookingMailer.delay.inform_booker_of_booking_confirmation(self.id)
end
end
state :active do
event :complete, :transitions_to => :complete
end
state :complete do
end
end
My Problem
What about the reminder??
I could schedule a task that checks upcoming bookings and sends out reminders when required. The thing I do not like about this is:
-
I need to record I have sent out the reminder
-
Process is defined outside of the RoomBooking
I could do:
RoomBookingMailer.delay_until( booking.start - 2.hours ).remind_booker_of_booking(self.id)
But what if they change their mind and alter the booking start time or cancel it, I would have to un-queue the reminder email and possibly queue another (messy!)
So I did this:
RoomBookingReminderWorker.perform_in(2.minutes, self.id, :customer_confirmed)
Then in that worker will (in 2 minutes) do the following:
-
Checks to see if the booking is still valid
-
Checks to see if it is time to send the reminder
If the booking it still valid and it is NOT time to send the reminder, it will simply schedule itself to run again in X amount of time.