How does rails decide if migrations are pending?

Ever wondered how rails decides if any migration is pending? If not, this tutorial will explain how it is done using a table named schema_migrations.

Rails provides a configuration option to indicate if we want to be alerted if any migration is pending.

# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load

This will raise an error of the pending migrations and notify the user as shown in the image below.

rails pending migrations

This can be disabled with the false value for the option as shown below.

config.active_record.migration_error = false

Migration file name format

There is a convention for naming the migration files.

YYYYMMDDHHMMSS_name_of_migration.rb

It uses values from the current time for YYYYMMDDHHMMSS and the name of the migration.

Once migrations are run, the value YYYYMMDDHHMMSS in migration file, is inserted in a table named schema_migrations.

Pending migration check

Rails provides a method to check if any migration is pending. Listing relevant methods from ActiveRecord::MigrationContext class below.

# Listing only relevant methods for the illustration

class ActiveRecord
  class MigrationContext

  def check_pending!(connection = Base.connection)
    raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
  end
  

This piece of code raises an exception ActiveRecord::PendingMigrationError if any migration is pending.

In the next section, we will see the logic used for needs_migration? method.

  def needs_migration? # :nodoc:
    pending_migration_versions.size > 0
  end

  def pending_migration_versions # :nodoc:
    migrations.collect(&:version) - get_all_versions
  end

  def migrations # :nodoc:
    migrations = migration_files.map do |file|
      version, name, scope = parse_migration_filename(file)
      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 get_all_versions # :nodoc:
    if schema_migration.table_exists?
      schema_migration.all_versions.map(&:to_i)
    else
      []
    end
  end
end

Logic

  • migrations method returns versions (YYYYMMDDHHMMSS) of all the migration files available in the application.
  • get_all_versions method returns all the versions available in schema_migrations table
  • pending_migration_versions method returns the difference between the two arrays mentioned above.

If there is any file with a version that is not availble in the database needs migration

This is the basis of the logic followed for figuring out if any migration is pending. If you liked reading, please subscribe to the newsletter.

akshay

Akshay Mohite

Hi there! I am a Ruby on Rails & ReactJS Enthusiast, building some cool products at DTree Labs.

Read More
Buy me a coffee