运行 在指定环境下以编程方式进行 Rake 任务

Run Rake task programmatically with specified environment

我在 Rails (3) 应用程序上用我的 Ruby 设置第二个数据库,所以我想创建一个 rake 任务来创建第二个开发数据库。我正在尝试覆盖 rake db:create 任务,以便它完成我需要的所有数据库创建。但是,我似乎找不到合适的方法来执行此任务。我尝试了几种方法 - 从 URL:

建立到数据库的连接
# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('@tasks').delete('db:create')

namespace :db do
  task :create do
    if Rails.env == "development"
      # database.yml contains an entry for secondary_development, this works, as confirmed from rails console
      ActiveRecord::Base.establish_connection "postgresql://localhost/secondary_development"       
      Rake::Task["db:create"].invoke # this does nothing
    end

    # invoke original db_create task - this works
    db_create.invoke
  end
end

另一种方法是:

# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('@tasks').delete('db:create')

namespace :db do
  task :create do
    if Rails.env == "development"
      Rails.env = "secondary_development"
      Rake::Task["db:create"].invoke
    end

    # invoke original db_create task - this doesn't work like this
    db_create.invoke
  end
end

这一次只有 secondary_development db:create 可以正常工作并且数据库已按需创建,但不再创建development 数据库使用这种方法。

根据我在其他地方找到的一个答案,我认为有必要重新启用该任务,但这并没有改变任何东西,而且似乎不是问题所在。

最后,一个行之有效的方法是:

# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('@tasks').delete('db:create')

namespace :db do
  task :create do
    if Rails.env == "development"
      system("rake db:create RAILS_ENV=secondary_development")
    end

    db_create.invoke
  end
end

这里唯一的问题是,因为 rake 任务是通过 system 执行的 运行,所以 Rails 应用程序必须在执行之前加载,所以我实际上是在加载完全应用两次只是为了 运行 任务 - 当我将测试数据库添加到组合中时,这将是 3 次。

因此,实际问题:

是否可以在指定环境中以编程方式运行 Rake::Task["..."]

为什么在创建数据库时ActiveRecord::Base.establish_connection不是这样工作的?当从 Rails 控制台 运行ning 时,我成功了。

我设法找到了解决办法。我相信原因是.invoke不会总是调用任务,但它会先判断是否有必要。鉴于 rake db:create 在同一任务中多次出现 运行,.invoke 认为后续调用是不必要的,因此不会 运行 它们。对于所需的行为,应改用 .execute

# remove db:create from the list of rake tasks in order to override it
db_create = Rake.application.instance_variable_get('@tasks').delete('db:create')

namespace :db do
  task :create do
    if Rails.env == "development"
      Rails.env = "secondary_development"
      Rake::Task["db:create"].execute # execute rather than invoke
    end

    # Reset the Rails env to 'development', otherwise it remains as 'secondary_development', which is not what we want (or move this above the if)
    Rails.env = "development"
    db_create.execute
  end
end