Class: ActiveRecord::Migrator

Inherits:
Object
  • Object
show all
Defined in:
activerecord/lib/active_record/migration.rb

Overview

:nodoc:

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(direction, migrations_paths, target_version = nil) ⇒ Migrator

Returns a new instance of Migrator

Raises:

  • (StandardError)

661
662
663
664
665
# File 'activerecord/lib/active_record/migration.rb', line 661

def initialize(direction, migrations_paths, target_version = nil)
  raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
  Base.connection.initialize_schema_migrations_table
  @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
end

Class Attribute Details

.migrations_pathsObject


604
605
606
607
608
# File 'activerecord/lib/active_record/migration.rb', line 604

def migrations_paths
  @migrations_paths ||= ['db/migrate']
  # just to not break things if someone uses: migration_path = some_string
  Array.wrap(@migrations_paths)
end

Class Method Details

.current_versionObject


590
591
592
593
594
595
596
597
# File 'activerecord/lib/active_record/migration.rb', line 590

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

.down(migrations_paths, target_version = nil, &block) ⇒ Object


573
574
575
# File 'activerecord/lib/active_record/migration.rb', line 573

def down(migrations_paths, target_version = nil, &block)
  self.new(:down, migrations_paths, target_version).migrate(&block)
end

.forward(migrations_paths, steps = 1) ⇒ Object


565
566
567
# File 'activerecord/lib/active_record/migration.rb', line 565

def forward(migrations_paths, steps=1)
  move(:up, migrations_paths, steps)
end

.get_all_versionsObject


585
586
587
588
# File 'activerecord/lib/active_record/migration.rb', line 585

def get_all_versions
  table = Arel::Table.new(schema_migrations_table_name)
  Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
end

.migrate(migrations_paths, target_version = nil, &block) ⇒ Object


548
549
550
551
552
553
554
555
556
557
558
559
# File 'activerecord/lib/active_record/migration.rb', line 548

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

.migrations(paths, *args) ⇒ Object


614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
# File 'activerecord/lib/active_record/migration.rb', line 614

def migrations(paths, *args)
  if args.empty?
    subdirectories = true
  else
    subdirectories = args.first
    ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
  end

  paths = Array.wrap(paths)

  glob = subdirectories ? "**/" : ""
  files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]

  seen = Hash.new false

  migrations = files.map do |file|
    version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first

    raise IllegalMigrationNameError.new(file) unless version
    version = version.to_i
    name = name.camelize

    raise DuplicateMigrationVersionError.new(version) if seen[version]
    raise DuplicateMigrationNameError.new(name) if seen[name]

    seen[version] = seen[name] = true

    MigrationProxy.new(name, version, file, scope)
  end

  migrations.sort_by(&:version)
end

.migrations_pathObject


610
611
612
# File 'activerecord/lib/active_record/migration.rb', line 610

def migrations_path
  migrations_paths.first
end

.proper_table_name(name) ⇒ Object


599
600
601
602
# File 'activerecord/lib/active_record/migration.rb', line 599

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
  name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
end

.rollback(migrations_paths, steps = 1) ⇒ Object


561
562
563
# File 'activerecord/lib/active_record/migration.rb', line 561

def rollback(migrations_paths, steps=1)
  move(:down, migrations_paths, steps)
end

.run(direction, migrations_paths, target_version) ⇒ Object


577
578
579
# File 'activerecord/lib/active_record/migration.rb', line 577

def run(direction, migrations_paths, target_version)
  self.new(direction, migrations_paths, target_version).run
end

.schema_migrations_table_nameObject


581
582
583
# File 'activerecord/lib/active_record/migration.rb', line 581

def schema_migrations_table_name
  Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
end

.up(migrations_paths, target_version = nil, &block) ⇒ Object


569
570
571
# File 'activerecord/lib/active_record/migration.rb', line 569

def up(migrations_paths, target_version = nil, &block)
  self.new(:up, migrations_paths, target_version).migrate(&block)
end

Instance Method Details

#current_migrationObject


671
672
673
# File 'activerecord/lib/active_record/migration.rb', line 671

def current_migration
  migrations.detect { |m| m.version == current_version }
end

#current_versionObject


667
668
669
# File 'activerecord/lib/active_record/migration.rb', line 667

def current_version
  migrated.last || 0
end

#migrate(&block) ⇒ Object


684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
# File 'activerecord/lib/active_record/migration.rb', line 684

def migrate(&block)
  current = migrations.detect { |m| m.version == current_version }
  target = migrations.detect { |m| m.version == @target_version }

  if target.nil? && @target_version && @target_version > 0
    raise UnknownMigrationVersionError.new(@target_version)
  end

  start = up? ? 0 : (migrations.index(current) || 0)
  finish = migrations.index(target) || migrations.size - 1
  runnable = migrations[start..finish]

  # skip the last migration if we're headed down, but not ALL the way down
  runnable.pop if down? && target

  ran = []
  runnable.each do |migration|
    if block && !block.call(migration)
      next
    end

    Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger

    seen = migrated.include?(migration.version.to_i)

    # On our way up, we skip migrating the ones we've already migrated
    next if up? && seen

    # On our way down, we skip reverting the ones we've never migrated
    if down? && !seen
      migration.announce 'never migrated, skipping'; migration.write
      next
    end

    begin
      ddl_transaction do
        migration.migrate(@direction)
        record_version_state_after_migrating(migration.version)
      end
      ran << migration
    rescue => e
      canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
      raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
    end
  end
  ran
end

#migratedObject


744
745
746
# File 'activerecord/lib/active_record/migration.rb', line 744

def migrated
  @migrated_versions ||= self.class.get_all_versions
end

#migrationsObject


732
733
734
735
736
737
# File 'activerecord/lib/active_record/migration.rb', line 732

def migrations
  @migrations ||= begin
    migrations = self.class.migrations(@migrations_paths)
    down? ? migrations.reverse : migrations
  end
end

#pending_migrationsObject


739
740
741
742
# File 'activerecord/lib/active_record/migration.rb', line 739

def pending_migrations
  already_migrated = migrated
  migrations.reject { |m| already_migrated.include?(m.version.to_i) }
end

#runObject


675
676
677
678
679
680
681
682
# File 'activerecord/lib/active_record/migration.rb', line 675

def run
  target = migrations.detect { |m| m.version == @target_version }
  raise UnknownMigrationVersionError.new(@target_version) if target.nil?
  unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
    target.migrate(@direction)
    record_version_state_after_migrating(target.version)
  end
end