Foliosus Web Design LLC: Your website done right

Blogfolio

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

    New comment

    Will never be disclosed or used inappropriately

    Enter the word how to confirm that you are a human being

    About the author

    Brent Miller is the owner and principal web designer of Foliosus Web Design LLC in Portland, Oregon. He enjoys food, plants, and the color green. If you are interested in hiring him for web work, please contact him.

    Categories

    Plant of the day

    Unknown sp. (Bromeliaceae)

    Unknown sp. (Bromeliaceae)

    Fresh photos