Dead simple task scheduling in Rails
Posted on 02/13/2009
So I was working on an Intridea project where I needed to send out a daily digest email. The emails needed to be sent out on a scheduled interval, not as the result of some user action. This meant that it would need to be handled outside of the normal request/response cycle.
Typically in the Rails world, when someone mentions long-running, or background tasks, you think either BackgrounDrb or Starling/Workling. Those tools are fine and all -- actually, don't get me started on BackgrounDrb -- but I wanted something a bit more simple.
I didn't need queueing, or jobs, or anything like that. I had only one task to manage. I just want something that will say, "Once a day, do [task]", and that's all.
Also, I really didn't want to have to monkey with deployment tasks, be it with Vlad or Capistrano or whatever. I wanted to just deploy as usual, and have it all just work, no extra processes to start or tasks to run.
This seemed like a tall order, but I began my search anyway. Google led me to a number of pages about a number of tools, all of which included one or more of the things I was trying to avoid. Of all places, I finally ended up on this page from the Ruby On Rails wiki.
Despite having only a one line mention near the bottom of the page, I decided to check out rufus-scheduler, and it turned out to be exactly what I was looking for. There was no database table, queueing mechanism, or separate process to manage. Just a simple scheduler to call out to your existing ruby code.
It couldn't get any simpler. Here's how I set it up...
First, install the gem.
sudo gem install rufus-scheduler
I also froze it into vendor/gems by declaring the gem in the initializer block of environment.rb and then running rake gems:unpack.
Then I created a file called task_scheduler.rb in my config/initializers directory. Inside this file is where the magic happens. This is where it all goes down. Are you ready? Here it is...
scheduler = Rufus::Scheduler.start_new scheduler.every("1m") do DailyDigest.send_digest! end
Yeah, that's it. Seriously.
I was in disbelief myself until I started up the server and watched it send me an email. In it's current state, it would send the digest every minute, which is not what I wanted. Fortunately, the rufus-scheduler provides several ways to schedule your tasks. Once I was ready, I changed it to more of a cron-style scheduler.
scheduler = Rufus::Scheduler.start_new # Send the digest every day at noon scheduler.cron("0 12 * * *") do DailyDigest.new.send_digest! end
You could also use scheduler.in or scheduler.at for one time tasks set to run at some point in the future.
I know a guy who's from "the future", but that's another story.
Anyway, there you have it. Dead simple task scheduling in Rails. It really doesn't get any easier than that.