Automate Tasks in Rails Using the Whenever Gem
A walkthrough of cron job implementation using the whenever gem
What is Cron?
Before we can jump in to using the whenever gem, we need to learn about Cron. Cron is a utility that is used to schedule commands at specified intervals, and is most likely already on your machine. It’s ideal for automating recurring tasks, called cron jobs. Cron jobs can be written directly in a config file called a crontab, however the syntax can be a little confusing (it’s a lot of asterisks):
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6)
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <command to execute>
*If you’re deploying your app using Heroku, take a look at Heroku Scheduler instead of the Whenever gem*
Whenever gem
The whenever gem adds a layer of simplicity to cron jobs, and provides a much more readable syntax to writing them. Let’s set it up:
- Install whenever
gem install whenever
or add the following to your Gemfile and then run bundle
gem 'whenever', require: false
2. Create a schedule.rb
file by running the following:
bundle exec wheneverize .
3. Set up your output
The schedule.rb
file has some examples commented out for reference. One thing that needs to be added is where the cron jobs will be logged once they complete. We can put ours in cron.log, but this can be customized:
set :output, "log/cron.log"
This step is really important, because if you try and run a simple cron job, like printing something — it will get printed to wherever you set your output and not your terminal.
4. Setting up your cron job and interval
Whenever has wonderfully simple syntax — you can check the documentation for any specific interval needs. For our example, let’s do some task every minute:
every 1.minute do#task will go hereend
Whenever offers a few different job types to express tasks. We will be using Rake, but check out all the options below:
runner “MyModel.some_process”
rake “rake:task”
command “/usr/bin/my_great_command”
- You can also define your own job type. The instructions are outlined in the whenever documentation.
5. Setting up your Rake task
Rails comes with Rake, so nothing to install here. If you don’t already have a task you plan to use, you can generate a new Rake task like this:rails g task <NAMESPACE> <TASKNAME>
. In our example, it looks like this:
rails g task sample test
We can find the file for this task in lib/tasks/sample.rb
. Now let’s write a task! Starting off with something simple, this task is just going to print a greeting to Cron.
namespace :sample do desc 'saying hi to cron' task :test => [ :environment ] do puts 'hi cron :)' endend
6. Adding and running the task
Now that we have a Rake task, we can stick it inside our interval.
every 1.minute do rake 'sample:test'end
Next, we update our crontab to let cron know about our task:
whenever --update-crontab
If we go to wherever our output is, log/cron.log
in this example, we should see something glorious like this — give it a few minutes though!
Please note: Whenever defaults to a production environment, if you want to set it to development, run the following when updating your crontab:
whenever --update-crontab --set environment='development'
And that’s it! You’ve successfully implemented a cron job using whenever.
Some useful commands:
- Clear your crontab:
whenever --clear-crontab
- See your cron jobs:
crontab -l
Some things to keep in mind:
While the example we walked through is very simple, don’t let that limit what you do with whenever and Cron! Anything that runs as a Rake task, model method, or command can also be automated. Whether that’s creating model instances or automating API calls. In my real life use case, I created a new instance of a Day model in my app every night at midnight.
This was my task:
namespace :day do
desc 'create a new instance of Day every day'
task :create => [ :environment ] do
Day.create()
end
end
And my schedule.rb
:
every 1.day, at: '12:00 am' do
rake 'day:create'
end
Resources: