Jacob Swanner Development Blog

Rake: Global Tasks

I’m hoping this to be the first post in a series about Rake, hopefully demonstrating some of the lesser known capabilities of the tool. But, seeing as this is the first in the series, I should probably give a little background about Rake itself.

Introduction

If you checkout the GitHub repository for Rake, you will see it describes Rake as:

A make-like build utility for Ruby.

And, unless you know what Make is, that description is probably not all that helpful. The Rake tutorial describes Rake as:

A build tool, written in Ruby, using Ruby as a build language.

Okay, so now we’ve seen the “build” term a lot, but again, if you are not familiar with Make, you are probably not too familiar with what a build tool/utility is. I believe the Wikipedia article for Rake does a better job summarizing it:

A software task management tool. It allows you to specify tasks and describe dependencies as well as to group tasks in a namespace.

But, now we’ve replaced one vague term, build tool, with another: task management tool. Because of that, I think we need a more helpful description; while not as succinct as those given above, here’s how I like to describe Rake:

  • Rake lets you define tasks
  • Tasks can depend on other tasks
  • Tasks are only run if needed
  • Tasks are only run once

But, we still need a definition for tasks. Since you define your tasks in Ruby, your tasks can do anything that you can do in Ruby. Which means you can do pretty much anything you want in your tasks – programmatically speaking.

Rakefile Format

We’ll go more in-depth with Rake’s domain-specific language (DSL) in future posts, but just to cover our bases, here’s a simple example:

task "hello" do
  puts "Hello, World"
end

You’d put this in a file called Rakefile, and to explain what that means: the task method is how you declare a task, you pass to the task method the name of the task ("hello" in our case), and you can give a task an action to run by giving a block to the task method. Now whenever we run the hello task, we’ll see the response of Hello, World:

$ rake hello
Hello, World

Global Tasks

Typically, Rake tasks are defined per project, by writing the task definitions in the Rakefile, placed in the root directory of your project. But, that’s not the only place you can define Rake tasks; Rake does let you define global tasks, these global tasks can be run from any directory – even those that do not have a Rakefile in their hierarchy.

  1. The first step is to make the ~/.rake directory where the task definition files will reside:

     $ mkdir ~/.rake
    
  2. Next create the file for the task definitions, the file’s name does not matter, but it must the have .rake extension:

     $ touch ~/.rake/hello.rake
    
  3. Add tasks to newly created file just like you would in any Rakefile; I’ll use our above hello task as an example:

     $ echo -e 'task "hello" do\n  puts "Hello, World"\nend' > ~/.rake/hello.rake
    
  4. Invoke global task, the -g flag is the important part:

     $ rake -g hello
     Hello, World
    

(Hopefully) Useful Example

So, now that we all know about global Rake tasks, perhaps you’re wondering how you can use them in your development workflow. Well, I’ll give you an example I use all the time, it’s a big long, so I’ve packaged it up in a gist.

Here’s a scenario where it can come in handy: suppose you are working in a topic branch of a Rails application, the topic branch has database migrations in it, you need to switch branches, but your app will not run properly in the other branch with the migrations present in the topic branch. What can you do in this scenario?

Well, you need to determine the migrations specific to this topic branch, and roll those migrations back:

$ VERSION=<version> rake db:migrate:down

Or, you can keep running rake db:rollback until all the migrations have been undone, but this assumes all the migrations needing to be rolled back are the latest one; which might not be the case if you’ve merged in the base branch, or rebased the topic branch, at any point.

Another option is to install the Rake task from the gist, and run:

$ rake -g rollback_branch_migrations[master]

Here I’m assuming that master is the branch you want to change to – wrapping it in square brackets like that ([master]) is how you pass arguments to a Rake task. This Rake task will handle determining the migrations specific to your topic branch, and roll those migrations back; that way you can switch branches and your database will be in the state the other branch expects.

TL;DR

To make global Rake tasks:

  1. Define tasks in ~/.rake/*.rake files
  2. Run tasks with -g flag: rake -g task_name