Scheduling tasks in rails with the Whenever gem
Published: 2021-09-30 17:25:21


#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](https://en.wikipedia.org/wiki/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](https://devcenter.heroku.com/articles/scheduler).

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>
```

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
```

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
```

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`:

```ruby
#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"
```

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:


```ruby
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
```

>**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](https://github.com/javan/whenever)


##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:

```ruby
every 1.minute do
   runner "MyModel.some_method"
end
```

###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:

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

end
```

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

```ruby
every 1.minute do
   runner "MyModel.some_method"
   rake "example:say_hello"
end
```

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:

```ruby
every 1.minute do
   runner "MyModel.some_method"
   rake "example:say_hello"
   command "/usr/bin/this_is_a_command"
end
```

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](https://github.com/javan/whenever) 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](https://github.com/javan/whenever). 

To learn more about the Cron, check out the wiki [here](https://en.wikipedia.org/wiki/Cron) and a useful guide [here](https://ostechnix.com/a-beginners-guide-to-cron-jobs/). 

If you want to learn how to set up scheduled tasks using Heroku, check out their scheduler's documentation [here](https://devcenter.heroku.com/articles/scheduler).