如何处理分布式Gem中的ActiveRecord迁移?

时间:2013-04-03 07:25:51

标签: activerecord gem rails-migrations

我正在尝试使用没有Rails的ActiveRecord将应用程序编写为gem。

我的问题是如何迁移已经没有rake等用户部署的数据库。我刚刚发布了一个schema.rb文件并从中创建了db。但现在我想允许用户更新到新的gem并迁移他们的数据库。

我看过ActiveRecord :: Migrator,但无法弄清楚如何使用它。

例如,我如何告诉ActiveRecord :: Migrator从current_migration中运行所有迁移?

任何人对如何进行此操作或提供良好参考有任何建议?

1 个答案:

答案 0 :(得分:1)

今天早上出现这种新鲜感之后,我想出了以下哪项效果很好:

module Byr
  module Db

    class << self
      attr_accessor :config
      attr_accessor :adapter
      attr_accessor :db_name
    end

    def self.create_sqlite(config)
      require 'sqlite3'
      config = config.merge('database' => File.join(Byr.db_dir, config['database']))
      ActiveRecord::Base.establish_connection(config)
    end

    def self.create_pg(config)
      require 'pg'
      # Connect to the postgres db to create the db
      ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres',
                                                           'schema_search_path' => 'public'))
      begin
        result = ActiveRecord::Base.connection.create_database(config['database'])
      rescue PG::Error, ActiveRecord::StatementInvalid => e
        unless e.message =~ /already exists/
          raise
        end
      end
      true
    end

    def self.mysql_creation_options(config)
      @charset   = ENV['CHARSET']   || 'utf8'
      @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
      {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
    end

    def self.create_mysql(config)
      require 'mysql2'
      error_class = config['adapter'] =~ /mysql2/ ? Mysql2::Error : Mysql::Error
      begin
        ActiveRecord::Base.establish_connection(config.merge('database' => nil))
        ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
        ActiveRecord::Base.establish_connection(config)
      rescue error_class => sqlerr
        access_denied_error = 1045
        if sqlerr.errno == access_denied_error
          print "#{sqlerr.error}. \nPlease provide the root password for your mysql installation\n>"
          root_password = $stdin.gets.strip
          grant_statement = "GRANT ALL PRIVILEGES ON #{config['database']}.* " \
          "TO '#{config['username']}'@'localhost' " \
          "IDENTIFIED BY '#{config['password']}' WITH GRANT OPTION;"
          ActiveRecord::Base.establish_connection(config.merge(
                                                               'database' => nil, 'username' => 'root', 'password' => root_password))
          ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
          ActiveRecord::Base.connection.execute grant_statement
          ActiveRecord::Base.establish_connection(config)
        else
          Byr.warn sqlerr.error
          Byr.warn "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation}"
          Byr.warn "(if you set the charset manually, make sure you have a matching collation)" if config['charset']
        end
      rescue ActiveRecord::StatementInvalid => e
        ActiveRecord::Base.establish_connection(config)
      end
    end

    def self.migrate
      sys_migration_dir = File.join(Byr.install_dir, "db/migrate")
      ActiveRecord::Migration.verbose = true
      ActiveRecord::Migrator.migrate(sys_migration_dir)
    end

    def self.connected?
      ActiveRecord::Base.connected? and
        ActiveRecord::Base.connection_config[:adapter] == adapter
    end

    def self.disconnect
      ActiveRecord::Base.connection_pool.disconnect!
    end

    # Really only for testing
    def self.drop_db
      ActiveRecord::Base.connection.drop_database(Byr.db_config)
    end
  end
end

然后,通过迁移来初始化db:

module Byr
  class << self
    attr_accessor :install_dir
    attr_accessor :db_dir
    attr_accessor :config_dir
    attr_accessor :config_file
    attr_accessor :config
    attr_accessor :db_config
    attr_accessor :adapter
    attr_accessor :database
  def self.create_db
    case db_config['adapter']
    when /postgresql/
      Byr::Db.create_pg(db_config)
    when /sqlite/
      Byr::Db.create_sqlite(db_config)
    when /mysql/
      Byr::Db.create_mysql(db_config)
    else
      raise ByrError "Your config.yml file specifies an unknown database adapter \'#{config['adapter']}\'"
    end
  end

  def self.connect_db(reconnect = false)
    unless reconnect
      return true if Byr.connected?
    end
    ActiveRecord::Base.establish_connection(db_config)
  end

  def self.migrate
    Byr::Db.migrate
  end

  def self.init(connect = true, adapter = nil)
    adapter = canonicalize_adapter(adapter) if adapter
    setup_db_config
    if connect
      create_db and connect_db and migrate
    end
  end
end

省略了一些不相关的代码部分,但我希望这会有所帮助 别人。

很多困难来自于铁轨中的佣金任务。