Prepend to an existing rake task

Rake is a commonly used tool in Ruby projects to provide command line tasks, Rails comes with a number of rake tasks included for example. It is quite well documented how to write rake tasks, a while ago however I needed to prepend something to an existing rake task. One way to achieve this is to write a new rake task that executes the code that you want to execute and then calls the existing rake task. The (perhaps obvious) downside to this method is that you have to execute another rake task than you usually would.

It turns out that there is another way, you can use the enhance method on an existing rake task. In this article I will briefly explore how you can use this method.

The example use case

Rails can run tests in parallel allowing you to run your test suite a lot faster. You can control the number of simultaneous tests that run by setting the environment variable PARALLEL_WORKERS to that number. On my development environment I have set this number to what works well for me generally so I don’t have to specify it every time when I run my tests.

While this is great there is one use case where you always want to run one test at a time: when you run the systems tests and want to watch them run on your browser.

Adding a prerequisite task

When you define a rake task you can specify other tasks that you want to run before executing it by defining its prerequisites, with the enhance method you can add additional prerequisites to an existing rake task.

In Rails you can add your own rake tasks by adding them as files with the rake extension in the lib/tasks directory (for other projects using rake you will have to look into the project documentation to determine where you add your customisations).

Take a look the below snippet:

namespace 'test' do                                                                  
  task 'override_parallel_workers' do                                                          
    unless ENV.fetch('SELENIUM_BROWSER', 'headless_firefox').start_with? 'headless_' 
      ENV['PARALLEL_WORKERS'] = '1'                                                  
Rake::Task['test:system'].enhance ['test:override_parallel_workers']                                 

This code adds a new rake task called test:override_parallel_workers, when the environment variable SELENIUM_BROWSER starts with “headless_” it sets the value of PARALLEL_WORKERS in ENV to 1 (overriding the environment variable inside the Ruby process without overriding it in the shell above). The test:system rake task is then given this new task as a prerequisite.

Note that the use of the SELENIUM_BROWSER environment variable is not something that is part of Rails’ defaults, it’s something custom to my Rails project’s setup (similar to what is described in my article on Capybara with Selenium van Vagrant without Rails). If you want to use the snippet in your own project you will have to change the conditions to match your own setup to determine when to override the PARALLEL_WORKERS.

In conclusion

Adding prerequisites to existing rake tasks is a handy little technique that makes it easy for others to work with your project without having to remember (when) to use custom rake tasks. To learn more about rake you can explore its documentation here.

If you have feedback on this article or have questions based on its contents then feel free to reach out to me on Twitter or through e-mail.