Faster rake tasks in Rails
Rake tasks are awesome for project specific scripts among other things, but in rails projects they suck for short tasks that don’t depend on rails. Here’s why:
- You type
rake my_task_that_doesnt_depend_on_the_rails_environment
- Rails loads (10-20 seconds)
- Only then does your superfast rake task run. Boring!
Looking for a solution I came across Xavier Shay’s attempt to deal with this.
His solution is to maintain a list of rails tasks which depend on Rails (like rake db:migrate
) in your Rakefile, and have the Rails environment autoload when any of those tasks are called. Any tasks in lib/tasks will now run nice and fast.
This helped, but it means you have to maintain a list of all the Rails tasks you may want to use (and any rake tasks included by gems used in your app) in your Gemfile, or remember to append LOAD_RAILS=1
to your rake call to force rails to load.
Because I can’t be bothered trying to remember which of my tasks depend on rails, I modified his Rakefile to autoload the Rails environment if Rake can’t find a task (on the assumption that it’ll be hidden in Rails/gem somewhere. This has the side-benefit of loading the :environment
task, that any tasks which require the Rails environment depend on, if and when needed.
If you have any thoughts I’d love to hear them, the gist is here.
#!/usr/bin/env rake | |
# 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. | |
#Load all the tasks in lib/tasks | |
Dir[File.expand_path('../lib/tasks/', __FILE__) + '/*.rake'].each do |file| | |
load file | |
end | |
#The original rails rakefile loading | |
def load_rails_environment | |
require File.expand_path('../config/application', __FILE__) | |
require 'rake' | |
MyApp::Application.load_tasks | |
end | |
#Catch task missing and try again | |
class Rake::Application | |
private | |
alias_method :invoke_task_without_catch, :invoke_task | |
def invoke_task_with_catch(*args) | |
begin | |
invoke_task_without_catch(*args) | |
rescue RuntimeError => e | |
begin | |
load_rails_environment | |
invoke_task_without_catch(*args) | |
rescue Exception => e | |
raise(e) | |
end | |
end | |
end | |
alias_method :invoke_task, :invoke_task_with_catch | |
end | |
#Add a task to the top of the rake -T list, to explain how to get the full list | |
#of tasks including rails and gem tasks | |
if ENV['LOAD_RAILS'] == '1' | |
load_rails_environment | |
else | |
desc "!!! Rails and gem tasks are not listed, rerun with LOAD_RAILS=1 to reveal" | |
task :_README | |
end |