DEV Community

Risa Fujii
Risa Fujii

Posted on • Updated on

Cron jobs in Rails: A simple guide to ACTUALLY using the Whenever gem (now with tasks!)

I had the chance to use whenever to schedule a task in Rails, and I'm writing this post for future reference. I know there are other ways to schedule regular tasks in Rails, but this is one - maybe the most - simple way to do it.
Note: Guide is for Linux or MacOS systems.

What are cron jobs?

Cron is a job scheduler that lets you execute recurring tasks at fixed times (e.g. every minute, every hour, every day at 3pm).
Jobs are written in a file called the crontab (short for β€œcron table”), typically saved here: /usr/bin/

Use whenever for cron jobs in Rails

Whenever is a gem that helps you set up cron jobs. Instead of having to manually edit the crontab yourself, you can let whenever update the crontab for you by writing your jobs in Ruby code. You'll make your edits in schedule.rb

Note: One thing worth mentioning is that the actual task itself (in my case, sending out batch messages periodically) should not be written in the crontab, or in schedule.rb, the Rails file for writing in your crontab. The task itself should be written separately as a Rake task. You can then schedule that Rake task on a regular basis as a cron job.

Steps

1. Install whenever

Include it in your app's Gemfile, and run bundle to install.

gem 'whenever', require: false
Enter fullscreen mode Exit fullscreen mode

2. Schedule your cron job in schedule.rb

config/schedule.rb is the file for specifying when to execute the job, and the name of the Rake task (again, the actual task should be written in a separate file).

Execute this from your Rails app root to create the schedule.rb file:

bundle exec wheneverize .
Enter fullscreen mode Exit fullscreen mode

Now, write your job specifics in schedule.rb. For example, if you want to execute the Rake task batch:send_messages every minute:

every 1.minute do
  rake 'batch:send_messages'
end
Enter fullscreen mode Exit fullscreen mode

You can also do every 2.hours and every 1.day and so on.

3. Write the whenever task to your crontab

Let whenever write your job into the crontab.

bundle exec whenever --update-crontab
Enter fullscreen mode Exit fullscreen mode

Important
Whenever takes into consideration your Rails app environment when writing to the crontab. If you're using the development environment, then run the command with the option below (default is production).

whenever --update-crontab --set environment='development'
Enter fullscreen mode Exit fullscreen mode

For your reference, this is what your crontab will look like after running this command (RAILS_ENV is development):

# Begin Whenever generated tasks for: /Users/<USER>/<APPNAME>/config/schedule.rb at: 2019-01-14 18:15:41 +0900
* * * * * /bin/bash -l -c 'cd /Users/<USER>/<APPNAME> && RAILS_ENV=development bundle exec rake batch:send_messages --silent'

# End Whenever generated tasks for: /Users/<USER>/<APPNAME>/config/schedule.rb at: 2019-01-14 18:15:41 +0900

Enter fullscreen mode Exit fullscreen mode

Other commands

  • Display crontab: crontab -l
  • Wipe crontab of your whenever task: bundle exec whenever --clear-crontab

4. Create the Rake task

Rake tasks follow a specific naming convention - namespace and task name. Namespaces are for grouping certain tasks together. For example, in the case below, if you had multiple batch tasks, you could put them all under batch.

Generate a file for your Rake task under lib/tasks/batch.rake. They follow the formula of:

rails g task <NAMESPACE> <TASKNAME>

So to generate our send_messages task in the batch namespace, we'll enter the following into terminal:

rails g task batch send_messages
Enter fullscreen mode Exit fullscreen mode

Now write your task, which takes two parameters: desc, which is an arbitrary description of your task, and task, which is the code to be executed.

For example:

namespace :batch do
  desc 'Send out batch messages'
  task send_messages: :environment do
    # The code to actually send our messages would go here
  end
end
Enter fullscreen mode Exit fullscreen mode

You can test your Rake task by executing it from the command line as below.

rails batch:send_messages
Enter fullscreen mode Exit fullscreen mode

Thanks for reading!

Top comments (9)

Collapse
 
moriort profile image
Moriort

Easy to understand.

Thank you!

Collapse
 
bachdx2812 profile image
bachdx

Thank you for your post

btw, you can run this command to update crontab for development environment without actually editing the crontab file

whenever --update-crontab --set environment='development'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
risafj profile image
Risa Fujii

That's really convenient! I'll update the post accordingly. Thank you :)

Collapse
 
risafj profile image
Risa Fujii

Thanks for the comment, I'm happy to hear it!

Collapse
 
esbendamgaard profile image
Info Comment hidden by post author - thread only accessible via permalink
Esben Damgaard

I've made a new way of using "cron" in Rails, because I find the other ways cumbersome and error prone. Check out arask.

Collapse
 
icesatwo profile image
Isatou

Hi, new to rails here, running rails g task keeps over writing my rake file, do you have any idea why that could be? any help would be appreciated greatly!

Collapse
 
risafj profile image
Risa Fujii

Hi, why are you running rails g task multiple times? You only need to create the task once. Running the task is done via a command like rails batch:send_messages (Example taken from my blog post, please use your actual task name and namespace).

Collapse
 
rishudc119 profile image
Deepak Chauhan • Edited

Hi Risa, Is there any alternative for scheduling jobs in seconds because chron jobs scheduling requires time to be in minutes

Collapse
 
risafj profile image
Risa Fujii

Hi Deepak, I feel like that question goes beyond the scope of this post since it's a limitation of cron itself, not the whenever gem πŸ€” How about looking into solutions like the ones suggested here?

How to run scripts every 5 seconds?

Some comments have been hidden by the post's author - find out more