
If you’re developing with Rails you’ve probably encountered rake once or twice. This blog post aims to walk you through where rake came from and an introduction on how to use it effectively in your Rails apps.
A Little Bit of History
Rake is the project of Jim Weirich. It’s a build tool. For a good laugh and an even more in depth history check out the "rational.rdoc" from the Rake documentation. Essentially, rake started as an idea for using Ruby inside of a Makefile. Though Jim doesn’t sound convinced from the tone in that document, it is a good idea.
What’s the need for an automated build system at all? As usual, Wikipedia has the answer:
Historically, developers used build automation to call compilers and linkers from inside a build script versus attempting to make the compiler calls from the command line. It is simple to use the command line to pass a single source module to a compiler and then to a linker to create the final deployable object. However, when attempting to compile and link many source code modules, in a particular order, using the command line process is not a reasonable solution. [sic]
As the build process grew more complex, developers began adding pre and post actions around the calls to the compilers such as a check-out from version control to the copying of deployable objects to a test location. The term “build automation” now includes managing the pre and post compile and link activities as well as the compile and link activities.
It’s about Dependencies
This may be a bit of a stretch to say but build tools are about dependencies. One file or set of files depends on another set to get compiled, linked, or other fun things before the next set can be processed. The same idea exists in rake with tasks and task dependencies. Let’s look at a simple rake task. Save the following as “Rakefile” in any directory:
directory "tmp" file "hello.tmp" => "tmp" do sh "echo 'Hello' >> 'tmp/hello.tmp'" end
What we’re saying here is that the file named “hello.tmp” depends on the directory "tmp". When rake runs across this, it’s going to create the directory "tmp" first before running the "hello.tmp" task. When you run it, you’ll see something like the following:
[jason@brick:~/src]$ rake hello.tmp (in /Users/jason/src) echo 'Hello' > 'tmp/hello.tmp'
If you were to look at the "hello.tmp" file you would see the phrase "Hello". What happens if you run it again? You’ll see the same output again. What’s going on? Rake is generating the file again. It’s doing this because it can’t actually find the file tmp/hello.tmp from that definition. Let’s redefine the task:
directory "tmp" file "tmp/hello.tmp" => "tmp" do sh "echo 'Hello' > 'tmp/hello.tmp'" end
Now if you were to run it twice you would see something like this:
[jason@brick:~/src]$ rake "tmp/hello.tmp" (in /Users/jason/src) mkdir -p tmp echo 'Hello' > 'tmp/hello.tmp' [jason@brick:~/src]$ rake "tmp/hello.tmp" (in /Users/jason/src)
Rake now knows that the file task has been run.
Running Other Tasks
Rake tasks can take the form of having prerequisites and can depend on another task. Let’s say I wanted to get ready in the morning. My process would be something like this:
- Turn off alarm clock.
- Groom myself.
- Make coffee.
- Walk dog.
Let’s further assume that I have OCD and have to do all of these in order. In rake I might express my morning as follows:
task :turn_off_alarm do puts "Turned off alarm. Would have liked 5 more minutes, though." end task :groom_myself do puts "Brushed teeth." puts "Showered." puts "Shaved." end task :make_coffee do cups = ENV["COFFEE_CUPS"] || 2 puts "Made #{cups} cups of coffee. Shakes are gone." end task :walk_dog do puts "Dog walked." end task :ready_for_the_day => [:turn_off_alarm, :groom_myself, :make_coffee, :walk_dog] do puts "Ready for the day!" end
If I were to run this as is I would type rake ready_for_the_day and I’d see the following:
[jason@brick:~/src]$ rake ready_for_the_day (in /Users/jason/src) Turned off alarm. Would have liked 5 more minutes, though. Brushed teeth. Showered. Shaved. Made 5 cups of coffee. Shakes are gone. Dog walked. Ready for the day!
By running the ready_for_the_day task it notices that the turn_off_alarm, groom_myself, make_coffee, and walk_dog tasks are all prerequisites of the ready_for_the_day task. Then it runs them all in the appropriate order. You’ll notice that we can pass something in to the make_coffee task. If we were having a really tough day we could pass in a value to the COFFEE_CUPS environment variable and be more prepared:
[jason@brick:~/src]$ rake COFFEE_CUPS=5 make_coffee (in /Users/jason/src) Made 5 cups of coffee. Shakes are gone.
Namespaces
Rake supports the concept of namespaces which essentially lets you group together similar tasks inside of one namespace. You’d then specify the namespace when you call a task inside it. It keeps things tidy while still being quite effective. In Rails, you might notice the db:migrate task. In that example, db is the namespace and migrate is the task. Using the above example, we might put everything in to the morning namespace:
namespace :morning do task :turn_of_alarm .... end
Now if you were to run rake COFFEE_CUPS=3 morning:ready_for_the_day you would have the same output as above, only it only took 3 cups of coffee today. Score!
The Default Task
Rake has the concept of a default task. This is essentially the task that will be run if you type rake without any arguments. If we wanted our default task to be turning off the alarm from the example above, we’d do this:
task :default => 'morning:turn_off_alarm'
Running rake now produces the following:
[jason@brick:~/src]$ rake (in /Users/jason/src) Turned off alarm. Would have liked 5 more minutes, though.
Describing Your Tasks
You can use the desc method to describe your tasks. This is done on the line right above the task definition. It’s also what gives you that nice output when you run
... desc "Make coffee" task :make_coffee do cups = ENV["COFFEE_CUPS"] || 2 puts "Made #{cups} cups of coffee. Shakes are gone." end ...
Now when we run
[jason@brick:~/src]$ rake -T (in /Users/jason/src) rake afternoon:make_coffee # Make afternoon coffee rake morning:groom_myself # Take care of normal hygeine tasks. rake morning:make_coffee # Make coffee rake morning:ready_for_the_day # Get ready for the day rake morning:turn_off_alarm # Turn off alarm. rake morning:walk_dog # Walk the dog
You can add in a string to get tasks matching that displayed. Running rake -T af would show just the afternoon task.
Redefining Tasks
Let’s say you want to add on to an existing task. Perhaps you have another item in your grooming routine like styling your hair. You could write another task and slip it in as a dependency for groom_myself but you could also redefine groom_myself later on (shortened for brevity but you get the idea):
namespace :morning do .... task :groom_myself do puts "Brushed teeth." puts "Showered." puts "Shaved." end .... end ... namespace :morning do task :groom_myself do puts "Styled hair." end end
[jason@brick:~/src]$ rake morning:groom_myself (in /Users/jason/src) Brushed teeth. Showered. Shaved. Styled hair.

