HOWTO monkeypatch Rake: overriding a Rake task
May 05 ’08
I know that some people really don't like monkeypatching, and I see why. But sometimes it's just unavoidable. Recently at work we came across a situation where the standard rake db:schema:dump task just wasn't working. So I started by writing the new version of the rake task that I wanted:
namespace :db do
namespace :schema do
desc "Create a db/schema.rb file"
task :dump => :environment do
require 'active_record/schema_dumper'
puts "Creating schema:"
File.open(ENV['SCHEMA'] || "db/schema.rb", "w") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
end
end
end
This task as shown does exactly what the default rake task does, except for the puts line. Let's just pretend that it's a radically different and wonderful version that solves world hunger and raises your children. Now, if you drop this into a rakefile in your app's lib/tasks directory, and then you run rake db:schema:dump --trace you'll notice that the schema.rb file gets created twice. What the???
Check the docs
So I started poking around the Rake docs. Rake::Task.define_task does the following:
Define a task given args and an option block. If a rule with the given name already exists, the prerequisites and actions are added to the existing task. Returns the defined task.
Aha! So instead of overriding the existing task (monkeypatching it), I'm adding to it. That's why the schema gets dumped twice. But what if I don't want that? What to do if I want to replace the rake task with my new version?
Check the internets
About an hour of googling later, I finally came across a post from Matthew Bass explaining the solution to my problem:
Rake::TaskManager.class_eval do
def remove_task(task_name)
@tasks.delete(task_name.to_s)
end
end
def remove_task(task_name)
Rake.application.remove_task(task_name)
end
# Override existing test task to prevent integrations
# from being run unless specifically asked for
remove_task 'db:schema:dump'
# Now define db:schema:dump again, the way we did above
And that was it. It's relatively straight-forward, but has a tendency to do strange things when rails gets updated. So you could abstract it into a plugin, with tests, but don't forget to test it whenever you gem update rails.
Alternative solution
While I was googling I also ran into a very interesting post on alias_task_chain, a method that patches Rake tasks to work like more typical methods with alias_method_chain. If you were clever and wanted to spend the time, you could get a solution to the problem using this method.
Start the conversation
* indicates a required field








