'How do I force RAILS_ENV in a rake task?

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



Solution 1:[1]

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 

Solution 2:[2]

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

Solution 3:[3]

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)

Solution 4:[4]

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.

Solution 5:[5]

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

Solution 6:[6]

Rails itself uses system to restart process.

https://github.com/rails/rails/blob/8ec74553851bfafd1cc6944a1baad82c13fa76ff/railties/lib/rails/test_unit/runner.rb#L35

That means there is no way to override Rails.env after Rake tasks loaded.

This is my solution:

# rubocop:disable Rails/RakeEnvironment
task :force_test_env do
  unless Rails.env.test?
    tasks = Rake.application.top_level_tasks
    exec({'RAILS_ENV' => 'test'}, 'bin/rails', *tasks)
  end
end
task :test_env => [:force_test_env, :environment]

task :your_task_for_test => :test_env do
  YourTask.exec
end
task :your_another_task_for_test => :test_env do
  YourTask.exec :another
end

force_test_env restarts process. This task doesn't require environment. test_env task do the trick.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yacc
Solution 2 Kris
Solution 3
Solution 4 ealdent
Solution 5 kross
Solution 6 kuboon