There’s a nice Railscast introduction to rake for Rails, which goes into a number of other important details that aren’t covered in this post. Below is a little tutorial of creating a Rails rake task and getting it to run remotely on heroku.

Introduction to Rake

In lib/tasks, create a file called greet.rake

task :greet do
   puts "Hello world"

By naming the task .rake and putting it in this special place rails will automatically pick it up and make it available to you. You can see it listed if you type: rake -T on the command line. To run it:

rake greet

which will print “Hello world”

to run one task before another, specify a dependency like this (multiple tasks may be specified in the same file):

task :ask => :greet do
   puts "How are you?"

Writing a Practical Rake Task

Now for the task at hand, I’m going to create a rake task which creates a bunch of fake data for me to test with. First I’ll create a little experimental app:

rails rake_example
cd rake example
script/generate scaffold person first_name:string last_name:string
rake db:migrate
rails generate admin fake_people

Here’s the rake task (lib/tasks/fake_people.rake):

require 'faker'

namespace :admin  do
  desc "create some fake data"
  task :fake_people => :environment do
    print "How many fake people do you want?"
    num_people = $stdin.gets.to_i
    num_people.times do
      Person.create(:first_name => Faker::Name.first_name,
                    :last_name => Faker::Name.last_name)
    print "#{num_people} created.n"

Note that I’m using the faker gem (docs here) and I created a task dependency on loading the rails environment so I could access my Person model.

Now I can run

rake admin:fake_people

and it will prompt me to ask how many I want and then it will create them. Cool goodness, yes?

Running Remotely on Heroku

We’re not done yet. I want to deploy this on heroku and be able to run the task remotely. For this, there are two gotchas, first I can’t run an interactive script remotely; also I need to tell heroku that I am using the fake gem and make sure it is installed.

1) removing interactivity

Instead of an interactive script, we can set an environment variable or command line argument (thanks to a tip by Adam Wiggins).

My modified task looks like this:

require 'faker'

namespace :admin  do
  desc "create some fake data"
  task :fake_people => :environment do
    num_people = ENV['NUM_RECORDS'].to_i
    num_people.times do
      Person.create(:first_name => Faker::Name.first_name,
                    :last_name => Faker::Name.last_name)
    print "#{num_people} created.n"

which I can call locally from the command line like this:

rake admin:fake_people NUM_RECORDS=1

2) adding gem to heroku

I need to create a gems manifest, which sounds fancy, but is simply creating a .gems file at the root of my app with contents similar to what I would put in my config environment.rb to specify that my app requires a gem:

faker --version ">=0.3.1"

3) Deploy and Run

So I can deploy my app to heroku with the usual steps

git init
git add .
git commit -m "example app for rake script testing"
heroku create
git push heroku master
heroku rake db:migrate

and run the task remotely:

heroku run rake admin:fake_people NUM_RECORDS=1

6 thoughts on “creating a custom rake task

  1. If you want your greet task to appear when you do ‘rake -T’ you need to add a description.

    Also if you envision doing ‘rake gems:install’ at any point to you should move ‘require faker’ into the body of a task. Otherwise if you run ‘rake gems:install’ without faker installed, rake will fail. Here you could put it as the first line of your ‘fake_people’ task but you could also create a ‘require_faker’ task that did the require and was a dependency of ‘require_people’.

  2. This is a very helpful post, thank you for writing it!

    Are there any significant updates to the above methodology you are aware of for Rails 3?

  3. Thank you for your post…

    Could you tell me the significance of :environment in :task_name => :environment. what happens if i vomit this :environment ?

  4. :task_name => :environment will load the Rails environment, which will allow you to access things like your ActiveRecord models or Rails config. Without that you can just run pure Ruby code.

  5. The latest version of Heroku add “run” to the command. Therefore:

    %> heroku run rake:db:migrate

    instead of:

    %> heroku rake:db:migrate

  6. sorry my comment was it should be:

    heroku run rake db:migrate

    (replace colon with a space between rake and db)

What do you think?