How do I force RAILS_ENV in a rake task?

后端 未结 6 812
暗喜
暗喜 2020-11-28 06:00

I have this little rake task:

namespace :db do 
  namespace :test do 
    task :reset do 
      ENV[\'RAILS_ENV\'] = \"test\" 
      Rake::Task[\'db:drop\']         


        
相关标签:
6条回答
  • 2020-11-28 06:29

    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

    0 讨论(0)
  • 2020-11-28 06:34

    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)
    
    0 讨论(0)
  • 2020-11-28 06:47

    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 
    
    0 讨论(0)
  • 2020-11-28 06:51

    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
    
    0 讨论(0)
  • 2020-11-28 06:55

    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
    
    0 讨论(0)
  • 2020-11-28 06:55

    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.

    0 讨论(0)
提交回复
热议问题