ZetCode

Ruby Rake

last modified October 18, 2026

Rake is a build automation tool for Ruby, often described as "make" written in Ruby. Tasks are written in plain Ruby inside a file called Rakefile, and the rake command runs them. Rake handles task dependencies, file-based rebuilds, namespaces, and listing of documented tasks. This guide covers the core features of Rake with runnable examples.

Rakefile and Simple Tasks

A Rakefile lives in the project root. Each task is declared with task :name do ... end, and running rake name executes its block. The desc method attaches a description to the next task; rake -T lists all described tasks. Tasks without a desc are still runnable but are hidden from the list.

Rakefile
desc "Say hello"
task :hello do
  puts "Hello from Rake!"
end

desc "Print the current Ruby version"
task :version do
  puts "Ruby #{RUBY_VERSION}"
end

Each task is a named block of Ruby code. The desc lines provide the descriptions shown by rake -T, which prints the task names together with their descriptions.

$ rake hello
Hello from Rake!
$ rake version
Ruby 3.0.2
$ rake -T
rake hello    # Say hello
rake version  # Print the current Ruby version

Task Dependencies

Tasks can depend on other tasks. The syntax task build: [:compile] declares that build must run after compile. Rake runs each prerequisite once per invocation, in the order they are declared, before running the task itself. Multiple prerequisites are given as an array.

Rakefile
task :compile do
  puts "Compiling sources..."
end

task :test do
  puts "Running tests..."
end

task build: [:compile] do
  puts "Build complete"
end

task release: [:build, :test] do
  puts "Release ready"
end

Running release first resolves its prerequisites: build requires compile, and test is also required. Rake executes them in dependency order, so the output shows compilation before the build finishes and tests before the release step.

$ rake release
Compiling sources...
Build complete
Running tests...
Release ready

Tasks with Arguments

Task arguments are declared as a list of symbols after the task name: task :greet, [:name, :greeting] do |t, args|. Values are passed on the command line in square brackets, separated by commas. The with_defaults method supplies fallback values for arguments that were not provided. Quote the whole argument when using a shell such as bash, because brackets are special there.

Rakefile
desc "Greet a user"
task :greet, [:name, :greeting] do |t, args|
  args.with_defaults(greeting: "Hello")
  puts "#{args.greeting}, #{args.name}!"
end

The block receives the task object t and an args object. Each declared argument is accessible as a method on args. Without with_defaults, a missing argument would be nil.

$ rake "greet[Alice]"
Hello, Alice!
$ rake "greet[Bob,Hi]"
Hi, Bob!

File Tasks

A file task, declared with file "out" => ["in"] do ... end, produces a file from its prerequisites. Rake runs the block only when the target file does not exist or is older than one of its prerequisites. This makes repeated runs incremental: if nothing has changed, the task does nothing.

Rakefile
file "hello.o" => ["hello.c"] do |t|
  puts "cc -c #{t.prerequisites.first} -o #{t.name}"
  File.write(t.name, "object code\n")
end

Inside the block, t.name is the target file and t.prerequisites lists its inputs. The example writes a placeholder object file so the second run can detect that it is up to date. When hello.o already exists and is newer than hello.c, the second invocation prints nothing.

$ printf 'int main(void) { return 0; }\n' > hello.c
$ rake hello.o
cc -c hello.c -o hello.o
$ rake hello.o

Namespaces and the Default Task

The namespace block groups related tasks under a prefix, so task :migrate inside namespace :db is invoked as rake db:migrate. A task named default runs when rake is called without arguments. Prerequisites may refer to namespaced tasks by their full name, such as "db:migrate".

Rakefile
namespace :db do
  desc "Migrate the database"
  task :migrate do
    puts "Running migrations"
  end

  desc "Seed the database"
  task :seed => :migrate do
    puts "Seeding data"
  end
end

task default: "db:seed"

Because db:seed depends on db:migrate, running the default task executes the migration first and then the seed step. Inside the namespace, the short name :migrate resolves to db:migrate.

$ rake
Running migrations
Seeding data
$ rake -T
rake db:migrate  # Migrate the database
rake db:seed     # Seed the database

Rake::FileList

Rake::FileList is a collection of file paths built from glob patterns. It behaves like an array of strings but supports exclusion patterns and path manipulation methods such as ext for changing extensions. It is commonly used to build the lists of sources and objects for file tasks.

Rakefile
files = Rake::FileList["*.txt"]
desc "List text files"
task :list do
  puts files.to_a.inspect
end

The glob *.txt is expanded when the Rakefile is loaded. Calling to_a converts the list to a plain Ruby array before printing it. The output shown assumes the directory contains a.txt and b.txt; your list will differ if other text files are present.

$ rake list
["a.txt", "b.txt"]

Best Practices

Keep Rakefiles small by moving reusable logic into Ruby files under a lib/ directory and requiring them from the Rakefile. Always add a desc to tasks you want others to use, so they appear in rake -T. Prefer file tasks over plain tasks when output is a file, since Rake then skips work that is already up to date. Use namespaces to avoid name collisions in larger projects.

List all Ruby tutorials.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.