Automate Tasks in Rails Using the Whenever Gem

A walkthrough of cron job implementation using the whenever gem

Alexandra Radevich
The Startup

--

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:

  1. 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!

Beautiful, isn’t it?

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:

--

--