class ActiveRecord::Migrator
:nodoc:
def current_migration
def current_migration migrations.detect { |m| m.version == current_version } end
def current_version
def current_version sm_table = schema_migrations_table_name if Base.connection.table_exists?(sm_table) get_all_versions.max || 0 else 0 end end
def current_version
def current_version migrated.max || 0 end
def ddl_transaction(migration)
def ddl_transaction(migration) if use_transaction?(migration) Base.transaction { yield } else yield end end
def down(migrations_paths, target_version = nil, &block)
def down(migrations_paths, target_version = nil, &block) migrations = migrations(migrations_paths) migrations.select! { |m| yield m } if block_given? self.new(:down, migrations, target_version).migrate end
def down?
def down? @direction == :down end
def execute_migration_in_transaction(migration, direction)
def execute_migration_in_transaction(migration, direction) ddl_transaction(migration) do migration.migrate(direction) record_version_state_after_migrating(migration.version) end end
def finish
def finish migrations.index(target) || migrations.size - 1 end
def forward(migrations_paths, steps=1)
def forward(migrations_paths, steps=1) move(:up, migrations_paths, steps) end
def get_all_versions
def get_all_versions SchemaMigration.all.map { |x| x.version.to_i }.sort end
def initialize(direction, migrations, target_version = nil)
def initialize(direction, migrations, target_version = nil) raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations? @direction = direction @target_version = target_version @migrated_versions = nil if Array(migrations).grep(String).empty? @migrations = migrations else ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations" @migrations = self.class.migrations(migrations) end validate(@migrations) ActiveRecord::SchemaMigration.create_table end
def last_migration #:nodoc:
def last_migration #:nodoc: migrations(migrations_paths).last || NullMigration.new end
def last_version
def last_version last_migration.version end
def migrate(migrations_paths, target_version = nil, &block)
def migrate(migrations_paths, target_version = nil, &block) case when target_version.nil? up(migrations_paths, target_version, &block) when current_version == 0 && target_version == 0 [] when current_version > target_version down(migrations_paths, target_version, &block) else up(migrations_paths, target_version, &block) end end
def migrate
def migrate if !target && @target_version && @target_version > 0 raise UnknownMigrationVersionError.new(@target_version) end running = runnable if block_given? message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator" ActiveSupport::Deprecation.warn message running.select! { |m| yield m } end running.each do |migration| Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger begin execute_migration_in_transaction(migration, @direction) rescue => e canceled_msg = use_transaction?(migration) ? "this and " : "" raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace end end end
def migrated
def migrated @migrated_versions ||= Set.new(self.class.get_all_versions) end
def migrations(paths)
def migrations(paths) paths = Array(paths) files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }] migrations = files.map do |file| version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first raise IllegalMigrationNameError.new(file) unless version version = version.to_i name = name.camelize MigrationProxy.new(name, version, file, scope) end migrations.sort_by(&:version) end
def migrations
def migrations down? ? @migrations.reverse : @migrations.sort_by(&:version) end
def migrations_path
def migrations_path migrations_paths.first end
def migrations_paths
def migrations_paths @migrations_paths ||= ['db/migrate'] # just to not break things if someone uses: migration_path = some_string Array(@migrations_paths) end
def move(direction, migrations_paths, steps)
def move(direction, migrations_paths, steps) migrator = self.new(direction, migrations(migrations_paths)) start_index = migrator.migrations.index(migrator.current_migration) if start_index finish = migrator.migrations[start_index + steps] version = finish ? finish.version : 0 send(direction, migrations_paths, version) end end
def needs_migration?
def needs_migration? current_version < last_version end
def open(migrations_paths)
def open(migrations_paths) self.new(:up, migrations(migrations_paths), nil) end
def pending_migrations
def pending_migrations already_migrated = migrated migrations.reject { |m| already_migrated.include?(m.version) } end
def proper_table_name(name)
def proper_table_name(name) # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string if name.respond_to? :table_name name.table_name else "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}" end end
def ran?(migration)
def ran?(migration) migrated.include?(migration.version.to_i) end
def record_version_state_after_migrating(version)
def record_version_state_after_migrating(version) if down? migrated.delete(version) ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all else migrated << version ActiveRecord::SchemaMigration.create!(:version => version.to_s) end end
def rollback(migrations_paths, steps=1)
def rollback(migrations_paths, steps=1) move(:down, migrations_paths, steps) end
def run(direction, migrations_paths, target_version)
def run(direction, migrations_paths, target_version) self.new(direction, migrations(migrations_paths), target_version).run end
def run
def run migration = migrations.detect { |m| m.version == @target_version } raise UnknownMigrationVersionError.new(@target_version) if migration.nil? unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i)) begin execute_migration_in_transaction(migration, @direction) rescue => e canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : "" raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace end end end
def runnable
def runnable runnable = migrations[start..finish] if up? runnable.reject { |m| ran?(m) } else # skip the last migration if we're headed down, but not ALL the way down runnable.pop if target runnable.find_all { |m| ran?(m) } end end
def schema_migrations_table_name
def schema_migrations_table_name SchemaMigration.table_name end
def start
def start up? ? 0 : (migrations.index(current) || 0) end
def target
def target migrations.detect { |m| m.version == @target_version } end
def up(migrations_paths, target_version = nil)
def up(migrations_paths, target_version = nil) migrations = migrations(migrations_paths) migrations.select! { |m| yield m } if block_given? self.new(:up, migrations, target_version).migrate end
def up?
def up? @direction == :up end
def use_transaction?(migration)
def use_transaction?(migration) !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions? end
def validate(migrations)
def validate(migrations) name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 } raise DuplicateMigrationNameError.new(name) if name version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 } raise DuplicateMigrationVersionError.new(version) if version end