In Progress
Unit 1, Lesson 21
In Progress

Rake Global Tasks

Rake isn’t just for running tests. You can use Rake to organize all your favorite development automation tasks across ALL your projects. Find out how, in this episode!

Video transcript & code

Ruby on Rails (version 5.1 and higher) ships with system tests. We run these with rake test:system.

Unlike unit and integration tests, system tests put an application through its paces at a high level by simulating human browser interactions.

(By the way, if you came to Rails more recently, you might be used to typing rails test rather than rake test. But the rails just passes this command through to the Rake tool.)

One thing that surprised me when getting started with Rails system tests is that they aren’t included in a regular rails test run.

It would be nice to have a single command to run all the tests at once.

In order to add a new Rake task to this project, we can add a new .rake file in lib/tasks

In this file we re-open the :test namespace, and add a new task named :all.

We make this task depend on the Rails-provided test and test:system tasks.

And we add some documentation, so the task will show up in rake task lists.

namespace :test do
  desc "Run ALL tests, including system tests"
  task :all => ["test", "test:system"]
end

Now we can invoke rake test:all to invoke all automated tests at once.

rake test:all

This is fine for a single project. But if we work on a lot of Rails codebases, it might be nice to have this shortcut everywhere, without having to add it to each project individually.

We can do this with one of Rake’s lesser-known features: system tasks, aka global tasks.

Let’s remove the added code from this codebase.

Then we’ll create a directory named .rake in our home directory.

And we’ll edit a new file named rails.rake in that directory.

Let’s paste in our task definition to run all tests.

namespace :test do
  desc "Run Rails tests, including system tests"
  task :all => ["test", "test:system"]
end

(A quick side-note here: if you’re on a Windows host, this directory needs to be named Rake, with a capital R and no dot.)

Now we’ll go back to our project, and run rake test:all

…and it says it can’t find a task by that name.

$ rake test:all
rake aborted!
Don't know how to build task 'test:all' (See the list of available tasks with `rake --tasks`)
Did you mean?  test:mailers

However, if we run rake -g test:all, we get a different error:

$ rake -g test:all
rake aborted!
Don't know how to build task 'test:system' (See the list of available tasks with `rake --tasks`)

This time it says that it can’t find test:system. That’s definitely a problem, but let’s talk about what worked here: apparently, it found our test:all task!

The -g flag we used tells Rake to look for global tasks. These are also called system tasks in Rake parlance.

If we run rake -g -T to list tasks, we see a list of all of these global tasks.

$ rake -g -T
rake test:all  # Run Rails tests, including system tests

Let’s talk about what we don’t see here: we don’t see any of the Rake tasks defined in the local Rails project. That’s because Rake operates in either project mode or global mode. It won’t combine global and project tasks into a single list.

And that explains the error we saw earlier.

$ rake -g test:all
rake aborted!
Don't know how to build task 'test:system' (See the list of available tasks with `rake --tasks`)

Our test:all task declares a dependency on test:system. But in global mode, Rake can’t see project-local tasks like test:system.

But that’s OK. We can redefine the task to start another non-global Rake as a subprocess instead.

namespace :test do
  desc "Run Rails tests, including system tests"
  task :all do
    sh "rake test test:system"
  end
end

And now we can run rake -g test:all in any Rails project.

When I first discovered that Rake can’t combine project-local and global tasks into a single list all at once, I was a little disappointed. But as I’ve thought about it more, I’ve realized how confusing it could be to have these two lists merged into one… especially if some of the tasks had the same name. Now I feel like the extra step of specifying -g to switch Rake into global mode is helpfully explicit.

I’ve been using the example of automating a common Rails development task. But the great thing about Rake global tasks is that they aren’t limited to Rails projects or even doing anything Ruby-related. If you’re like me and you enjoy writing system automations in Ruby, Rake global tasks are a great way to organize all the little automations you might otherwise have written as shell scripts or aliases.

That’s all for today. Happy hacking!

Responses