Motivation
Rails rake tasks are commands that automate specific actions to be used either by the developers or by other mechanisms (e.g.: a deploy that will invoke rake tasks). Many tasks come configured with Rails out of the box to provide important functionality. In this post, I will explain how to override tasks and change their steps to customize them for your own application.
Here are a few examples of out of the box tasks:
$ rake -T # Lists all rake tasks for the application
$ rake # Default rake task. On a fresh application, does the same as rake test
$ rake test # Task to run your tests (replaced by "rake spec" if using rspec instead of minitest)
$ rake db:seed # Populates the database using the seeds.rb file
However, tasks can quite easily be overridden to execute different actions. This is useful for defining steps for a build process that might include more than running the test suite or simply for changing the behavior of an existing task.
Customizing rake tasks to fit your needs can improve your productivity and help to guarantee that everyone in the project is using a standardized process (or at least that they have the tools to easily do so).
How to do it?
Overriding rake tasks is pretty straight forward. It involves two things: clearing the original task and re-defining it. Any code can go inside the new definition, however I recommend to neatly organize each step with their own rake task for simplicity.
How to create a new rake task
User defined rake tasks live inside the lib/tasks folder. Any file ending in ".rake" will be automatically picked up and loaded by Rails.
# lib/tasks/my_task.rake
namespace :custom do
desc "This take does something useful!"
task :do_it do
puts "Do something useful!"
end
end
Done! You can now invoke your new rake task using the command below. In the next section, we will go through on how to invoke it from other tasks.
$ rake custom:do_it
How to override an existing task
To override a task, clear the current behavior and re-define it in Rakefile. The example below will override the default rake to invoke our custom task.
# Rakefile
# frozen_string_literal: true
require_relative "config/application"
Rails.application.load_tasks
Rake::Task["default"].clear
task :default do
Rake::Task["custom:do_it"].invoke
end
And there you have it! The default rake task can be configured to do whatever your application needs.
In the next section, I will go through my usual set up for Rails applications.
My preferred configuration
The configuration I like to use overrides both the default and the test rake tasks, integrating them with a few tools. The steps for each are described below (check individual tool set up and configuration in their respective repos).
If any of these steps break, build process is stopped and fails.
Default
Steps run in the following order
- Brakeman - Code analysis for security
- Parallel tests - Runs tests in parallel
- Rubocop - Code quality analysis
- Rails best practices - Best practices for Rails analysis
- Reek - Code smells analysis
Test
This one simply replaces running tests to use parallel
- Parallel tests - Runs tests in parallel
Now that we went through the high level description, let's get to business. Here is the Rakefile with the overridden tasks and the individual task definitions themselves.
# Rakefile
# frozen_string_literal: true
require_relative "config/application"
require "rubocop/rake_task"
Rails.application.load_tasks
Rake::Task["default"].clear
Rake::Task["test"].clear
task :default do
Rake::Task["brakeman:check"].invoke
Rake::Task["parallel:test"].invoke
RuboCop::RakeTask.new(:rubocop)
Rake::Task["rubocop"].invoke
Rake::Task["rails_best_practices:run"].invoke
Rake::Task["reek:run"].invoke
end
task :test do
Rake::Task["parallel:test"].invoke
end
The brakeman rake task
# lib/tasks/brakeman.rake
# frozen_string_literal: true
namespace :brakeman do
desc "Check your code with Brakeman"
task :check do
require "brakeman"
result = Brakeman.run app_path: ".", print_report: true, pager: nil
exit Brakeman::Warnings_Found_Exit_Code unless result.filtered_warnings.empty?
end
end
The rails best practices rake task
# lib/tasks/rails_best_practices.rake
# frozen_string_literal: true
namespace :rails_best_practices do
desc "Run rails best practices"
task :run do
puts "Running rails best practices!"
puts `rails_best_practices`
end
end
The reek rake task
# lib/tasks/reek.rake
# frozen_string_literal: true
namespace :reek do
desc "Run reek"
task :run do
puts "Running reek!"
bundle exec "reek ."
end
end
The Gemfile
# Gemfile
group :development, :test do
gem "brakeman", require: false
gem "parallel_tests"
gem "rails_best_practices", require: false
gem "reek", require: false
gem "rubocop", require: false
end
Conclusion
Overridden rake tasks can boost productivity and help enforce good practices among members of a team. Take advantage of them!
What other tools would you integrate with rake tasks?
What other uses can this capability have?
Top comments (1)
Fabulous post