83

I have a Rails 2.2 project in which I want to override the functionality of the rake db:test:prepare task. I thought this would work, but it doesn't:

#lib/tasks/db.rake
namespace :db do
  namespace :test do
    desc "Overridden version of rails' standard db:test:prepare task since the schema dump used in that can't handle DB enums"  
    task :prepare => [:environment] do
      puts "doing db:structure:dump"
      Rake::Task['db:structure:dump'].invoke
      puts "doing db:test:clone_structure"
      Rake::Task['db:test:clone_structure'].invoke
    end   
  end
end

I get the standard task's behaviour. If I change the name of the task to :prepare2 and then do rake db:test:prepare2, then it works fine. The natural conclusion I draw from this is that my rake tasks are being defined before the built-in Rails ones, so mine is overridden by the standard :prepare task.

Can anyone see how I can fix this? I'd rather override it than have to use a new task. Thanks, max

theUtherSide
  • 3,338
  • 4
  • 36
  • 35
Max Williams
  • 32,435
  • 31
  • 130
  • 197

3 Answers3

140

If you define a rake task that already exists, its execution gets appended to the original task's execution; both tasks will be executed.

If you want to redefine a task you need to clear the original task first:

Rake::Task["db:test:prepare"].clear

It's also useful to note that once a task has been executed in rake, it won't execute again even if you call it again. This is by design but you can call .reset on a task to allow it to be run again.

Brendon Muir
  • 4,540
  • 2
  • 33
  • 55
  • Ah, looks like that also works and is a much nicer syntax. thanks! – Max Williams Nov 14 '11 at 09:51
  • 1
    No worries :) I think that might be the first time someone has accepted an answer of mine! :D – Brendon Muir Nov 14 '11 at 22:29
  • 12
    This can fail if the task in question doesn't exist (for example if you're running in a production vs development mode). To make it work even when the task doesn't exist, create an implicit empty task first: `task :default => []; Rake::Task[:default].clear` – Jo Liss Jul 11 '12 at 19:22
  • Rake::Task.clear Rake::Task['rake_task'].reenable Rake::Task['rake_task'].invoke Is this ok? – faisal bhatti Sep 24 '19 at 11:53
13

You have to remove the default task before adding your own:

Rake.application.instance_variable_get('@tasks').delete('db:test:prepare')
namespace 'db' do
  namespace 'test' do
    task 'prepare' do
      # ...
    end
  end
end

A fairly popular idiom is to create a convenience method called remove_task like so:

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

(Source: drnic/newgem)

Alex Peattie
  • 26,633
  • 5
  • 50
  • 52
  • 1
    Thanks Alex, the top one is fine for me and also more obvious as to what's going on, for the sake of anyone else who looks at it. Cheers – Max Williams Nov 13 '11 at 17:50
6

Create a new project.rake file at lib/tasks/, and paster below code into it.

namespace :mv do
desc "Display hint and info for your rails 4 project"
task info: :environment do
    puts 'Run rake test to test'
end
end

task(:default).clear.enhance ['mv:info']

inspired by Krasimir Angelov's blog

Eric Guo
  • 1,755
  • 17
  • 25