Sending automatic mails in a Rails app - ruby-on-rails

In my rails application, users can subscribe to some events. I'd like to send a mail to the user 24 hours before the begining of the the event.
I'm not sure how to implement that. A rake task ? Is there some gems or some best practices to follow ?
Thanks!
EDIT:
I guess that my cron must be run every minute to get each event that starts in 24 hours. But if the cron stops, or it takes more than a minute, I can't skip some emails. In the other way, how can I be sure I'm not sending the same email twice ?

Check out:
http://railscasts.com/episodes/206-action-mailer-in-rails-3
for sending mail and
http://railscasts.com/episodes/164-cron-in-ruby-revised
for using whenever to set up a cron to call your mailer
EDIT: Just saw your comment. In that case I would look at using something like resque-scheduler or the equivalent with sidekiq:
https://github.com/bvandenbos/resque-scheduler
then you can do things like this:
Resque.enqueue_at(24.hours.from_now, SendFollowUpEmail, :user_id => current_user.id)
sidekiq example:
FollowUpEmailer.perform_in(24.hours.from_now, current_user.id)

https://github.com/mperham/sidekiq/pull/257
This will help you to solve automatic mailing system in scheduled time!

Related

How do I schedule rails mailers to send on specific days (heroku)?

So I'm just needing a bit of advice here.
I have an app that I deployed to heroku, and I have mailers set up so when things like 'record is added' mailers run and send email to users.
One type of mailer I'd like to create is one that goes out on a specific day.
For example, I might say, send mailer on November 3rd at 6:00 am.
How would I go about scheduling that? I mean I can't really use .deliver_later because I realize when the dyno goes to sleep the job will no longer be queued (at least that's how I understand it)
SO I supposed I need to use something like Redis, or Sidekiq, or perhaps Heroku Scheduler? Any advice on this subject would be extremely helpful. I've read a few SO posts about this, but I'm still lost.
Thank you in advanced.
You can achieve it using Sidekiq.
Sidekiq allows you to schedule the time when a job will be executed. You use perform_in(interval_in_seconds, *args) or perform_at(timestamp, *args) rather than the standard perform_async(*args):
MyWorker.perform_in(3.hours, 'mike', 1)
MyWorker.perform_at(3.hours.from_now, 'mike', 1)
This is useful for example if you want to send the user an email 3 hours after they sign up. Jobs which are scheduled in the past are enqueued for immediate execution.
In your case, you can achieve it using perform_at(timestamp, *args) have a look at code -
time_stemp = DateTime.parse("09/11/2020 06:00 AM")
SendMailToUserWorker.perform_at(time_stemp, args)
More information here -
https://github.com/mperham/sidekiq/wiki/Scheduled-Jobs
I haven't used Heroku. In my case, I use sidekiq and sidekiq-cron to do some cron job

Best way to manage gem whenever for sending emails

In my application there are posts which are going to expire in certain time of a day.
After the expiration the customer needs to notify through emails.
I have used Rails cron whenever for in each 1 minute and it makes expire the post at the specified time and sends the email to customers.
Schedule.rb
every 1.minutes do
rake "ad_has_expired_task"
end
Will be this a better way?
Any help is appreciated.
Thanks
For small app, this way is good, but for a bigger app, that may keep cpu busy.
Here is a solution from my site.
gem sidekiq
gem whenever
in scheduler.rb
every 1.day do
rake "scan_expired_task"
end
in rake taks scan_expired_task.rake
if post.will_expire_today
MyMailerWoker.perform_async(related_user_ids, post.expired_at-Time.now)
end
Then you write your mailer program in your MyMailerWoker
The codes above can't be executed directly, you need to customize it by your business.
Hope it helps.
reference:
https://github.com/mperham/sidekiq/wiki/Getting-Started

Rails Runner or Rake task to send one-off email to registered users

I'd like to send a one off email to users and, being new-ish to ActionMailer, not sure how to approach a one-off like this? Rails Runner, a Rake task or maybe even the rails console on the production machine?!?
Figure I'd add an action for it to user_mailer.rb. And create an accompanying view in app/views/user_mailer/. Just not sure how to trigger it. I'd like to grab a few users and send to them in batches. Happy to do this manually...as we stagger sending the messages over the next few days/weeks.
Appreciate any suggestions or advice.
If you want to do this as automatedly as possible, I'd take a look at the whenever gem. It lets you automate rake (or runner) tasks as cron jobs. You can add a script somewhere in lib that uses ActionMailer to generate the emails, and schedule when they're sent with whenever/cron.
As far as sending in batches,
I would do something like this:
Users.find_each(:batch_size => n) do |m|
mail = UserMailer.new(m.email_address, message)
mail.deliver
end
Obviously, that's near-pseudocode, but hopefully it puts you on the right track?

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