DEV Community

Cover image for Scheduling tasks in rails with the Whenever gem
Joe Christensen
Joe Christensen

Posted on

Scheduling tasks in rails with the Whenever gem

How do we schedule tasks?

Have you ever wanted to schedule tasks for your website? Like removing user accounts that are over a certain age of inactivity? Or adding money to a user's account once a week? How about sending automated newsletters daily? Well, a commonly used utility that handles this is the cron.

What is cron?

The cron is a command-line utility job scheduler. It allows users to set up tasks, also known as cron jobs, generally in the form of commands or scripts, that will run automatically on defined intervals. But how does it work exactly?

Well, you define cron jobs using a very specific syntax in a file called a cron table, primarily known as a crontab.

Note: The deploying platform Heroku does not implement cron and the whenever gem. If you wish to schedule tasks on Heroku, use their Heroku Scheduler add-on found here.

The jobs follow this given syntax:

# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6)
# │ │ │ │ │                       
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <command to execute>
Enter fullscreen mode Exit fullscreen mode

As you can see, there are five asterisks that correspond to minutes, hours, day of the month, month of the year, and day of the week. After defining these parameters, you write the cron job that you want to run.

An example of a cron job looks like this:

1 0 * * * printf "" > /var/log/apache/error_log
Enter fullscreen mode Exit fullscreen mode

What exactly is this cron job doing? Well, on the second minute of the first hour of each day, it prints an empty string the the error_log file at the given file path. This essentially clears the error_log file once a day.

There are also useful specifying commands built into cron like /n and ,. /n means "every n-th interval" and , allows you to specify multiple time intervals.

Lets look at an example using these:

*/5 1,2,3 * * * echo hello world
Enter fullscreen mode Exit fullscreen mode

So, for the minutes, this cron job has */5 and for the hours, it has 1,2,3. This means that the cron job will run and print "hello world" every fifth minute of the first, second, and third hour.

This equates to it running at 1:05, 1:10, 1:15, so on and so forth for the hours 1, 2, and 3.

Whenever Gem

So now that we know a little bit about cron jobs, how do we use them with our rails backend? Well, lucky for us, there's a ruby gem that sets up cron and translates ruby code into a cron table for us. That gem is the Whenever gem.

Setting Up

To set up the whenever gem, you must first install it or add it to your GemFile.

To install it type:

gem install whenever

or add this to your GemFile:

gem 'whenever', require: false

Once you have installed the whenever gem, you need to create a special ruby file called schedule.rb that will house our crontab.

Do so by running this command:

bundle exec wheneverize .

If successful, you will find a schedule.rb file inside of your config folder.

Now, before starting to add tasks, it is good to add these lines to the top of your schedule.rb:

#Creates a output log for you to view previously run cron jobs
set :output, "log/cron.log" 

#Sets the environment to run during development mode (Set to production by default)
set :environment, "development"
Enter fullscreen mode Exit fullscreen mode

The first command is incredibly helpful as it creates a log of all previously run cron jobs. This is the best way to check and see what the cron is doing. If there are errors, check there. If you're wondering if a job is running, check there!

The second command sets the crontab to run while in the development environment. It is good to have while setting up your cron jobs to make sure everything is working fine.

Job Syntax

The Whenever gem provides some very straightforward and easy syntax for creating cron jobs. Instead of specifying a bunch of information and having asterisks in place, you can just create a code block to run at a given interval.

An example of this is like so:

every 1.minute do
   #Tasks defined here will run once a minute
end

every 3.hours do
   #Tasks defined here will run once every 3 hours
end
Enter fullscreen mode Exit fullscreen mode

Note: The quickest interval you can define is 1 minute.

To get a full list of possible intervals, check the whenever gem's documentation here

Job Types

So, we know how to define the intervals that we want the tasks to run on, but how do we define the tasks themselves? The whenever gem provides three useful job types that we use to define our tasks.

The first type is the runner

The runner allows access to a database table's model. You can define a class method in the model.rb file for a specific table, and then access and run it using the runner job like so:

every 1.minute do
   runner "MyModel.some_method"
end
Enter fullscreen mode Exit fullscreen mode

The second type is rake

Like the name implies, rake is used to run a predefined rake task. First, you must create a rake task using the command rails g task <NAMESPACE> <TASKNAME> like so:

rails g task example say_hello

This will generate a task file in the lib/tasks folder called example.rb

Set up the example task to print "hello world" to the console like so:

namespace :example do
  desc "says hello to the world"
  task say_hello: :environment do
     puts "hello world"
  end

end
Enter fullscreen mode Exit fullscreen mode

Now, go back to your schedule.rb file and add this task to the interval block like so:

every 1.minute do
   runner "MyModel.some_method"
   rake "example:say_hello"
end
Enter fullscreen mode Exit fullscreen mode

Every 1 minute, the example:say_hello task will now run and print "hello world" to the console.

The third type is command

Again, this type is very straightforward. The command job type will run a given command at the defined interval

It is set up like so:

every 1.minute do
   runner "MyModel.some_method"
   rake "example:say_hello"
   command "/usr/bin/this_is_a_command"
end
Enter fullscreen mode Exit fullscreen mode

The command found at that file path will now run every 1 minute.

The whenever gem also lets you define your own job types. Read the documentation to find out how.

Updating crontab

Every time you make a change to the schedule.rb file, you must update the crontab with the newly changed file in order to implement the changes.

You can do so by running this command:

whenever --update-crontab

To make sure it worked, run this to view all current cron jobs:

crontab -l

You can also clear your crontab by running this:

whenever --clear-crontab

Once you have your tasks defined and your crontab updated, keep an eye on your cron.log file to view the changes every time a cron job runs.

Conclusion

Cron is a very useful tool that allows for defining and running scheduled tasks. The Whenever gem allows for easy manipulation of the Cron from a rails backend.

This was just a brief overview of whats all available with the Whenever gem. To learn more, check out the documentation here.

To learn more about the Cron, check out the wiki here and a useful guide here.

If you want to learn how to set up scheduled tasks using Heroku, check out their scheduler's documentation here.

Top comments (3)

Collapse
 
vuducvi1999 profile image
Vuducvi1999

pretty detail!

Collapse
 
mattschaefer profile image
mattSchaefer

great guide- thanks for the breakdown!

Collapse
 
ilias_gimranov profile image
Ilias Gimranov

Thank you for guide. But I still have a question.
How to pass arguments to a rake task using whenever?

every 1.minute do
   rake "example:say_hello" #how to pass arguments to this rake task?
end
Enter fullscreen mode Exit fullscreen mode