Overview
Rake provides a domain-specific language for defining tasks and their dependencies in Ruby applications. Tasks execute in order based on dependency relationships, with Rake ensuring prerequisites run before dependent tasks. The system centers on the Rake::Task
class and uses a global task registry managed by Rake::Application
.
Rake tasks accept parameters, invoke other tasks programmatically, and define file dependencies for build automation. The task definition syntax uses blocks to contain executable code, with dependency declarations establishing execution order.
require 'rake'
# Define a simple task
task :hello do
puts "Hello, Rake!"
end
# Task with dependencies
task :compile => :clean do
puts "Compiling source files"
end
# Task with parameters
task :deploy, [:environment] do |t, args|
puts "Deploying to #{args[:environment]}"
end
Ruby applications typically define tasks in a Rakefile
located in the project root. Rake loads this file automatically when invoked from the command line, making tasks available for execution.
# Rakefile example
desc "Remove temporary files"
task :clean do
FileUtils.rm_rf('tmp')
end
desc "Run test suite"
task :test => :clean do
sh 'ruby -Ilib:test test/test_*.rb'
end
task :default => :test
Basic Usage
Task definitions use the task
method with a name symbol or hash for dependencies. The block contains executable Ruby code that runs when the task executes. Task names become available as command-line arguments to the rake
command.
task :setup do
Dir.mkdir('build') unless Dir.exist?('build')
puts "Created build directory"
end
task :build => :setup do
puts "Building application"
end
# Execute: rake build
Dependencies declare as symbols or arrays, with Rake resolving and executing prerequisites first. Multiple dependencies separate with commas or use array syntax for complex dependency graphs.
task :assets => [:javascript, :stylesheets, :images] do
puts "All assets processed"
end
task :javascript do
puts "Processing JavaScript files"
end
task :stylesheets do
puts "Processing CSS files"
end
task :images do
puts "Optimizing images"
end
Parameterized tasks accept arguments through the task definition and access them via the args
parameter. Arguments pass from the command line using bracket syntax.
task :backup, [:database, :destination] do |t, args|
db = args[:database] || 'production'
dest = args[:destination] || '/backups'
puts "Backing up #{db} database to #{dest}"
end
# Execute: rake backup[production,/tmp/backups]
File tasks create dependencies on filesystem timestamps, rebuilding targets only when source files change. This pattern supports efficient incremental builds for compiled assets or processed files.
file 'app.js' => ['src/main.js', 'src/utils.js'] do
sh 'cat src/main.js src/utils.js > app.js'
end
task :assets => 'app.js'
Advanced Usage
Task enhancement extends existing tasks with additional behavior while preserving original functionality. Multiple enhancements accumulate, with each block executing in definition order.
task :deploy do
puts "Basic deployment steps"
end
# Enhance the task with additional behavior
task :deploy do
puts "Additional deployment configuration"
end
# Both blocks execute when :deploy runs
Namespace organization groups related tasks under qualified names, preventing naming conflicts and providing logical structure for complex applications. Nested namespaces create hierarchical task organization.
namespace :db do
desc "Create database"
task :create do
puts "Creating database"
end
namespace :migrate do
task :up do
puts "Running migrations"
end
task :down do
puts "Rolling back migrations"
end
end
end
# Execute: rake db:create
# Execute: rake db:migrate:up
Task invocation within other tasks uses the invoke
method, executing the target task and its dependencies once per Rake session. Tasks invoked multiple times run only on the first invocation unless explicitly re-enabled.
task :prepare do
Rake::Task[:clean].invoke
Rake::Task[:setup].invoke
puts "Preparation complete"
end
task :deploy => :prepare do
puts "Deploying application"
Rake::Task[:notify].invoke
end
Dynamic task generation creates tasks programmatically based on configuration or discovered files. This pattern scales task definitions without manual enumeration.
# Generate tasks for each environment
%w[development staging production].each do |env|
namespace env.to_sym do
task :deploy do
puts "Deploying to #{env}"
deploy_to_environment(env)
end
task :rollback do
puts "Rolling back #{env}"
rollback_environment(env)
end
end
end
Rule-based task definitions create pattern-matching tasks for file transformations. Rules define how to create target files from source files based on filename patterns.
rule '.css' => '.scss' do |t|
sh "sass #{t.source} #{t.name}"
end
rule '.min.js' => '.js' do |t|
sh "uglifyjs #{t.source} -o #{t.name}"
end
# Automatically handles: rake styles/main.css
# If styles/main.scss exists
Multitasking executes independent tasks concurrently using threads. This optimization reduces execution time for I/O bound operations or parallel-safe processes.
multitask :parallel_build => [:javascript, :css, :images] do
puts "All assets built in parallel"
end
task :javascript do
sleep 2 # Simulate compilation time
puts "JavaScript compiled"
end
task :css do
sleep 1 # Simulate processing time
puts "CSS processed"
end
Error Handling & Debugging
Task failure propagation stops execution when tasks raise exceptions, preventing dependent tasks from running with invalid prerequisites. Rake preserves the original exception context for debugging.
task :validate_config do
config_file = 'config/deploy.yml'
unless File.exist?(config_file)
raise "Configuration file #{config_file} not found"
end
config = YAML.load_file(config_file)
required_keys = %w[server username deploy_path]
missing = required_keys - config.keys
unless missing.empty?
raise "Missing configuration keys: #{missing.join(', ')}"
end
end
task :deploy => :validate_config do
puts "Configuration valid, proceeding with deployment"
end
Exception handling within tasks catches and processes errors while maintaining task execution flow. Recovery strategies include retrying operations, logging failures, or setting default values.
task :download_assets do
urls = [
'https://cdn.example.com/lib.js',
'https://cdn.example.com/styles.css'
]
urls.each do |url|
begin
puts "Downloading #{url}"
# Simulate download operation
raise Net::HTTPError, "Connection timeout" if rand > 0.7
puts "Successfully downloaded #{File.basename(url)}"
rescue Net::HTTPError => e
puts "Failed to download #{url}: #{e.message}"
puts "Using local fallback version"
end
end
end
Task debugging utilizes the --trace
flag to display full execution paths and timing information. Custom debugging output provides visibility into task logic and variable states.
task :debug_build do
puts "Current working directory: #{Dir.pwd}"
puts "Environment: #{ENV['RAILS_ENV'] || 'development'}"
files = Dir.glob('src/**/*.js')
puts "Source files found: #{files.length}"
files.each { |file| puts " #{file}" }
if files.empty?
puts "WARNING: No source files found in src/ directory"
puts "Build may not produce expected results"
end
end
Conditional task execution prevents errors by checking prerequisites before attempting operations. Guards validate environment conditions, file existence, or system requirements.
task :deploy_production do
unless ENV['RAILS_ENV'] == 'production'
raise "This task must run in production environment"
end
unless system('which docker >/dev/null 2>&1')
raise "Docker is required but not installed"
end
config_valid = system('rake config:validate >/dev/null 2>&1')
unless config_valid
raise "Configuration validation failed"
end
puts "All checks passed, deploying to production"
end
Production Patterns
Environment-specific task configuration adapts behavior based on deployment targets. Tasks query environment variables or configuration files to modify execution paths and parameters.
namespace :deploy do
task :setup do
@config = {
'development' => {
server: 'localhost',
path: '/tmp/app',
restart_cmd: 'touch tmp/restart.txt'
},
'production' => {
server: 'prod.example.com',
path: '/var/www/app',
restart_cmd: 'sudo service app restart'
}
}
@env = ENV['RAILS_ENV'] || 'development'
@deploy_config = @config[@env]
unless @deploy_config
raise "Unknown environment: #{@env}"
end
end
task :code => :setup do
puts "Deploying code to #{@deploy_config[:server]}:#{@deploy_config[:path]}"
sh "rsync -av ./ #{@deploy_config[:server]}:#{@deploy_config[:path]}"
end
task :restart => :code do
puts "Restarting application"
sh @deploy_config[:restart_cmd]
end
end
Database migration tasks coordinate schema changes across environments with rollback capabilities. Tasks track migration versions and provide recovery mechanisms for failed updates.
namespace :db do
task :migrate do
require 'active_record'
ActiveRecord::Base.establish_connection(
adapter: 'sqlite3',
database: "db/#{ENV['RAILS_ENV'] || 'development'}.sqlite3"
)
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
begin
if version
ActiveRecord::Migrator.migrate('db/migrate', version)
puts "Migrated to version #{version}"
else
ActiveRecord::Migrator.migrate('db/migrate')
current_version = ActiveRecord::Migrator.current_version
puts "Migrated to version #{current_version}"
end
rescue ActiveRecord::IrreversibleMigration => e
puts "Migration failed: #{e.message}"
puts "Use VERSION parameter to specify target version"
exit 1
end
end
task :rollback do
ENV['VERSION'] = (ENV['VERSION'].to_i - 1).to_s
Rake::Task['db:migrate'].invoke
end
end
Asset pipeline integration processes and optimizes web assets during deployment. Tasks coordinate compilation, compression, and fingerprinting for efficient browser caching.
namespace :assets do
task :precompile => :environment do
require 'sass'
require 'uglifier'
# Process SCSS files
Dir.glob('app/assets/stylesheets/**/*.scss').each do |scss_file|
css_file = scss_file.gsub('.scss', '.css').gsub('app/assets/', 'public/assets/')
FileUtils.mkdir_p(File.dirname(css_file))
engine = Sass::Engine.for_file(scss_file, {})
css_content = engine.render
File.write(css_file, css_content)
puts "Compiled #{scss_file} -> #{css_file}"
end
# Process JavaScript files
uglifier = Uglifier.new
Dir.glob('app/assets/javascripts/**/*.js').each do |js_file|
next if js_file.end_with?('.min.js')
min_file = js_file.gsub('.js', '.min.js').gsub('app/assets/', 'public/assets/')
FileUtils.mkdir_p(File.dirname(min_file))
original = File.read(js_file)
minified = uglifier.compile(original)
File.write(min_file, minified)
puts "Minified #{js_file} -> #{min_file}"
end
end
task :clean do
FileUtils.rm_rf('public/assets')
puts "Cleaned compiled assets"
end
end
Health check tasks validate application state and dependencies before and after deployments. Automated verification reduces deployment risk and provides early failure detection.
namespace :health do
task :check do
checks = [
{ name: 'Database Connection', proc: -> { check_database } },
{ name: 'Redis Connection', proc: -> { check_redis } },
{ name: 'External API', proc: -> { check_external_api } },
{ name: 'Disk Space', proc: -> { check_disk_space } }
]
failed_checks = []
checks.each do |check|
print "Checking #{check[:name]}... "
begin
check[:proc].call
puts "✓"
rescue => e
puts "✗ (#{e.message})"
failed_checks << check[:name]
end
end
if failed_checks.any?
puts "\nFailed checks: #{failed_checks.join(', ')}"
exit 1
else
puts "\nAll health checks passed"
end
end
def check_database
ActiveRecord::Base.connection.execute('SELECT 1')
end
def check_redis
Redis.current.ping
end
def check_external_api
response = Net::HTTP.get_response(URI('https://api.example.com/health'))
raise "API returned #{response.code}" unless response.code == '200'
end
def check_disk_space
stat = File.statvfs('.')
available_mb = (stat.f_bavail * stat.f_bsize) / (1024 * 1024)
raise "Only #{available_mb}MB available" if available_mb < 100
end
end
Reference
Core Classes and Methods
Class/Method | Parameters | Returns | Description |
---|---|---|---|
Rake::Task.define_task(name, &block) |
name (Symbol/Hash), block (Proc) |
Rake::Task |
Creates new task with optional dependencies |
Rake::Task[] |
name (String/Symbol) |
Rake::Task |
Retrieves existing task by name |
Rake::Task#invoke(*args) |
args (Array) |
nil |
Executes task with arguments once per session |
Rake::Task#execute(args) |
args (Rake::TaskArguments) |
nil |
Runs task block without dependency checking |
Rake::Task#reenable |
None | self |
Allows task to be invoked again |
Rake::Task#prerequisites |
None | Array<String> |
Returns array of dependency task names |
Rake::FileTask.new(name, app) |
name (String), app (Rake::Application) |
Rake::FileTask |
Creates file-based task with timestamp checking |
Task Definition Methods
Method | Parameters | Returns | Description |
---|---|---|---|
task(args, &block) |
args (Symbol/Hash), block (Proc) |
Rake::Task |
Defines basic task with dependencies |
file(args, &block) |
args (String/Hash), block (Proc) |
Rake::FileTask |
Creates file task with source dependencies |
directory(dir) |
dir (String) |
Rake::FileTask |
Defines directory creation task |
rule(pattern => source, &block) |
Pattern mapping, block (Proc) | nil |
Creates pattern-based file transformation rule |
multitask(args, &block) |
args (Symbol/Hash), block (Proc) |
Rake::MultiTask |
Defines task with parallel prerequisite execution |
namespace(name, &block) |
name (Symbol), block (Proc) |
nil |
Groups tasks under qualified namespace |
desc(description) |
description (String) |
nil |
Sets description for next defined task |
Command Line Options
Option | Description |
---|---|
--dry-run, -n |
Display tasks without executing them |
--tasks, -T |
Show available tasks with descriptions |
--trace |
Enable full backtrace on errors |
--verbose, -v |
Enable verbose output |
--quiet, -q |
Suppress standard output |
--rakefile FILE, -f |
Use specified rakefile |
--directory DIR, -C |
Change to directory before running |
--no-system |
Ignore system-wide rakefiles |
--version |
Display Rake version |
Environment Variables
Variable | Description | Default |
---|---|---|
RAKE_SYSTEM |
System-wide rakefile location | /etc/rakefile |
RAKEFILE |
Default rakefile name | rakefile , Rakefile , rakefile.rb , Rakefile.rb |
RAKE_COLUMNS |
Terminal width for task formatting | Auto-detected |
Task Arguments and Parameters
# Task with named parameters
task :deploy, [:environment, :branch] do |t, args|
env = args[:environment] || 'staging'
branch = args[:branch] || 'main'
# Task implementation
end
# Access with defaults
task :backup, [:database] do |t, args|
args.with_defaults(database: 'production')
# args[:database] now guaranteed to have value
end
File Task Patterns
Pattern Type | Example | Usage |
---|---|---|
Single file dependency | file 'output.txt' => 'input.txt' |
Basic file transformation |
Multiple file sources | file 'app.js' => Dir.glob('src/*.js') |
Concatenation or compilation |
Extension rules | rule '.o' => '.c' |
Generic file type transformation |
Regex patterns | rule /\.html$/ => '.md' |
Pattern-based transformations |
Proc-based sources | rule '.css' => proc { |target| target.sub('.css', '.scss') } |
Dynamic source file detection |
Exception Classes
Exception | Raised When | Recovery Strategy |
---|---|---|
Rake::TaskNotFoundError |
Task name doesn't exist | Check task name spelling, verify namespace |
Rake::RuleRecursionError |
Rule creates circular dependency | Redesign rule patterns, check file dependencies |
Rake::TaskArgumentError |
Wrong number of task arguments | Verify task parameter definition and invocation |
Rake::CommandFailedError |
Shell command returns non-zero exit | Add error handling, check command availability |
Advanced Configuration
# Custom application instance
app = Rake::Application.new
Rake.application = app
# Task enhancement patterns
Rake::Task[:existing].enhance do
puts "Additional behavior"
end
# Conditional task loading
if File.exist?('config/database.yml')
load 'lib/tasks/db.rake'
end