Scheduling tasks is so common nowadays, I wonder how long it will take until we see it baked in right in Ruby on Rails. Until then whenever is the gem that would do the job for us. whenever schedules the tasks by creating cron jobs on our server, adding some nice scoping too.
Installation
We can install the gem directly:
gem install whenever
or by adding it to the Gemfile of our app:
gem 'whenever', :require => false
and running the bundle
command:
bundle update
Next we run the generator:
wheneverize .
This command will add a schedule.rb
file inside the config
directory of our Rails app. It is here where we define the cron jobs using a lovely ruby DSL, rather the quite enigmatic cron syntax. Using whenever, we are sure that the job is defined correctly, and we’re not running a daily job every month ;).
The schedule.rb
has already some examples to guide us:
every 2.hours do
command "/usr/bin/some_great_command"
runner "MyModel.some_method"
rake "some:great:rake:task"
end
every 4.days do
runner "AnotherModel.prune_old_records"
end
We are going to schedule a rake task, which we’ll create next.
Schedule your task
Our rake task will be very simple and most futile:
namespace :tutor do
desc 'A simple task'
task :simple => :environment do
puts 'I am a simple task'
end
end
Let’s now update the config/schedule.rb
file to run the task every 24 hours:
every 24.hours do
rake "tutor:simple"
end
Plain English, right?!
Integrate with Capistrano
Now we need to the schedule to our server. We’re deploying with Capistrano 3, so we’ll see how we can integrate whenerver with it.
The github Readme suggests to add the below line to our Capfile
:
require "whenever/capistrano"
but this did not work well for me. Please try it, and if it works for you, you can ignore the rest of this section.
We can create our own Capistrano tasks. Let’s add the following block to config/deploy/production.rb
(or any other file with our Capistrano tasks).
namespace :deploy do
desc "Update crontab with whenever"
task :update_cron do
on roles(:app) do
within current_path do
execute :bundle, :exec, "whenever --update-crontab #{fetch(:application)}"
end
end
end
after :finishing, 'deploy:update_cron'
end
Here we call the whenever
command for our application, and tell the Capistrano to run at the end of the deployment process.
The cron file
Let’s run the Capistrano task directly to see how it has changed the cron file:
cap production deploy:update_cron
If we login to our server and run:
crontab -l
we should see the following lines:
# Begin Whenever generated tasks for: tutor
0 0 * * * /bin/bash -l -c 'cd /home/webadmin/apps/tutor/releases/20150418102048 && RAILS_ENV=production bundle exec rake tutor:simple --silent'
# End Whenever generated tasks for: tutor
Whenever uses /bin/bash -l -c
to run the commands on the server. We can clearly see that the command when run, will go to the release directory of our tutor app, and will execute the rake task with bundle
in a production environment. Which is what we want. For clarity, whenever has also added some scoping comments.
Happy coding!