Rake 101

This article explains the basics of Rake. It’s a super popular build tool that is written in Ruby. It offers a lot of flexibility and is used to manage all kinds of tasks. If you are using Rails, I recommend taking a look under the hood to find out what tasks are at your disposal and how to write your own.

Topics

  • What Is Rake?
  • Task List
  • Getting Started
  • Namespaces
  • Prerequisites
  • Passing Arguments
  • Tasks of Interest

What Is Rake?

Thanks to Rails, Rake has become a de facto standard as a Ruby build tool. It’s super popular in the Ruby community. Very early, the team behind Rails decided to use Rake as the builder for Rails itself, which meant that in the past when you downloaded Rails, you also needed a copy of Rake. That way it exposed Rake to a lot of people. A little later, it was included into Ruby (1.9) itself.

Rake de facto replaced the Unix utility Make as a build tool in Ruby land. A build tool like Rake is handy for automating tasks of various kinds—a software for managing tasks basically. It is often used for administration tasks in Rails—which is where you most likely have run into it so far—but its use cases are plenty. Some people write their ebooks in Markdown and have set up Rake tasks that convert the relevant files to intermediate HTML files, which then in turn get converted into ebook formats, for example. Saves a lot of trouble to use Rake for that.

What makes Rake really powerful is that these tasks can relate to one another and can be built on top of each other. Also, since it is written in Ruby, you can write any Ruby code for your tasks. Want to use Ruby libraries in your Rake tasks? No problem! Fun fact: it is the most downloaded RubyGem, with over 100 million downloads. So definitely something in your tool belt that you should pay a little more attention to. 

It was conceived by the late Jim Weirich, a well-known and beloved developer, speaker and contributor to the Ruby ecosystem. It’s a beautiful tool really—thanks, Jim! RIP!

Task List

Let’s take a look at some tasks that Rails offers right out of the box. I bet you are a bit surprised at what’s available if you haven’t checked before. In the relevant directory of your app or your Rakefile, you can list them by typing the following into a shell:

Output:

The output in a Rails app is surprisingly plentiful, isn’t it? You can find a lot more handy tasks than the usual rake db:migrate or rake routes that we are so familiar with and run multiple times on a daily basis. 

On the left, you see the various tasks, and on the right, you see what is optionally provided as a description to every rake task. If you want to see the complete list, which among other things also includes tasks that lack a description, you need to add an additional flag.

Shell:

Output:

Surprise, almost three times as much! Take a look at them and play around if you like, but commit the highlights to memory for later usage in the future. Checking the tasks to see what’s available might prevent you from reinventing the wheel.

Getting Started

A Rakefile can have one of the following five appearances:

  • rakefile.rb
  • rakefile 
  • Rakefile
  • Rakefile.rb
  • .rake files

Mostly you will see the plain Rakefile version, but a framework like Rails needs more complex organization. Use whatever gets your blood flowing. You start by creating a Rakefile or files with a .rake extension if you want to split tasks up logically over multiple files. Then define your tasks inside either of them.

Custom Rakefile Organization

Rails makes this incredibly easy. It has a Rakefile in the root of your app. It contains the following:

Rakefile

When you have a lot of custom tasks, it makes more sense to split them into discrete .rake files and place them in lib/tasks. The Rakefile above just loads them, but the lib directory is a better logical home for the tasks. There is even a Rails generator to automate part of the process. If you type:

Shell

You will get a Rakefile placed automatically in the right directory. Even the task is already set up for you. Nice! In other projects, not using Rails, you just need to create a rakelib directory and place your Rakefiles in there—preferably with .rake file extensions. Then create a file called Rakefile and all these files are already at your disposal.

Rake Task Anatomy

lib/tasks/some_task.rake

For the complete Ruby newbies among you, and for people coming from bracket-heavy languages, this is how it would look with parentheses.

Looks very weird, by the way. Just lose the extra parentheses—nobody writes tasks this way.

We provided a named task :about with a description that not only reminds us in the future what we wanted to achieve with particular tasks, but also shows up when we run rake -T. Don’t get lazy on this part; it’s probably not worth it.

