In the previous chapters, we explored some Ruby tools and concentrated on how those tools fit into the Ruby toolbox: how they interact with Ruby, where they live on your local filesystem, and how to identify issues that arise when using them. In this chapter, we will examine another tool, Rake. This time, we will concentrate on what it does for you and why you need it.
Rake is a Rubygem that automates many common functions required to build, test, package, and install programs; it is part of every modern Ruby installation, so you don't need to install it yourself.
Here are some common Rake tasks that you may encounter:
In short, you can write Rake tasks to automate anything you may want to do with your application during the development, testing, and release cycles.
Rake uses a file named Rakefile
that lives in your project directory; this file describes the tasks that Rake can perform for your project, and how to perform those tasks. For instance, here is a very simple Rakefile
:
desc 'Say hello'
task :hello do
puts "Hello there. This is the `hello` task."
end
desc 'Say goodbye'
task :bye do
puts 'Bye now!'
end
desc 'Do everything'
task :default => [:hello, :bye]
This Rakefile
contains three tasks: two that simply display a single message, and one task that has the other tasks as prerequisites or dependencies. The first two tasks are named :hello
and :bye
, while the final task is the default task; Rake runs the default task if you do not provide a specific task name when you invoke Rake.
Each of the above tasks calls two Rake methods: desc
and task
. The desc
method provides a short description that Rake displays when you run rake -T
(see below), and the task
method associates a name with either a block of Ruby code (lines 2-4 and 7-9) or a list of dependencies (line 12). Here, the :default
task depends on the :hello
and :bye
tasks: when you run the :default
task, Rake will run the :hello
and :bye
tasks first.
Let's see this in action. The first thing you should do with any Rakefile is find out what tasks it can run. You do this with the rake -T
command:
$ bundle exec rake -T
rake bye # Say goodbye
rake default # Do everything
rake hello # Say hello
This shows that there are three tasks defined by the Rakefile
: bye
, default
, and hello
. The output shows a short description of each task on the right: this information comes from the desc
method calls in Rakefile
.
Note that we used bundle exec rake -T
, not just rake -T
. We discussed bundle exec
in the previous chapter. Often, rake -T
will work just fine, but many prefer to use bundle exec
with rake
when possible (i.e., when your project uses Bundler); if you don't, you may see unusual messages like this:
$ rake -T
rake aborted!
Gem::LoadError: You have already activated rake 12.0.0, but your Gemfile requires rake 10.4.2. Prepending `bundle exec` to your command may solve this.
This shows that your Gemfile
requires a version of rake that differs from the version you get when you type rake
at the command line. bundle exec rake
makes sure that you use the correct version of rake
that your application depends on.
If you see:
Could not locate Gemfile or .bundle/ directory
when using bundle exec rake
, this means that your project isn't using Bundler. Here, you should omit bundle exec
and just run rake
instead.
One very important thing to notice is that Rakefile
is actually a Ruby program. You can put any Ruby code you want in a Rakefile
and run it as part of a task. Commands like desc
and task
are just method calls to parts of Rake; these method calls comprise a Domain Specific Language (DSL) for writing automated Rake tasks.
Once you know what Rake tasks you can run, you just have to run them:
$ bundle exec rake bye
Bye now!
$ bundle exec rake hello
Hello there. This is the `hello` task.
$ bundle exec rake default
Hello there. This is the `hello` task.
Bye now!
$ bundle exec rake # we don't need to specify 'default'
Hello there. This is the `hello` task.
Bye now!
One reason why you need Rake is that nearly every Ruby project you can find has a Rakefile
, and the presence of that file means you need to use Rake if you want to work on that project. However, just because everybody does something doesn't mean you need to use it too. If you start a brand-new project, you can always say "I don't feel like using Rake, so I won't."
While you can always opt-out of using Rake in your projects, there is little point to doing so. Every project that aims to produce a finished project that either you or other people intend to use in the future has repetitive tasks the developer needs. For instance, to release a new version of an existing program, you may want to:
Each step is easy enough to do manually, but you want to make sure you execute them in the proper order (for instance, you want to set the new version number before you commit your changes). You also don't want to be at the mercy of arbitrary typos that may do the wrong thing. It's far better to have a way to perform these tasks automatically with just one command, which is where Rake becomes extremely useful.
Your Rakefile
likely has each of these as a separate task, as well as a single overall task (call it release
, for instance) that steps through the tasks one at a time. The release task would stop only when it completes all the tasks or one task fails.
Let's look at a real world Rakefile
, the one used by the Pry Gem.
Note that Pry's Rakefile is a bit sensitive in terms of the environment, so, if you follow along, you may see different output than we show. Don't be alarmed or concerned about this right now.
If you want to follow along, go to the Pry repo and clone it to your local filesystem:
$ git clone https://github.com/pry/pry
See the Introduction to Git and GitHub book for more detail about git clone
.
Make the cloned directory your current directory:
$ cd pry
Before we look at the Rakefile
, let's run Bundler to make sure we have everything installed that needs to be installed. Run the following command from the pry
directory:
$ bundle install
This may take several minutes depending on how many and which Gems Bundler needs to install.
Once Bundler has installed everything, you can ask rake
to show you what tasks the Rakefile
defines:
$ bundle exec rake -T
rake clean # Remove any temporary products
rake clobber # Remove any generated files
rake default # Set up and run tests
rake gems # build all platform gems at once
rake profile # Profile pry's startup time
rake pry # Run pry (you can pass arguments using _ in ...
rake pushgems # build and push latest gems
rake reinstall # reinstall gem
rake rmgems # remove all platform gems
rake ruby:clobber_package # Remove package products
rake ruby:gem # Build the gem file pry-0.10.4.gem
rake ruby:package # Build all the packages
rake ruby:repackage # Force a rebuild of the package files
rake test # Run tests
rake version # Show pry version
This abridged listing eliminates several groups of commands for brevity. Note that the Rakefile may have changed since this was written, so the above listing may differ from what you see. In addition, some of the commands described below may have changed since this writing.
Let's say we want to run the tests that the developer uses to test Pry. As we scan the list of commands and their descriptions from above, we see that rake default
sets up and runs the tests, while rake test
just runs the tests. Given that this is the first time we're running the lists, let's run the default action first; there may be some setup that Rake needs to do:
$ bundle exec rake default
...output from tests is here...
Finished in 6.73 seconds (files took 0.61458 seconds to load)
1166 examples, 0 failures
Your results may be different at this point! You may even have seen some errors and the tests may not have run. As mentioned above, don't be concerned about this right now.
You can also run:
$ bundle exec rake
Rake invokes the "default" task when no task is provided on the command line.
So, how did Rake know what to do when you ran bundle exec rake default
? Let's look at Pry's Rakefile
(again, what you see here may differ from what you see with your copy of Pry). If you scan through this file, you will soon find these two lines (or something very similar):
desc "Set up and run tests"
task :default => [:test]
As before, desc
provides the description for the next task
call in the file: the :default
task. The array to the right of the =>
in the task
call identifies a list of dependencies for the named task. Here, the :default
task depends on the :test
task, so Rake also runs the :test
task.
Scanning down a little further, we find something like this:
desc "Run tests"
task :test do
paths =
if explicit_list = ENV['run']
explicit_list.split(',')
else
Dir['spec/**/*_spec.rb'].shuffle!
end
run_specs paths
end
This call to task
includes a block of Ruby code; the block tells Rake how to run the tests for Pry. Specifically, it first determines the paths of the test files it needs to run (these names come from the run
environment variable if present; otherwise, the task reads the names from the spec
directory). Once Rake has the path names, it runs the tests with the run_specs
method, which Rake provides.
If you haven't encountered specs yet, think of them as equivalent to Minitest tests. There are a variety of different testing frameworks for Ruby, and they sometimes use differing terminology. The specs term comes from a testing framework named RSpec, a very popular tool among Rubyists.
Let's look at the reinstall
task now. Before we try running it, find the :reinstall
task in the Rakefile
. It looks something like this:
desc "reinstall gem"
task :reinstall => :gems do
sh "gem uninstall pry" rescue nil
sh "gem install #{File.dirname(__FILE__)}/pkg/pry-#{Pry::VERSION}.gem"
end
Here, we see another call to desc
and task
. The :reinstall
task has a dependency named :gems
. Note that we don't use array syntax here; that means that :gems
is an array defined elsewhere in Rakefile
:
desc "build all platform gems at once"
task :gems => [:clean, :rmgems, 'ruby:gem', 'jruby:gem']
:gems
depends on four other tasks, each of which will be run by Rake. We won't worry much about what they do. Briefly, they perform some cleanup and setup processing required by Rubygems.
Once the :gems
task finishes, Rake returns to the :reinstall
task, and runs the block we passed to task
. This block uninstalls the existing version of pry
, then reinstalls it based on the current state of the local repo (your pry
directory).
If this sounds as though it might break something, don't worry. As it happens, this code installs the updated Pry package in the pkg
subdirectory of the current directory. This won't interfere with other programs that use Pry, but you can still use and inspect what got installed in the pkg
directory. So, feel free to go ahead and run:
$ bundle exec rake reinstall
$ ls -l pkg # this will show you the newly "installed" package.
Again, don't be alarmed if this command fails on your system.
As a Rubygem, it is subject to the same problems as other Gems. It is also subject to problems that occur with Ruby version managers. Thus, you can use many of the commands discussed in previous chapters to help diagnose issues with Rake.
The main issue you will have with Rake
is when to use bundle exec
: sometimes you need to use bundle exec
, sometimes you don't, and sometimes you can't use it. Generally, if your project uses Bundler (i.e., you have a Gemfile
), use bundle exec
; if your project doesn't use Bundler, you can omit bundle exec
and just use rake
.
In this chapter, we've taken a brief look at Rake and shown you how to use it to automate tasks while working on a Ruby project.