I have this little rake task:
namespace :db do
namespace :test do
task :reset do
ENV[\'RAILS_ENV\'] = \"test\"
Rake::Task[\'db:drop\']
There is some strange code in database_tasks.rb
:
def each_current_configuration(environment)
environments = [environment]
environments << 'test' if environment == 'development'
configurations = ActiveRecord::Base.configurations.values_at(*environments)
configurations.compact.each do |configuration|
yield configuration unless configuration['database'].blank?
end
end
It always adds test
if env is development
. I solved the case of wanting to do a custom db:rebuild
task for simultaneous development
and test
by running development
first, and test
second. In addition, before running the tasks, I call my set_env
method which makes sure to set ActiveRecord::Tasks::DatabaseTasks.env
, without this, the database connections don't seem to be handled discretely for environments as expected. I tried all other sorts of disconnect etc, but this worked without further code.
def set_env(env)
Rails.env = env.to_s
ENV['RAILS_ENV'] = env.to_s
ActiveRecord::Tasks::DatabaseTasks.env = env.to_s
end
Here is a gist of my full db.rake file with simultaneous multi-environment db:rebuild and db:truncate
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)
In Rails 3, you'll have to use
Rails.env = "test"
Rake::Task["db:drop"].invoke
instead of
RAILS_ENV = "test"
Rake::Task["db:drop"].invoke
Another option is to check the env and refuse to continue:
unless Rails.env.development?
puts "This task can only be run in development environment"
exit
end
or ask if they really want to continue:
unless Rails.env.development?
puts "You are using #{Rails.env} environment, are you sure? y/n"
continue = STDIN.gets.chomp
exit unless continue == 'y'
end
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
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.