Right below is the keyword task that defines a new task named about. This can be invoked on the command line via rake about which does its magic then. rake :about on the other hand will cause Rake to abort, not knowing “how to build task :about”.

Via the do end block, we have a lambda, and its body specifies what the task does. This is the basic setup a task will need. It offers a lot more options, of course, but the overall structure is the same. 

require / import

Some Rakefile

If you need to include other Ruby files or Rakefiles, it can easily be achieved by a standard require statement.

Some Rakefile

Rake itself provides us with another way to do this—the import method. This can be used in any line of the Rakefile. This one will help when you run into trouble because the required file was loaded before the Rakefile finished loading and blew up therefore. The imported file, on the other hand, will always load after the Rakefile.

invoke & execute

Sometimes you might want to execute some defined task from your Task class manually. For this purpose, you have two methods of the Rake::Task class: execute and invoke.

With the Rake::Task['some_task'] code, we got the some_task Rake task to execute. It returns an instance of the Rake::Task class and then runs any method on it that is available.

Namespaces

A very cool feature is the ability to namespace your tasks. You probably have used this dozens of times already. When you run rake db:migrate, you have made use of the db namespace, for example. You invoke the task by separating it with a colon : from the namespace. Namespaces are a handy way to organize your tasks within in a rake file—it keeps them logically separated. Multiple namespaces like rake time:zones:all are fine, by the way.

Other examples include:

Some Rakefile

This is the basic setup. In reality it’s much more complicated and can even be nested multiple times. Have a quick peek at the Rails codebase and see for yourself how rake db:migrate is implemented. Don’t feel bad if it’s over your head. Just look around, try to discern how it’s structured, and move on for now.

Prerequisites

Another strategy to organize your tasks and to keep them DRY is using prerequisites for executing a task. It’s like a dependency that has to run first before the actual task starts its job. That way you can build up more complex tasks—as complex as you need. But I would recommend not getting too clever and to keep it as simple as possible—and as easy to understand as possible as well.

Some Rakefile

If you want to rely on multiple tasks, you just stick them into an array. The order in which you place them matters, of course.

If you run the rake task that depends on the other, we’ll get the following output:

Shell

The order in which you define your rake tasks has no effect on the output—only the order in which you place the prerequisite tasks in the array for task dependencies. Also, please use the hashrocket => syntax for this.

A long list of dependencies might be a code smell. If you have to deal with something long, clean it up by encapsulating it inside a method which we then pass as a prerequisite.

In the context of prerequisites, one thing to keep in mind is that you only need to mention a namespace if you are outside the relevant one.

Some Rakefile

Important to note, though: in case you do need to mention the namespace, you have to pass the prerequisite as a string => 'marsellus_wallace:call_winston_wolf'.

Of course, the examples above are goofy and not real-life examples, but the intention was to show you how prerequisites work and how you’d put them together while they depend on each other.

Passing Arguments

You have two options to pass arguments to Rake tasks: either by using Bash variables or by making use of Rake’s syntax itself.

ENV Variable

In case you haven’t played with Bash before—or Bash sounds like gobbledegook to you—let’s take five and start from the beginning. Bash in your shell offers two sorts of variables: global (aka environment) variables and local ones. Both are written in uppercase. The environment variables are global ones, which means they are available in all shells and don't vanish when you close one—unlike local Bash variables, which are only available in the current shell. 

Environment variables can contain data that can be used by multiple applications and are often used as a handy way to share configuration settings. In contrast to that, local Bash variables are just that, local. In our context of using Rake, you have the ability to access both via Ruby and in effect pass variables from the command line.

FYI

Just as a little aside, if you type env or ENV in your shell, you’ll get access to a whole bunch of environment variables. I redacted the list, but for a better understanding of what environment variables are and what they include, I encourage you to run it for yourselves.

Shell

Output

If you want to see a list of local Bash variables, you can run set.

Shell

The set command gives you a lot more output, but the above shows you the relevant bits right away.

Ruby’s ENV Class Method

Ruby offers a way to use environment and local Bash variables alike via a hash-like accessor. For our needs, when we pass a variable to a Rake task, it’s going to be a local Bash variable, which you can find in the list of variables running set or a variation of it. Ruby can read it out using ENV['VARIABLE'].

