June 06, 2010
Rake Tasks 101
I’ve been working with Rake quite a bit on my current project so I thought I’d share some beginner tips.
Before I go into Rake, what is it? Rake is a Ruby-based build program. Ruby on Rails uses Rake quite a bit in it’s process. If you’ve worked on a Rails project you’ll used one, some or all of the following: rake db:create, rake gems:unpack, rake db:migrate, and rake test. Now that’s not all of Rail’s Rake tasks, just some common ones.
You’re here to make your own Rake tasks so lets get started!
Make Your Task
Rake files can live within plugins or in your Rails.root/lib/tasks directory. In this post I’ll be referencing the latter. Let’s create our new rake file: Rails.root/lib/tasks/manners.rake
Next we can declare our Rake task in the manners.rake file:
1 2 3 4 5 6 |
namespace :manners do desc 'the description of what my rake task will do' task :greet do puts 'Hello from Rake' end end |
After thats done we should be able to execute our new task at the command-line with rake manners:greet.
Task Variables
Let’s make the Rake task reusable. To do this we’ll pass in some variables. Rewrite the task block of the manners.rake file like this:
1 2 3 4 |
task :greet, :name do |cmd, args| puts “command: #{cmd}” puts “args: #{args.inspect}” end |
The output you receive from running the task again should look like this:
1 2 |
command: manners:greet
args: {} |
Now run the task again, but define the variable by calling it like this, rake manners:greet[Dane]. That should yield:
1 2 |
command: manners:greet
args: {:name => “Dane”} |
This is where Rake tasks get interesting. Rake always passes in the command run into the task block as the first variable, thats why we see “command: manners:greet.” The second variable defines the hash that will contain all the variables passed into the the Rake task. The args hash index is any symbol that follows the task name symbol. We set :greet as the task name so :name becomes an available index in the args hash.
Lets rework the :greet task a little bit by defining our variables as :first_name and :last_name.
1 2 3 |
task :greet, :first_name, :last_name do |cmd, args| puts “Good day #{args[:first_name]} #{args[:last_name]}” end |
Run the Rake task and define both variables, rake manners:greet[Dane,Harrigan].
Spaces are not allowed when passing variables into a Rake task when calling it at the command-line. This is why its written as [Dane,Harrigan] and not [Dane, Harrigan]. Quotes can be used if spaces are necessary to a variable for example, rake manners:greet['Mr. Dane',Harrigan].
Rake Dependencies
We’ve built a Rake task so now lets make another and have it depend on manners:greet. Add a manners:question task that asks, “How are you?” Start by just making a Rake task like we did with manners:greet. To make the :question task dependent on :greet define the task as task :question => 'manners:greet'. Our manners.rake file should look like this:
1 2 3 4 5 6 7 8 9 10 11 |
namespace :manners do desc 'Greet the Rake user' task :greet, :first_name, :last_name do |cmd, args| puts “Good day #{args[:first_name]} #{args[:last_name]}” end desc 'Ask a question' task :question => 'manners:greet' do puts 'How are you doing?' end end |
If we run rake manners:question you’ll see that it greets us with, “Good day,” and, “How are you doing?” but we can’t set variables in a task dependency when its declared this way. Defining the dependency this way doesn’t work either, task :question => 'manners:greet[Dane,Harrigan]'. Let’s remove the ‘manners:greet’ dependency and call invoke on it instead.
1 2 3 4 |
task :question do Rake::Tasks['manners:greet'].invoke('Dane','Harrigan') puts 'How are you doing?' end |
Now when we run the task you’ll see a greeting to Dane and the question.
Tasks Run Once
When calling a task with invoke or execute Rake keeps track of whether or not it has already run. If the task has run already it wont run a second time. If we did the following you’ll only see one greeting.
1 2 3 4 5 |
task :question do Rake::Task['manners:greet'].invoke('Dane','Harrigan') Rake::Task['manners:greet'].invoke('John','Smith') puts 'How are you doing?' end |
You won’t see the greeting to John Smith. Well that’s rude, but we can fix this easily. If you want to call the task multiple times you’ll need to reenable the task each time before calling it. You can reenable a task anywhere, but I’ve found it makes the most sense to call reenable at the end of the task block of the one being reenabled. In our example we’ll call reenable inside of task :greet.
1 2 3 4 |
task :greet, :first_name, :last_name do |cmd, args| puts “Good day #{args[:first_name]} #{args[:last_name]}” Rake::Task['manners:greet'].reenable end |
Now if we call rake manners:question we’ll see both greetings. Perfect!
And We’re Done
Rake is a very nice piece of software and I encourage others to read up on it. I hope this post gave you enough understanding to start writing your own tasks. Also, please do comment if there are questions or other areas of Rake you’d like to know about. A Rake Tasks 102 post perhaps?
Rake Tasks 102 is up! If you liked Rake Tasks 101, I think you’ll enjoy 102 just as much.

