A wonderful little gem that allows you to schedule your tasks in plain old Ruby and updates your crontab automatically on a Capistrano deploy.
By Kathy Heffern (Software Developer, Giant Hat)
This post originally appeared on the Giant Hat blog.
There comes a point in every young Rails developer's career when she has to schedule a task to run periodically in her app. You can do this by hand in the crontab in the server, but the syntax is a little complicated. The Whenever gem is a wonderful gem that allows you to schedule your tasks in plain old Ruby, and updates your crontab automatically on a Capistrano deploy. I love this gem because I don't have to worry about remembering to update my crontab by hand, and I like that it documents my scheduled tasks right in my app, so other developers coming on board will quickly be able to see that there are tasks scheduled (whereas it's not as obvious if those tasks are only documented in the crontab). Ok, let's learn how to use Whenever!
Get the Whenever Gem
Add it to your gemfile, along with :require => false.
Including :require => false means that the gem will be installed, but won't be loaded into a process unless you explicitly call require "whenever". We're going to do that later when we talk about deploying. This is done for performance reasons, when you're using a gem like Whenever, that you need to run from the command line but don't otherwise use inside your Rails app.
Install the gem with bundler.
Schedule Your Tasks
To integrate Whenever into your project, cd into your main app directory and run thewheneverize command. This will create a schedule.rb file in the config directory of your Rails application. The schedule.rb file is where we'll write our tasks.
In schedule.rb, create your scheduled tasks. The syntax here is really basic - check out these examples:
- every 20.minutes do
- every :hour do
- every :friday, :at => '11pm' do
- every :weekday do
This is one of the tasks I added to my schedule.rb file.
When I first fill out schedule.rb, I usually run my tasks every 5 minutes, no matter how often I really want them run in production, just for debugging purposes. Once I can see that they are being run, I update schedule.rb with the correct interval.
I also like to put a logger in the method that I'm calling in the task, so I’ll know when it’s been run. This especially helps when I am debugging something.
What if I Only Want to Run Jobs in a Certain Environment?
If you want to limit which environments to run a task in, use the @environment variable. In my app, I wanted the daily report email to only send in production, and not staging.
Write your Tasks to the Crontab
After I code my tasks into my schedule file, I like to preview how Whenever will write it onto my crontab. Running the whenever command will show you a preview of the scheduled tasks, in cron format. It will NOT update your crontab, guys, FYI. This is what it looks like when you run whenever.
When you're looking at the task, converted into cron format, it's helpful to understand a little bit about cron syntax so you know what the asterisks and numbers represent.
When you're ready to update your crontab, there are two ways you can do it: by hand, or automatically, during a Capistano deploy. If you want to do it by hand, this is the command.
After you run that, you should see:
If you want to check that Whenever wrote your tasks correctly on the crontab, you can run the crontab -l command.
The result will look something like this:
Integrate with Capistrano 3
We use Capistrano 3 for deployment, so the following instructions are for Capistrano 3. If you're using an earlier version of Capistrano, the workflow is a little different. Refer to theWhenever readme for instructions.
In your Capfile, add this line:
Make sure that you require whenever after bundler in your Capfile. I originally hadrequire “whenever/capistrano” in a line before require “whenever/bundler” and I got deployment errors.
In deploy.rb, set whenever_roles.
If you don't set them, whenever_roles will default to :db. Make sure you add the roles of the servers you want to have your tasks on to the list. Our servers' roles are set to :web and :app so I had to add both of those to my whenever_roles. We don't have any servers with a :db role in this project, so I didn't need :db on my whenever_roles. Now by default, your tasks will be deployed to any servers with the roles you've listed in whenever_roles. If you only need certain tasks on certain servers, you can specify them like this:
Now Whenever will update your crontab with the tasks you listed in schedule.rb every time you deploy.
After I deploy, I like to make sure the crontab was written correctly. You can do this by going onto the server and running the crontab -l command.
There you go! Let's get out there and schedule some tasks!
About the guest blogger: Kathy Heffern is a Ruby on Rails developer at Giant Hat, a software development shop based out of St. Louis. She loves feminism, Lisa Frank and delighting clients with software.