Ticket #11458: timestamped-migrations-3.diff
| File timestamped-migrations-3.diff, 15.4 kB (added by jbarnette, 2 months ago) |
|---|
-
a/activerecord/lib/active_record/base.rb
old new 431 431 # adapters for, e.g., your development and test environments. 432 432 cattr_accessor :schema_format , :instance_writer => false 433 433 @@schema_format = :ruby 434 434 435 435 class << self # Class methods 436 436 # Find operates with four different retrieval approaches: 437 437 # -
a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
old new 232 232 233 233 # Should not be called normally, but this operation is non-destructive. 234 234 # The migrations module handles this automatically. 235 def initialize_schema_information 235 def initialize_schema_information(current_version=0) 236 236 begin 237 execute "CREATE TABLE #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version #{type_to_sql(: integer)})"238 execute "INSERT INTO #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version) VALUES( 0)"237 execute "CREATE TABLE #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version #{type_to_sql(:string)})" 238 execute "INSERT INTO #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)} (version) VALUES(#{current_version})" 239 239 rescue ActiveRecord::StatementInvalid 240 # Schema has been initialized 240 # Schema has been initialized, make sure version is a string 241 version_column = columns(:schema_info).detect { |c| c.name == "version" } 242 243 # can't just alter the table, since SQLite can't deal 244 unless version_column.type == :string 245 version = ActiveRecord::Migrator.current_version 246 execute "DROP TABLE #{quote_table_name(ActiveRecord::Migrator.schema_info_table_name)}" 247 initialize_schema_information(version) 248 end 241 249 end 242 250 end 243 251 -
a/activerecord/lib/active_record/migration.rb
old new 8 8 end 9 9 end 10 10 11 class UnknownMigrationVersionError < ActiveRecordError #:nodoc: 12 def initialize(version) 13 super("No migration with version number #{version}") 14 end 15 end 16 11 17 class IllegalMigrationNameError < ActiveRecordError#:nodoc: 12 18 def initialize(name) 13 19 super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)") … … 308 314 class Migrator#:nodoc: 309 315 class << self 310 316 def migrate(migrations_path, target_version = nil) 311 Base.connection.initialize_schema_information312 313 317 case 314 318 when target_version.nil?, current_version < target_version 315 319 up(migrations_path, target_version) … … 319 323 return # You're on the right version 320 324 end 321 325 end 326 327 def rollback(migrations_path, steps=1) 328 migrator = self.new(:down, migrations_path) 329 start_index = migrator.migrations.index(migrator.current_migration) 330 331 return unless start_index 332 333 finish = migrator.migrations[start_index + steps] 334 down(migrations_path, finish ? finish.version : 0) 335 end 322 336 323 337 def up(migrations_path, target_version = nil) 324 338 self.new(:up, migrations_path, target_version).migrate … … 327 341 def down(migrations_path, target_version = nil) 328 342 self.new(:down, migrations_path, target_version).migrate 329 343 end 344 345 def run(direction, migrations_path, target_version) 346 self.new(direction, migrations_path, target_version).run 347 end 330 348 331 349 def schema_info_table_name 332 350 Base.table_name_prefix + "schema_info" + Base.table_name_suffix … … 344 362 345 363 def initialize(direction, migrations_path, target_version = nil) 346 364 raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations? 347 @direction, @migrations_path, @target_version = direction, migrations_path, target_version348 365 Base.connection.initialize_schema_information 366 @direction, @migrations_path, @target_version = direction, migrations_path, target_version 349 367 end 350 368 351 369 def current_version 352 370 self.class.current_version 353 371 end 372 373 def current_migration 374 migrations.detect { |m| m.version == current_version } 375 end 376 377 def run 378 target = migrations.detect { |m| m.version == @target_version } 379 raise UnknownMigrationVersionError.new(@target_version) if target.nil? 380 target.migrate(@direction) 381 end 354 382 355 383 def migrate 356 migration_classes.each do |migration_class| 357 if reached_target_version?(migration_class.version) 358 Base.logger.info("Reached target version: #{@target_version}") 359 break 360 end 361 362 next if irrelevant_migration?(migration_class.version) 384 current = migrations.detect { |m| m.version == current_version } 385 target = migrations.detect { |m| m.version == @target_version } 386 387 if target.nil? && !@target_version.nil? && @target_version > 0 388 raise UnknownMigrationVersionError.new(@target_version) 389 end 390 391 start = migrations.index(current) || 0 392 finish = migrations.index(target) || migrations.size - 1 393 runnable = migrations[start..finish] 394 395 # skip the current migration if we're heading upwards 396 runnable.shift if up? && runnable.first == current 397 398 # skip the last migration if we're headed down, but not ALL the way down 399 runnable.pop if down? && !target.nil? 400 401 runnable.each do |migration| 402 Base.logger.info "Migrating to #{migration} (#{migration.version})" 403 migration.migrate(@direction) 404 set_schema_version_after_migrating(migration) 405 end 406 end 363 407 364 Base.logger.info "Migrating to #{migration_class} (#{migration_class.version})" 365 migration_class.migrate(@direction) 366 set_schema_version(migration_class.version) 408 def migrations 409 @migrations ||= begin 410 files = Dir["#{@migrations_path}/[0-9]*_*.rb"] 411 412 migrations = files.inject([]) do |klasses, file| 413 version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first 414 415 raise IllegalMigrationNameError.new(f) unless version 416 version = version.to_i 417 418 if klasses.detect { |m| m.version == version } 419 raise DuplicateMigrationVersionError.new(version) 420 end 421 422 load(file) 423 424 klasses << returning(name.camelize.constantize) do |klass| 425 class << klass; attr_accessor :version end 426 klass.version = version 427 end 428 end 429 430 migrations = migrations.sort_by(&:version) 431 down? ? migrations.reverse : migrations 367 432 end 368 433 end 369 434 370 435 def pending_migrations 371 migration _classes.select { |m| m.version > current_version }436 migrations.select { |m| m.version > current_version } 372 437 end 373 438 374 439 private 375 def migration_classes 376 classes = migration_files.inject([]) do |migrations, migration_file| 377 load(migration_file) 378 version, name = migration_version_and_name(migration_file) 379 assert_unique_migration_version(migrations, version.to_i) 380 migrations << migration_class(name, version.to_i) 381 end.sort_by(&:version) 382 383 down? ? classes.reverse : classes 384 end 385 386 def assert_unique_migration_version(migrations, version) 387 if !migrations.empty? && migrations.find { |m| m.version == version } 388 raise DuplicateMigrationVersionError.new(version) 389 end 390 end 391 392 def migration_files 393 files = Dir["#{@migrations_path}/[0-9]*_*.rb"].sort_by do |f| 394 m = migration_version_and_name(f) 395 raise IllegalMigrationNameError.new(f) unless m 396 m.first.to_i 440 def set_schema_version_after_migrating(migration) 441 version = migration.version 442 443 if down? 444 after = migrations[migrations.index(migration) + 1] 445 version = after ? after.version : 0 397 446 end 398 down? ? files.reverse : files 399 end 400 401 def migration_class(migration_name, version) 402 klass = migration_name.camelize.constantize 403 class << klass; attr_accessor :version end 404 klass.version = version 405 klass 406 end 407 408 def migration_version_and_name(migration_file) 409 return *migration_file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first 410 end 411 412 def set_schema_version(version) 413 Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{down? ? version.to_i - 1 : version.to_i}") 447 448 Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{version}") 414 449 end 415 450 416 451 def up? … … 420 455 def down? 421 456 @direction == :down 422 457 end 423 424 def reached_target_version?(version)425 return false if @target_version == nil426 (up? && version.to_i - 1 >= @target_version) || (down? && version.to_i <= @target_version)427 end428 429 def irrelevant_migration?(version)430 (up? && version.to_i <= current_version) || (down? && version.to_i > current_version)431 end432 458 end 433 459 end -
a/activerecord/test/cases/migration_test.rb
old new 757 757 758 758 def test_migrator_one_down 759 759 ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid") 760 760 761 761 ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1) 762 762 763 763 Person.reset_column_information 764 764 assert Person.column_methods_hash.include?(:last_name) 765 765 assert !Reminder.table_exists? … … 805 805 assert Reminder.create("content" => "hello world", "remind_at" => Time.now) 806 806 assert_equal "hello world", Reminder.find(:first).content 807 807 end 808 809 def test_migrator_rollback 810 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") 811 assert_equal(3, ActiveRecord::Migrator.current_version) 812 813 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") 814 assert_equal(2, ActiveRecord::Migrator.current_version) 815 816 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") 817 assert_equal(1, ActiveRecord::Migrator.current_version) 818 819 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") 820 assert_equal(0, ActiveRecord::Migrator.current_version) 821 822 ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") 823 assert_equal(0, ActiveRecord::Migrator.current_version) 824 end 825 826 def test_migrator_run 827 assert_equal(0, ActiveRecord::Migrator.current_version) 828 ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 3) 829 assert_equal(0, ActiveRecord::Migrator.current_version) 830 831 assert_equal(0, ActiveRecord::Migrator.current_version) 832 ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 3) 833 assert_equal(0, ActiveRecord::Migrator.current_version) 834 end 808 835 809 836 def test_schema_info_table_name 810 837 ActiveRecord::Base.table_name_prefix = "prefix_" … … 892 919 end 893 920 894 921 def test_migrator_with_missing_version_numbers 895 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500) 896 assert !Person.column_methods_hash.include?(:middle_name) 897 assert_equal 4, ActiveRecord::Migrator.current_version 898 899 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 2) 900 Person.reset_column_information 901 assert !Reminder.table_exists? 902 assert Person.column_methods_hash.include?(:last_name) 903 assert_equal 2, ActiveRecord::Migrator.current_version 922 assert_raise(ActiveRecord::UnknownMigrationVersionError) do 923 ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500) 924 end 904 925 end 905 926 906 927 def test_create_table_with_custom_sequence_name -
a/railties/lib/rails_generator/commands.rb
old new 69 69 not existing_migrations(file_name).empty? 70 70 end 71 71 72 def current_migration_number73 Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path|74 n = File.basename(file_path).split('_', 2).first.to_i75 if n > max then n else max end76 end77 end78 79 def next_migration_number80 current_migration_number + 181 end82 83 72 def next_migration_string(padding = 3) 84 "%.#{padding}d" % next_migration_number73 Time.now.utc.strftime("%Y%m%d%H%M%S") 85 74 end 86 75 87 76 def gsub_file(relative_destination, regexp, *args, &block) -
a/railties/lib/tasks/databases.rake
old new 98 98 99 99 desc 'Resets your database using your migrations for the current environment' 100 100 task :reset => ["db:drop", "db:create", "db:migrate"] 101 102 desc 'Runs the "up" for a given migration VERSION.' 103 task :up => :environment do 104 version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil 105 raise "VERSION is required" unless version 106 ActiveRecord::Migrator.run(:up, "db/migrate/", version) 107 Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby 108 end 109 110 desc 'Runs the "down" for a given migration VERSION.' 111 task :down => :environment do 112 version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil 113 raise "VERSION is required" unless version 114 ActiveRecord::Migrator.run(:down, "db/migrate/", version) 115 Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby 116 end 101 117 end 102 118 103 119 desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n' 104 120 task :rollback => :environment do 105 121 step = ENV['STEP'] ? ENV['STEP'].to_i : 1 106 version = ActiveRecord::Migrator.current_version - step107 ActiveRecord::Migrator.migrate('db/migrate/', version)122 ActiveRecord::Migrator.rollback('db/migrate/', step) 123 Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby 108 124 end 109 125 110 126 desc 'Drops and recreates the database from db/schema.rb for the current environment.'