Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6799: parallel_migrations.diff

File parallel_migrations.diff, 8.5 kB (added by court3nay, 2 years ago)
  • test/migration_test.rb

    old new  
    33 
    44require 'fixtures/person' 
    55require 'fixtures/topic' 
     6require 'fixtures/monkey' 
     7require 'fixtures/task' 
    68require File.dirname(__FILE__) + '/fixtures/migrations/1_people_have_last_names' 
    79require File.dirname(__FILE__) + '/fixtures/migrations/2_we_need_reminders' 
    810require File.dirname(__FILE__) + '/fixtures/migrations_with_decimal/1_give_me_big_numbers' 
     
    3436      ActiveRecord::Base.connection.initialize_schema_information 
    3537      ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0" 
    3638 
    37       %w(reminders people_reminders prefix_reminders_suffix).each do |table| 
     39      %w(reminders people_reminders prefix_reminders_suffix monkeys tasks ).each do |table| 
    3840        Reminder.connection.drop_table(table) rescue nil 
    3941      end 
    4042      Reminder.reset_column_information 
     
    508510    def test_migrator 
    509511      assert !Person.column_methods_hash.include?(:last_name) 
    510512      assert !Reminder.table_exists? 
     513      assert !Task.table_exists? 
     514      assert !Monkey.table_exists? 
    511515 
    512516      ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/') 
    513517 
    514       assert_equal 3, ActiveRecord::Migrator.current_version 
     518      assert_equal 4, ActiveRecord::Migrator.current_version 
    515519      Person.reset_column_information 
    516520      assert Person.column_methods_hash.include?(:last_name) 
    517521      assert Reminder.create("content" => "hello world", "remind_at" => Time.now) 
     
    542546    end 
    543547 
    544548    def test_migrator_one_down 
    545       ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/') 
    546  
     549      assert !Person.column_methods_hash.include?(:last_name) 
     550       
     551      ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 4) 
     552       
     553      Person.reset_column_information 
     554      assert Person.column_methods_hash.include?(:last_name), Person.column_methods_hash.inspect 
     555      assert Reminder.table_exists? 
     556      assert Author.table_exists? 
     557       
    547558      ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 1) 
    548559 
    549560      Person.reset_column_information 
    550       assert Person.column_methods_hash.include?(:last_name) 
     561      assert Person.column_methods_hash.include?(:last_name), Person.column_methods_hash.inspect 
    551562      assert !Reminder.table_exists? 
     563      assert !Monkey.table_exists? 
     564      assert !Task.table_exists? 
    552565    end 
    553566 
    554567    def test_migrator_one_up_one_down 
  • test/fixtures/monkey.rb

    old new  
     1class Monkey < ActiveRecord::Base 
     2end 
  • test/fixtures/migrations/4_duplicate_one.rb

    old new  
     1class DuplicateOne < ActiveRecord::IndependentMigration 
     2  def self.up 
     3    create_table("tasks", :id => false) do |t| 
     4      t.column :person_id, :integer 
     5    end 
     6  end 
     7 
     8  def self.down 
     9    drop_table "tasks" 
     10  end 
     11end 
  • test/fixtures/migrations/4_duplicate_two.rb

    old new  
     1class DuplicateTwo < ActiveRecord::IndependentMigration 
     2  def self.up 
     3    create_table("monkeys", :id => false) do |t| 
     4      t.column :person_id, :integer 
     5    end 
     6  end 
     7 
     8  def self.down 
     9    drop_table "monkeys" 
     10  end 
     11end 
  • lib/active_record/migration.rb

    old new  
    33  end 
    44   
    55  class DuplicateMigrationVersionError < ActiveRecordError#:nodoc: 
    6     def initialize(version
    7       super("Multiple migrations have the version number #{version}") 
     6    def initialize(version, name
     7      super("Multiple migrations have the version number #{version} (bad file: #{name})") 
    88    end 
    99  end 
    1010   
     
    276276      end 
    277277    end 
    278278  end 
     279   
     280  # An IndependentMigration is used whenener you have two or more migrations occurring with the same number. 
     281  # This is useful if you are mirroring another project where you don't have control of the migration numbering, 
     282  # but want to modify your local database, or if you want to indicate that a succession of migrations  
     283  # are independent of each other, and can occur in parallel. 
     284  # 
     285  # Additionally, when merging multiple branches to trunk, setting conflicting migration classes to IndependentMigration 
     286  # will remove the DuplicateMigrationVersionError error that is normally raised, so you don't have to renumber.  
     287  # Note that all migrations sharing the same number should be IndependentMigration class. 
     288  # 
     289  # == Usage 
     290  # 
     291  # Use like a normal migration. 
     292  # 
     293  #   db/migrate 
     294  #     067_add_person_field.rb  # ActiveRecord::Migration 
     295  #     068_add_monkey_table.rb  # ActiveRecord::IndependentMigration 
     296  #     068_add_banana_table.rb  # ActiveRecord::IndependentMigration 
     297  #     069_remove_link_id.rb    # ActiveRecord::Migration 
     298  # 
     299  class IndependentMigration < Migration 
     300  end 
    279301 
    280302  class Migrator#:nodoc: 
    281303    class << self 
     
    326348    end 
    327349 
    328350    def migrate 
    329       migration_classes.each do |(version, migration_class)| 
    330         Base.logger.info("Reached target version: #{@target_version}") and break if reached_target_version?(version) 
    331         next if irrelevant_migration?(version) 
    332  
     351      @target_version ||= up? ? 999 : 0 # have to set a default version number 
     352      classes = migration_classes.select { |a| 
     353        (up?   && a[0] > current_version && a[0] <= @target_version) || 
     354        (down? && a[0] <= current_version && a[0] > @target_version) 
     355      } 
     356      # Migrating #{@direction} from #{current_version} to #{@target_version} :: #{classes.inspect} 
     357       
     358      classes.each do |(version, migration_class)| 
    333359        Base.logger.info "Migrating to #{migration_class} (#{version})" 
    334360        migration_class.migrate(@direction) 
    335361        set_schema_version(version) 
     
    341367        migrations = migration_files.inject([]) do |migrations, migration_file| 
    342368          load(migration_file) 
    343369          version, name = migration_version_and_name(migration_file) 
    344           assert_unique_migration_version(migrations, version.to_i
     370          assert_unique_migration_version(migrations, version.to_i, name
    345371          migrations << [ version.to_i, migration_class(name) ]  
    346372        end 
    347373 
    348         down? ? migrations.sort.reverse : migrations.sort 
     374        down? ? migrations.sort_by { |a| -a[0] } : migrations.sort_by {|a| a[0] } 
    349375      end 
    350376       
    351       def assert_unique_migration_version(migrations, version
     377      def assert_unique_migration_version(migrations, version, name
    352378        if !migrations.empty? && migrations.transpose.first.include?(version) 
    353           raise DuplicateMigrationVersionError.new(version) 
     379          conflicts = migrations.select { |m| m[0].to_i == version } << [version, migration_class(name)] 
     380          # Found multiple migrations at #{version}: #{conflicts.inspect} 
     381          dependents = conflicts.select { |m| m[1].superclass != ActiveRecord::IndependentMigration } 
     382          if dependents.any? 
     383            raise DuplicateMigrationVersionError.new(version, dependents.inspect) 
     384          else 
     385            # All clear, no dependent migrations. 
     386          end 
    354387        end 
    355388      end 
    356389       
     
    368401      def migration_version_and_name(migration_file) 
    369402        return *migration_file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first 
    370403      end 
    371        
     404     
    372405      def set_schema_version(version) 
    373406        Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{down? ? version.to_i - 1 : version.to_i}") 
    374407      end 
     
    380413      def down? 
    381414        @direction == :down 
    382415      end 
    383        
    384       def reached_target_version?(version) 
    385         return false if @target_version == nil 
    386         (up? && version.to_i - 1 >= @target_version) || (down? && version.to_i <= @target_version) 
    387       end 
    388        
    389       def irrelevant_migration?(version) 
    390         (up? && version.to_i <= current_version) || (down? && version.to_i > current_version) 
    391       end 
    392416  end 
    393417end