Rails send email on schedule - ruby-on-rails

I have an app that presents a calendar allowing you to create events that display on the calendar. One of the fields you set is the day the event starts. I would like to be able to send email reminders out when the day of the event arrives.
I'm new to rails so I'm not exactly sure what I need or where to start. I read the rails guide for ActionMailer and generated a mailer, I was assuming I would need one of those. I also read the rails guide for observers and thought maybe that might be a route to what I want to accomplish.
Let's say every day, 7 days a week at 9am central, a job (cron or rake task) should run that searches the 'start_at' column of every Event in the database (sqlite locally, Postegre with heroku in production) and if the start_date is equal to 'today?' then send the action mailer template to remind the user.
So what's the best way to (and with what tool) to build a job to examine that database and kick off emails every day? In the future I guess this would grow to reminders ahead of time.
Some pseudo-code:
def send_email
if self.start_at == Date.today #assuming self.start_at.today? works too.
// send email
end
Just not sure how to get that working.

You could have a Controller/Action that does it, then call the url from a cron job on a specified schedule using a command like cURL.

Related

Using sidekiq in rails, how can I send messages to a bunch of users, each of which has a unique time they want to receive it?

Hey I have a rails application and a bunch of users and I want to send them a message once per day at a preferred local time of the user's choice. (A message is a text, email, or chatbot notification.)
I believe I could add something to my User model that would allow for something to be performed every 24 hours at the preferred time but I'm not sure specifically how to implement that AND I also don't know how to remove these jobs from the queue if, for example, the user changes their preferred time or they want to disable messages all together.
Any thoughts on how I could do this?
Where is your application hosted?
You can have a cron task or a scheduler (Heroku) that runs every hour (or every 10 minutes). It would query in a users preferences table searching for users who want to receive the email at this moment (or in a range near this moment, such as the next 10 minutes).
time = Time.now
#users = User.include(:settings).where(“settings.receive_email_at between ? and ?”, time, time + 10.minutes)
You may need some changes to handle timezones and to avoid sending duplicate emails, but that’s just an idea.
It's simple. Build two models - One to store user and its time mapping (UserTimeMapping) and the second one to create a unique entry for each day when a message is sent to user (UserMessage).
Write a cron task which runs every 10 minutes and pulls all the records from UserTimeMapping which are to be executed for next 10 minutes and schedules a worker task for the exact time (say MessageTriggerWorker). The worker has to check in UserMessage table whether a record exists for the given user_id for today and if yes it has to return without performing any task. If that is not the case then it should send the message and create a record in the UserMessage table.

Rails: Sending a reminder email if a condition is true

I am looking to send users of a product an email reminding them to do something if a certain condition is true.
Based on my knowledge so far, the following aspects are going to be involved:
Script: The code that needs to be executed (sending the email), and a condition that checks whether the email should be sent or not.
Scheduling: Execute the script once a day, using the gem whenever for easier creation of cron jobs.
Assuming this is true, I would do this:
Create a script consisting of an email incl. the condition whether the email shall be sent.
Create a cron job using whenever to execute the script.
Would that work? It seems to be that step one does not work, since I doubt you can set up the whole email in the script.
Can someone elaborate on creating a script that sends a message based on a condition?
Bonus question: How can I adapt a cron job to different time zones? Some users are from Europe and some from the States. Ideally, everyone should get the email at the same time in their timezone.
I tried to write this question in an elaborative way so that other beginners on this topic who might run in to the same roadblocks have an easy way to follow.
The most straight forward way I can think of is to have a job run every hour that checks your condition, then sends an email to that user. You can use cron for this, or that gem whenever.
I would make this a rake task in your Rails application. That rake task's psuedo code would look something like this.
def remind_user_to_do_something
users = User.get_users_based_on_condition
// The above could be a scope defined on your User class.
// Basically that scope would check 2 things
// 1. Do I meet the condition you're talking about above.
// 2. Is it time to send an email to them based on their timezone.
for each user in users
MyActionMailerClass.send_email_for_user user
end
end

A way to create an auction-style timed trigger in Ruby on Rails

What gems/technology would be best suited for creating an auction style system where a certain action is run when a given duration of time has expired? (ie. sending an email message to users telling them the auction is over).
If you want real time notification I think resque_sceduler suites you. So you can schedule the task to send the email for the exact time the auction ends.
If there is no problem to wait some minutes or hours you can use cron jobs or something like that. There are many options actually.

How to have users create scheduled tasks in rails app deployed on Heroku

