Rake Tasks and Rails
Rake can be used to run tasks in Ruby. Rake is short for "Ruby Make" and is a tool that helps automate steps related to building an environment or deploying applications.
If you're first introduced to Rake through Rails, then it's easy to get started. You can just start putting .*rake files into the lib/tasks folder. But as you work more with tasks, you might start to ask questions like:
- How does Rake know to look for tasks in the
lib/tasksfolder? - Why are the methods in different
*.rakefiles clobbering each other?
Here's a quick run-through of how to make simple Rake tasks and how they are incorporated in Rails.
Rake
Say that we have a Ruby project and there's a task that we want to perform regularly. Let's say we just need it to print this simple message:
puts 'Performing task.'
We can turn this into a Rake task if we install the rake gem and then create a Rakefile with the following contents:
# Rakefile
desc 'This task will print some output'
task :my_task do
puts 'Performing task.'
end
This command will show the tasks that are available to run:
rake --tasks
And this will run our new task:
rake my_task
Tasks can also have prerequisites, which are other tasks that must run first. So if I update the Rakefile to look like:
# Rakefile
task :prereq do
puts 'Performing prerequisite.'
end
desc 'This task will print some output'
task my_task: :prereq do
puts 'Performing task.'
end
Things to note:
- The new
prereqtask won't show in therake --taskslist because I didn't give it a description. - The
prereqtask will run first wheneverrake my_taskis called.
So now calling rake my_task will result in this output:
Performing prerequisite.
Performing task.
You can also give tasks a namespace to group related tasks together. For example:
namespace :my_namespace do
desc 'This task will print some output'
task :my_task do
puts 'Performing task.'
end
end
And then this task would be invoked with:
rake my_namespace:my_task
Rake Tasks in Rails
For Rake tasks in Rails, rather than register a task in the Rakefile, we can create *.rake files. We could define a simple task as:
# lib/tasks/my_task.rake
desc 'This task will print some output'
task :my_task do
puts 'Performing task.'
end
If your task will need to interact with your app code you'll also need to include the special :environment prerequisite:
# lib/tasks/my_task.rake
desc 'This task will print some output'
task my_task: :environment do
puts 'Performing task.'
end
How does Rake know to look for tasks in the lib/tasks folder? When you create a new app, Rails will use this template to create a Rakefile with these contents:
# Rakefile
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative "config/application"
Rails.application.load_tasks
The first line to require config/application loads all Rails modules (ActiveRecord, ActiveSupport, etc.) but it doesn't load your app code.
The second line to call Rails.application.load_tasks requires "rake" and loads all the files in the lib/tasks folder. The files are loaded within a context where the Rake::DSL is available.
Since tasks can be defined in different *.rake files and they can be grouped in different namespaces, you might be tempted to treat those namespaces as if they were distinct classes with distinct methods. For example, you might create a task file that includes a prepare helper method:
# lib/tasks/task1.rake
namespace :namespace1 do
def prepare
puts 'Task preparation #1.'
end
task :task1 do
prepare
puts 'Performing task #1.'
end
end
And then in a different task file you might define a different prepare method:
# lib/tasks/task2.rake
namespace :namespace2 do
def prepare
puts 'Task preparation #2.'
end
task :task2 do
prepare
puts 'Performing task #2.'
end
end
But then when you run rake namespace1:task1 the results are:
Task preparation #2.
Performing task #1.
Oops. It ran the preparation method from namespace2. This is because Rake namespaces affect task name resolution, but they don't do anything to alter the method scoping. So when Rails loaded all tasks in the lib/tasks folder, the prepare method defined in namespace2 overrode the prepare method that was originally defined in namespace1.
If you need to have complicated tasks with many methods, it's better to push that work out into a separate Ruby class that is called by the task.
Summary
How does Rake know to look for tasks in the lib/tasks folder?
Because Rails uses a Rakefile that calls Rails.application.load_tasks which loads tasks from that folder.
Why are the methods in different *.rake files clobbering each other?
Before a task runs, all Rake tasks are loaded within the same scope, so method names need to remain unique.