Invoking Tasks
You may at some point want to invoke a task from inside another task. Let’s say, for example, you wanted to make coffee in the afternoon, too. If you need an extra upper after lunch you could do that the following way:
namespace :afternoon do task :make_coffee do Rake::Task['morning:make_coffee'].invoke puts "Ready for the rest of the day!" end end
Which outputs:
[jason@brick:~/src]$ rake afternoon:make_coffee COFFEE_CUPS=1 (in /Users/jason/src) Made 1 cups of coffee. Shakes are gone. Ready for the rest of the day!
A real world example of this is the rcov:all task. I use this in Genius Pool for aggregate rcov data. It’s shamelessly stolen from Clayton Lengel-Zigich. Go check out that post for a good example of invoking other tasks from rake.
Refactoring
You’ll notice in the example above we’re delegating most of the work to already defined methods and tasks in the RSpec and Cucumber classes. As a general rule, try to keep your methods already defined other places and call them from rake with your specific options and use cases. Let’s say I had a Rails application that e-mailed all accounts in the system that their account was expiring in a certain number of days. Here’s one way to write it:
namespace :accounts do desc "Email expiring accounts to let them know" task :email_expiring => :environment do date = ENV['from'] ? Date.parse(ENV['from']) : Date.today accounts = Account.find(:all, :conditions => ["expiration_date = ?", date] accounts.each do |account| Notifier.deliver_account_expiration(account) end end end
A better way, that would let you test it more thoroughly would be to do the following:
namespace :accounts do desc "Email expiring accounts to let them know" task :email_expiring => :environment do date = ENV['from'] ? Date.parse(ENV['from']) : Date.today Account.notify_expiring(date) end end
This lets you unit test your notify_expiring method on the account class and make sure that it’s doing what it’s supposed to do. This is a small, made up example, but you get the idea. Here’s an example from Resque:
desc 'Restart redis' task :restart do RedisRunner.stop RedisRunner.start end
Notice the delegation to the RedisRunner class methods? This is a great rake task
Rails
You can get access to your models, and in fact, your whole environment by making tasks dependent on the environment task. This lets you do things like run rake RAILS_ENV=staging db:migrate. Rails will autmatically pick up tasks in lib/tasks. Any files named with the .rake extension will get picked up when you do rake -T.
Scheduling Rake Tasks
You can use cron to schedule rake tasks. Let’s say you wanted to run the account email expiration task every night at 12:15 on your production server, you might have something like this:
15 * * * * cd /data/my_app/current && /usr/bin/rake RAILS_ENV=production accounts:email_expiring
Misc
Rake.original_dir gives you the directory that the original rake task was run from.
Derivatives
- Thor is a more class based approach to solving some of the things rake does as far as actual tasks go.
- Capistrano is the de facto standard for deploying rails apps. Its syntax is inspired pretty heavily by Rake, but it is definitely not rake.
Further Reading
- "Testing Rake Tasks" by Jay Fields
- "On Rake" by John Barnette
- How To: Setup RSpec, Cucumber, Webrat, RCov and Autotest on Leopard | Clayton Lengel-Zigich
- Custom Rake Tasks Railscast
Useful Rake Examples
- Sample Rakefile from this article.
- redis.rake from Resque.
If you have any more useful rake examples, please let me know and I’ll add them here.
Photo Credits:
Shaker rake by dicktay2000 on Flickr.
Toy sampling megaphone by altemark on Flickr.




Thanks for the nice writing
This is a great article, thank you very much Jason!
Excellent Tutorial!
Thanks a lot.
Thanks a lot everyone, glad you found it useful.
Great post, thanks for the straight-forward info.
Excellent tutorial! Made my life a whole lot easier. Book-marked for future reference!
Great Tutorial …..its easy to ..understand…!
I'm still rusty on rake tasks so this has been a great reference. Thanks so much for writing it!
Hi, I would like to add a few details for people out there….
1. The rake files you create should be named with the .rake extension
2. Place your .rake files in /current/lib/tasks in order for it to be able to run
Other than that, great tutorial!
Thanks for the info! I feel like this should be the first thing mentioned …
Thanks. I was wondering about where to put them.
Where is current?
I have create a rake task as below
namespace :name do
desc “It's my details.”
task :its_me do
puts “I am Diganta Mandal”
user = User.first
puts “======#{user.login}=========”
end
end
But when i am trying to run this task using this command “rake name:its_me”, It's giving me error saying 'uninitialized constant User'
This is the output i am getting :-
I am Diganta Mandal
rake aborted!
uninitialized constant User
and below is the full rake trace
/home/debashis/Rails_app/pms/vendor/rails/activesupport/lib/active_support/dependencies.rb:443:in `load_missing_constant'
/home/debashis/Rails_app/pms/vendor/rails/activesupport/lib/active_support/dependencies.rb:80:in `const_missing'
/home/debashis/Rails_app/pms/vendor/rails/activesupport/lib/active_support/dependencies.rb:92:in `const_missing'
/home/debashis/Rails_app/pms/lib/tasks/its_me.rake:7
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `call'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `execute'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:597:in `invoke_with_call_chain'
/usr/lib/ruby/1.8/monitor.rb:242:in `synchronize'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:583:in `invoke'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2051:in `invoke_task'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `each'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2023:in `top_level'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2001:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake.rb:1998:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/bin/rake:31
/usr/bin/rake:19:in `load'
/usr/bin/rake:19
Could you help me out to solve this problem.
Thanks in a advance
Diganta
Try doing:
task :its_me => :environment do
…
end
Thanks for the write up, very handy.
Great help – just the level of detail I wanted !!
great write up! perfect for as primer.
hey jason. This tutorial is really useful, because my name is jason also, and so it looks exactly the same when I a run through your example.
Just a light note, your first example output of 'rake ready_for_the_day' says 5 cups of coffee, not 2 like it should.
well done broseph.. i got some rake tasks going in about 10 short sweet minutes.. you are a good and decent man!
The cron entry ’15 * * * *’ means run every hour, 15 minutes after the full hour. Rub every night at 12:15 would be ’15 0 * * *’.
Very interesting article, thanks a lot, you helped me understand how rake works!
very precise article! off to write my first rake task.
Excellent article, well written and concise. Plus you covered all the Rake questions I had but had no idea how to do because I’m a Rake n00b.
Thanks!
I still can’t figure out how to access a model defined in another file from my Rafile
my rakefile code is:
require ‘app’
desc “This task is called by the Heroku cron add-on”
task :cron => :environment do
User.dropbox
end
and of course in app.rb lies the Class User.
Any help?
Thanks for the awesome tut anyways!
p.s. the error is:
rake aborted!
no such file to load — app
Stefano – any chance you solved this problem? I’m getting it too.
It looks like your environment can’t find the “app” file. Try putting a more relative path to it?
谢谢, 写得很好!
Agreed!
Cool write up.. Thank you for sharing !!
Best write up online. Great work.
Thanks for that Jason very helpful and rich in details and history about make.
Great post!
Thanks, solved my rails task problem.
Thanks heaps for this tutorial!!! Just wondering, do you know how to return values in tasks?
Say I want:
task :parent => environment do
…
…
child = Rake::Task[:create_child].invoke
#would like to do stuff on the returned child
end
It’s a bit unconventional to return things from rake tasks. What are you trying to do?
I wanted a child task that would create a new record and insert it into the table. Back in the parent task, I need the id of the created record to later do other stuff with, so was wondering if there were ways of returning this information, rather than making it a global variable.
I currently have the child task as a method, so that I am able to return the record. As an aside question when would you use a task over a method, and vise versa.
Thanks heaps!
It’s hard to say without seeing the app but this doesn’t sound like something you want to be doing from tasks or using global variables. Maybe try moving your behavior in to methods and calling those from your rake tasks.
With that said, perhaps you can modify your tasks that operate on record to find the record first?
thanks for the reply. I think I will keep it as a method rather than a task.
As a ruby/rails newbie, what are the advantages of using tasks over methods?
thanks again
You’re a good writer, thanks for the straightforward tutorial. Went through it without a hitch.
It’s a greate tutorial. Thanks :)
You’re a good writer, thanks . With that said, perhaps you can modify your tasks that operate on record to find the record first?
Thanks, that was very useful! Thanks for the cron hint too!
I’m getting the error:
Could not find rake-0.9.2 in any of the sources
when I try to run rake from a cron job. Any idea on how to solve this? My rake task depends on the environment (=> :environment) and it works when I run from terminal.
I have a task that I would like to have run automatically throughout the day without user intervention. It will be reading in an RSS feed and using that data to update a database. The application is secure, using https and requiring user certs. So how do you go about having an automated task run with basically the rights of the server, as opposed to passing in user credentials?
Fantastic blog $author||I really love your blog.
Finally. A great post explaining what rake is and how it works.
Loved the explanation of the :environment task and what that does.
Great write up.