views:

1497

answers:

3

I have this little rake task:

namespace :db do 
  namespace :test do 
    task :reset do 
      ENV['RAILS_ENV'] = "test" 
      Rake::Task['db:drop'].invoke
      Rake::Task['db:create'].invoke
      Rake::Task['db:migrate'].invoke
    end
  end
end

Now, when I execute, it will ignore the RAILS_ENV I tried to hard-code. How do I make this task work as expected

+3  A: 

The best way of course is to specify the environment from the command line when you run the rake task, but if for some reason that's not what you want to do, you can do this:

ENV["RAILS_ENV"] = 'test'
RAILS_ENV.replace('test') if defined?(RAILS_ENV)

load "#{RAILS_ROOT}/config/environment.rb"

And that should do the trick.

ealdent
You may just be able to `require 'config/environment'` instead of reloading it.
ealdent
What a hack, works like a champ.
Sam Saffron
it seems to be working for the rake task without the require or load ...
Sam Saffron
Cool, even easier!
ealdent
+5  A: 

For this particular task, you only need to change the DB connection, so as Adam pointed out, you can do this:

namespace :db do 
  namespace :test do 
    task :reset do 
      ActiveRecord::Base.establish_connection('test')
      Rake::Task['db:drop'].invoke
      Rake::Task['db:create'].invoke
      Rake::Task['db:migrate'].invoke
      ActiveRecord::Base.establish_connection(ENV['RAILS_ENV'])  #Make sure you don't have side-effects!
    end
  end
end

If your task is more complicated, and you need other aspects of ENV, you are safest spawning a new rake process:

namespace :db do 
  namespace :test do 
    task :reset do 
      system("rake db:drop RAILS_ENV=test")
      system("rake db:create RAILS_ENV=test")
      system("rake db:migrate RAILS_ENV=test")
    end
  end
end

or

namespace :db do 
  namespace :test do 
    task :reset do 
      if (ENV['RAILS_ENV'] == "test")
        Rake::Task['db:drop'].invoke
        Rake::Task['db:create'].invoke
        Rake::Task['db:migrate'].invoke
      else
        system("rake db:test:reset RAILS_ENV=test")
      end
    end
  end
end
Michael Sofaer
yes this looks a bit less hacky than mucking around with RAILS_ENV
Sam Saffron
For me invoking extra Rake processes looks much *more* hacky.
Adam Byrtek
It's better than having your task mess with the environment in a destructive way. If you do it this way you can use it as a dependency in another task without causing a total catastrophe. Want to run a rake task in test mode? Run the task in test mode. Trying to fake test mode, and them probably change it all back to whatever mode you're actually in afterward is sketchy.
Michael Sofaer
I see your point. I was curious I've just checked how it's done in default Rails db:test tasks and looks like they don't redefine RAILS_ENV but change the database connection manually:ActiveRecord::Base.establish_connection(:test)
Adam Byrtek
+2  A: 

The cleanest and simplest solution would be to redefine RAILS_ENV (not ENV['RAILS_ENV'])

namespace :db do
  namespace :test do  
    task :reset do 
      RAILS_ENV = "test" 
      Rake::Task['db:drop'].invoke
      Rake::Task['db:create'].invoke
      Rake::Task['db:migrate'].invoke
    end
  end
end

During the boot process of a Rails application RAILS_ENV is initialized as follows

RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)

The rest of Rails code uses RAILS_ENV directly.

However, as Michael has pointed out in a comment to his answer, switching RAILS_ENV on the fly can be risky. Another approach would be to switch the database connection, this solution is in fact used by the default db:test tasks

ActiveRecord::Base.establish_connection(:test)
Adam Byrtek