I have a rails app deployed on Heroku. I want to add a feature that enables users of the app to set a reminder. I need some way for the app to schedule sending an email at the time specified by the user.
I have found numerous posts referring to using delayed_job for this, but none of the write-ups / tutorials / etc. that I have found directly address what I am trying to accomplish (the descriptions I have found seem more geared towards managing long-running jobs that are to be run "whenever").
Am I on the right track looking at delayed_job for this? If so, can somebody point me towards a tutorial that might help me?
If delayed_job is not quite right for the job, does anybody have a suggestion for how I might approach this?
The most typical way of handling this is to use a cron job. You schedule a job to run every 15 minutes or so and deliver any reminders that come up in that time. Unfortunately, heroku only allows cron jobs to run every hour, which usually isn't often enough.
In this case, I'd use delayedjob and trick it into setting up a recurring task that delivers the notifications as often as necessary. For example, you could create a function that begins by rescheduling itself to run in 10 minutes and then goes on to send any reminders that popped up in the previous 10 minutes.
To view delayedjobs send_at syntax to schedule future jobs check here: https://github.com/tobi/delayed_job/wiki
ADDED after comments:
To send the reminder, you would need to create a function that searches for pending reminders and sends them. For example, let's say you have a model called Reminder (rails 3 syntax cause I like it better):
def self.find_and_send_reminders
reminders = Reminder.where("send_at < ? AND sent = ?", Time.now, false).all
reminders.each do |r|
#the following delayed_job syntax is apparently new, and I haven't tried it. came from the collective_idea fork of delayed_job on github
Notifier.delay.deliver_reminder_email(r)
#I'm not checking to make sure that anything actually sent successfully here, just assuming they did. may want to address this better in your real app
r.update_attributes!(:sent => true)
end
#again using the new syntax, untested. heroku may require the old "send_at" and "send_later" syntax
Reminder.delay(:run_at => 15.minutes.from_now).find_and_send_reminders
end
This syntax assumes you decided to use the single reminder entry for every occurence method. If you decide to use a single entry for all recurring reminders, you could create a field like "last_sent" instead of a boolean for "sent" and use that. Keep in mind these are all just ideas, I haven't actually taken the time to implement anything like this yet so I probably haven't considered all the options/problems.
Check out the runt gem, may be useful for you: http://runt.rubyforge.org/
You can use delayed_job's run_at to schedule at a specific time instead of whenever.
If your application allows the users to change the time of the reminders you need to remember the delayed_job to be able to update it or delete it when required.
Here is more details.
It's good to avoid polling if you can. The worker thread will poll at the database level, you don't want to add polling on top of polling.

How to go about sending email x hours after a user signs up in Ruby on Rails?

How would I go about sending an email to a user, say, 48 hours after they sign up, in Ruby on Rails? Thanks!
As Joseph Daigle mentioned, you need to obviously record the exact date and time the user registered. After that, you need a cron running every certain number of minutes (every hour, for example) checking to see if there's any new users whose registration time is greater than 48 hours, send a mail to said user and mark that user as already emailed, so you don't email them again.
As per the actual mail sending, check out the following documentation page:
http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer
It has all you need to know to send mails with RoR.
I recommend that you use the latest version of BackgrounDRb to handle this. You can read about BackgrounDRb here: http://backgroundrb.rubyforge.org/
In order to queue a message for later delivery, the BackgrounDRb client code (in your application model's after_create callback, maybe) could look something like this:
MiddleMan(:email_worker).enq_send_email_task(:message => #message,
:job_key => "notify1",
:scheduled_at => Time.now + 48.hours)
You'd have to build a BackgrounDRb worker to handle sending the email:
# RAILS_ROOT/lib/workers/email_worker.rb
class EmailWorker < BackgrounDRb::MetaWorker
set_worker_name :email_worker
def send_email_task(message)
# ... Code to send the email message
end
end
Note that in order to use BackgrounDRb in this way, you have to use persistent job queues, so make sure you run the migration included with BackgrounDRb to set up the persistence table in your application.
BackgrounDRb is started separately from Rails (mongrel, apache, etc) using 'script/backgroundrb start', so make sure that you add the daemon to whatever process monitoring you're using (god, monit, etc) or that you create an /etc/init.d script for it.
First you're going to need a running daemon or background service which can poll your queue (probably from in a database) every few minutes.
The algorithm is pretty simple. Record the time of the user event in the queue. When the daemon checks that item in the queue, and the time difference is greater than 48 hours, prepare the e-mail to send.
You can queue jobs with a delay using async observer. Ideally, anything you have that isn't known to be instant (or very close to it) all the time should pass through something like that.
I wrote a plugin called acts_as_scheduled that may help you out.
acts_as_scheduled allows you to manage
scheduled events for your models.
A good example of this is scheduling
the update of RSS Feeds in a
background process using Cron or
BackgroundRB.
With acts_as_scheduled your schedule
manager can simply call
"Model.find_next_scheduled()" to grab
the next item from the database.
How I would approach this is by creating a scheduling controller, that will query the database for the next_scheduled and then use a mailer to send the message. The you set up a Cron Job to call the controller periodically using WGET or CURL. The advantage of the Cron/Controller approach is that no further infrastructure or configuration is required on the server and you avoid complicated threading code.
I think I'd be inclined to store the need for the email and the earliest time after which it should be sent, somewhere separate, then have my things-to-do task look at that. That way I only have to process as many records as there are emails to be sent, rather than examine every user every time, which would either get tedious or require an otherwise probably unnecessary index. As a bonus, if I had other tasks to be performed on some sort of a diarised basis, the same construct would be useful with little modification.

Resources