Shell

What I want to make clear, though, is that this variable won’t get added to the ENV list that your system uses—the stuff that you saw calling env from the shell. To add it to that list, you would need to export it. This is another story, but I thought I should make this clear.

Some Rakefile

In this task definition, you can see how we prepared to accept or incorporate the variable passed to the task invocation. Ruby’s ENV[BASHVARIABLE] does all the heavy lifting. If BOOKTITLE had been a global environment variable, though, we could have accessed it inside this task definition as well with this syntax.

Rake Parameter Syntax

The second approach is using pure Rake syntax. You simply pass variables into square braces. That approach is better, and you can keep things more isolated. Why involve Bash if Rake is perfectly capable of handling this? Plus you don’t have any Bash variables floating around that way. If you want to pass multiple arguments into a task, it’s a lot more elegant as well.

Shell

Some Rakefile

When you pass in more arguments than you have defined in your task, you can simply access them via args. args.extras displays an array of all the additionally passed in parameters. args.to_a shows you all the parameters—in an array as well, of course.

Tasks of Interest

Below is a short list of Rake tasks that come with Rails:

  • db
  • doc
  • tmp
  • stats
  • notes
  • about
  • secret
  • assets
  • routes

db

Below are a couple of useful tasks under the db namespace for running Active Record migrations:

rake db:version prints the current version of the schema. The output looks something like this:

rake  db:migrate runs the last migration(s)—those which haven’t run yet. You can also pass it a specific migration to run.

Shell

rake db:create creates your database. If defaults to the development and test databases.

rake db:test:prepare makes sure that migrations that are already run on your development database are also run for your test database. If the test database’s schema were out of sync with your development database, it would not be very useful, of course.

rake db:drop:all drops both test and development databases by default.

rake db:migrate:up, rake db:migrate:down runs the up and down methods for the migration in question.

rake db:redo ensures that, after you have run a migration, the migration is reversible. It runs rake db:down first and then rake db:up.

rake db:rollback undoes the last migration.

rake db:drop drops by the development and test databases by default.

rake db:reset drops the databases first and sets them up again by loading the schema and seeding the database.

doc

rake doc:app generates documentation under doc/app. It creates HTML pages about your source code for easy browsing. Pretty cool!

Screenshot

A screenshot of the mission controller

rake doc:rails generates an API documentation under doc/api—also as HTML pages. Handy if you are offline, I guess.

tmp

The tmp directory in the root directory of your Rails app is the place for temporary files—most prominently files for sessions and cache. rake tmp:create sets you up with everything you need for dealing with temporary files. rake tmp:cache:clear clears the tmp/cache directory. rake tmp:sessions:clear clears the tmp/sessions directory.

stats

rake stats gives you a nice overview of your app.

notes

You can leave notes in your code. You can prefix them in your comments with TODO, FIXME, OPTIMIZE.

Some Ruby file

When you run rake notes, Rake parses these prefixes and gets you a list of all these notes from your code. 

Shell

You even get a detailed list where you find them—directory, filename, line number [Line Number], everything is included. Awesome, right?

about

rake about gives you an overview of version numbers for:

  • Rails
  • Ruby
  • RubyGems
  • Database adapter
  • Schema version
  • Middleware
  • Application root

and lots of other useful information.

secret

If you are paranoid about your session key and want to replace it, rake secret will generate a new pseudo-random key for you. It doesn't replace the key for you, though. The output in the shell looks like this:

assets

rake assets:precompile lets you precompile your assets under public/assets. If you want to get rid of older assets, just run rake assets:clean. Finally, running rake assets:clobber deletes the whole public/assets directory.

routes

rake routes might be the most important one. It shows you all the routes in your app.

Final Thoughts

Writing Rake tasks without knowing what’s going on under the hood gets boring pretty quickly. This article was a little warm-up and introduction for people who wanted to peek into Rake for the first time. 

I hope it covered all the basics without being too dry—after all, Rake is super dope and all, but at the same time, it’s maybe not the sexiest of toys. This basic knowledge of Rake, though, should give you all you need to get dangerous and expand your knowledge of the Rake tasks that are at your disposal.

Tags:

Comments

Related Articles