Tuesday, February 19, 2008

Rake: Task Overwriting

Update at bottom

By default Rake Tasks append behavior every time they are defined. The following example shows that both definitions are executed.

require 'rubygems'
require 'rake'

task :the_task do
p "one"
end

task :the_task do
p "two"
end

Rake::Task[:the_task].invoke
# >> "one"
# >> "two"

I like this behavior, but sometimes you want to overwrite a task instead of appending to what's already there.

When you no longer want the existing behavior the overwrite method can come in handy.

require 'rubygems'
require 'rake'

class Rake::Task
def overwrite(&block)
@actions.clear
enhance(&block)
end
end

task :the_task do
p "one"
end

Rake::Task[:the_task].overwrite do
p "two"
end


Rake::Task[:the_task].invoke
# >> "two"

The overwrite method is good, but sometimes you want to redefine the task using one of the specialized Rake tasks. I recently wanted to redefine the test task, so I created the abandon method to remove the existing definition.

require 'rubygems'
require 'rake'
require 'rake/testtask'

class Rake::Task
def abandon
@actions.clear
end
end

task :the_task do
p "one"
end

Rake::Task[:the_task].abandon

Rake::TestTask.new(:the_task) do |t|
t.libs << "test"
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

Rake::Task[:the_task].invoke
# >> Expectations ..................
# >> Finished in 0.00442 seconds
# >>
# >> Success: 18 fulfilled

Hopefully you wont need this type of thing very often, but it can be handy when you want to overwrite a task that has been previously by a framework you've included.

Update below...
Ola Bini pointed out that I'm not clearing the prerequisites. Clearing the prerequisites is something you can do without any modifications to rake.

Rake::Task[:task_name].prerequisites.clear

Also, if you want prerequisites cleared as part of overwrite or abandon, you can easily add it to both tasks.

class Rake::Task
def overwrite(&block)
@actions.clear
prerequisites.clear
enhance(&block)
end
def abandon
prerequisites.clear
@actions.clear
end
end

Labels:




Comments:
I've been thinking about adding an explicit API in rake to do something similar to your overwrite and abandon examples. The thing that makes me pause is that is doesn't play nice with plugins. Its one thing to clear a task as a project owner, since you are assuming responsibility for the entire project. However, as writer of a Rakefile module (such as the .rake files in a rails project), using abandon or overwrite clearly has the potential of overwriting someone else's overwrites. I've not come up with a good solution for that.
 
Hello Jim, thanks for the comment.

I think your concern is a valid one, but no different than the same threat that Open Classes pose. With great power...


I also think you might as well add overwrite and abandon to Rake since the cat's already out of the bag. RSpec wrote their own redefine_task method and after I finished with my blog post, I found someone else who had written their own overwrite method. It's basically public knowledge, so adding it to the framework is the next logic step, IMHO.

Cheers, Jay
 
Awesome post, to this day (more then 2 years of rails dev), I never understood why redefining a rake task didn't _redefine the rake task_. I guess I can see the benefit of appending the behavior. But It's not the first thing I would have thought, thanks!
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?