Rake in depth

Tasks and prerequisites

A Rakefile is a list of named tasks, and a task can name the tasks that must run first. rake with no argument runs the task called default:

task default: :test desc "Run the tests" task test: :prepare do puts "running tests" end task :prepare do puts "preparing" end

rake

preparing running tests

The prerequisite runs once per invocation, even if two tasks name it. That is what makes rake ci possible: one task depends on lint, test and build, and each of those pulls its own setup.

rake -T

rake build # Build the report rake db:create # Create the database rake db:seed # Seed the database rake test # Run the tests

rake -T lists the tasks that have a desc. A task without one is a helper, and hiding it from the list is the point of the distinction.

Namespaces

A namespace prefixes the task names and keeps a project from growing a flat list. The prerequisite syntax inside a namespace resolves to the same namespace first:

namespace :db do desc "Create the database" task :create do puts "created" end desc "Seed the database" task seed: :create do puts "seeded" end end

rake db:seed

created seeded

task seed: :create inside db means db:create, not a top-level create. The full name is available from anywhere as db:seed, and a top-level task can depend on "db:seed" as a string.

File tasks

file is the task that knows about timestamps: it runs only when the target is missing or older than a prerequisite. That is make without the syntax:

file "build/report.txt" => ["data.csv"] do |t| mkdir_p "build" File.write(t.name, "rows: #{File.readlines("data.csv").size}\n") puts "wrote #{t.name}" end desc "Build the report" task build: "build/report.txt"

rake build

mkdir -p build wrote build/report.txt

Run it again without touching data.csv and there is no output at all: the target is newer than the source, so the task is up to date. A build system that rebuilds everything every time is a Rakefile without file tasks.

multitask

multitask declares that two prerequisites do not depend on each other, so rake -m may run them at the same time:

multitask lint: [:rubocop, :whitespace] do puts "lint done" end task :rubocop do puts "rubocop" end task :whitespace do puts "whitespace" end

rake lint

whitespace rubocop lint done

The prerequisite order is not guaranteed, which is the point: a task that needs a particular order is a task with prerequisites, not a multitask.

What to watch for

A task name with a colon is a namespace, a name with a slash is a file. Mixing them produces a task that runs once and then never again, or one that runs every time; read the name before debugging the body.

sh raises when the command fails; a plain backtick or system does not. A CI task that must stop on the first failure uses sh "bundle exec rake test".

Next: A real CLI with OptionParser

Article author: Arthur Isaev