June 07, 2010
Thanks for the article. Not enough stuff out there about Rake. Maybe you could talk about accessing the Rails stack (i.e., models etc) inside the rake tasks?
June 07, 2010
Good stuff. It might be useful to say a bit about the different technologies of job processing (Rake, cron, background jobs), and how they relate.
FWIW, I wrote a set of notes on using Rake outside of Rails here:
http://www.stuartellis.eu/articles/rake/
June 07, 2010
@Gokul I completely agree. There are very few articles on Rake. I hope this post gets some people running that are new to it.
@Stuart, Gokul Your ideas for another Rake post are great! I’m planning to put both of your ideas together for the follow up article. Thanks!
June 08, 2010
It may be worth noting that dependencies within the same namespace don’t need qualification:
can also be written
or
(Actually, I wasn’t aware that you could express the dependency as a string, which is a useful thing for the future).
I think the more “traditional”, make-type uses for Rake where dependencies are expressed in terms of files, are less well-known and seldom documented.
June 08, 2010
@Mike Great point on passing the dependency as a symbol opposed to a string. For those reading this far, he is correct that in my example I didn’t need to prefix the :greet task with ‘manners.’ Although, when calling a dependency that isn’t within the same namespace, it is necessary. For example, if ‘db:migrate’ was a dependency of :greet or :question, it would need to be passed as a string.
June 18, 2010
You mention that with execute and invoke, Rake will only run the task once, unless you re-enable it. This is true of invoke, but with execute you can run the task as many times as you want:
https://gist.github.com/f87177b29621afbaf682
June 19, 2010
@Myron Thanks for catching that! You’re right, execute can be called multiple times on a task. The difference between execute and invoke is invoke will run the task’s dependencies, but execute will not. For example, when the :question task was set up like as “task :question => ‘manners:greet’” calling execute on :question would not run ‘manners:greet’.
June 23, 2010
The second code example under “Rake Dependencies” has an error:
Rake::Tasks[:greet].invoke(‘Dane’,’Harrigan’)
should be:
Rake::Task[‘manners:greet’].invoke(‘Dane’,’Harrigan’)
Thanks for a good article!
June 24, 2010
@Michael Fixed! Thanks for the extra pair of eyes!
August 02, 2010
Hello,
Is it possible to declare task dependency like
task :foo => :bar do |cmd, args| end
and also have access to passed arguments?
I just don’t like to call dependency directly from the task body.
August 02, 2010
Ok, I found answer to my question. Here is the code:
task :foo, :name, :needs => :bar do |cmd, args| end
August 05, 2010
Btw. how often these Rake Task Variables are used?
They seem pretty cumbersome, and I guess people often just use env vars like
RAILS_ENV=production rake db:migrate
August 09, 2010
@Kawun sorry for the late reply. You’re correct, a rake task will look for :needs to be declared if variables are declared as well. And thank you for sharing your answer!
August 09, 2010
@Evgenily I haven’t seen many people used task variables. Environmental variables are used often in Rails for things like setting the Rails environment (your example) and setting version numbers (rake db:migrate VERSION=0).
A perk to passing variables at the environment level is they’ll be available to every task and dependency through ARGV, but the con to this is rake isn’t aware of these so it wont educate the user when they do rake -T.
I’ve used rake variables in my projects when I could, but it depends on the scenario.