Sequel::Plugins::InlineMigrations::

Migrator class

Subclass of Sequel::Migrator that provides the logic for extracting and running migrations from the model classes themselves.

Constants

DEFAULT_OPTS

Default options for .run and initialize.

Attributes

baseclass R

The Class at the top of the hierarchy from which migrations will be fetched

column R

The name of the column which will contain the names of applied migrations as a Symbol.

dataset R

The migration table dataset (a Sequel::Dataset).

db R

The database to which the migrator will apply its migrations; a Sequel::Database.

table R

The name of the migration table as a Sequel::SQL::QualifiedIdentifier.

target R

The name of the target migration to play up or down to as a String.

Public Class Methods

new( baseclass, db=nil, opts={} )

Create a new Migrator that will organize migrations defined for baseclass or any of its subclasses for the specified db. See Sequel::Plugins::InlineMigrations::Migrator.run for argument details.

# File lib/sequel/plugins/inline_migrations.rb, line 308
def initialize( baseclass, db=nil, opts={} )
        if db.is_a?( Hash )
                opts = db
                db = nil
        end

        db ||= baseclass.db

        opts = DEFAULT_OPTS.merge( opts )
        schema, table = db.send( :schema_and_table, opts[:table] )

        @db        = db
        @baseclass = baseclass
        @table     = opts[ :table ]
        @column    = opts[ :column ]
        @target    = opts[ :target ]
        @dataset   = make_schema_dataset( @db, @table, @column )
end
run( baseclass, db=nil, opts={} )

Migrates the supplied db (a Sequel::Database) using the migrations declared in the given baseclass. The baseclass is the class to gather migrations from; it and all of its concrete descendents will be considered.

The options this method understands:

column

The column in the table that stores the migration version. Defaults to :version.

current

The current version of the database. If not given, it is retrieved from the database using the :table and :column options.

table

The name of the migrations table. Defaults to :schema_migrations.

target

The target version to migrate to. If not given, migrates to the maximum version.

Examples

# Assuming Acme::Model is a Sequel::Model subclass, and Acme::Vendor is a subclass
# of that...
Sequel::InlineMigrations::Migrator.run( Acme::Model )
Sequel::InlineMigrations::Migrator.run( Acme::Model, :target => 15, :current => 10 )
Sequel::InlineMigrations::Migrator.run( Acme::Vendor, :column => :app2_version)
Sequel::InlineMigrations::Migrator.run( Acme::Vendor, :column => :app2_version,
                                        :table => :schema_info2 )
# File lib/sequel/plugins/inline_migrations.rb, line 295
def self::run( baseclass, db=nil, opts={} )
        if db.is_a?( Hash )
                opts = db
                db = nil
        end

        new( baseclass, db, opts ).run
end

Public Instance Methods

all_migrating_model_classes()

Fetch an Array of all model classes which are descended from the migrating subclass, inclusive.

# File lib/sequel/plugins/inline_migrations.rb, line 406
def all_migrating_model_classes
        return [ self.baseclass ] + self.baseclass.descendents
end
all_migrations()

Returns any migration objects found in the migrating subclass or any of its descendents as an Array of Sequel::SimpleMigration objects, sorted by the migration name and the name of its migrating class.

# File lib/sequel/plugins/inline_migrations.rb, line 414
def all_migrations
        migrations = self.all_migrating_model_classes.
                collect( &:migrations ).
                compact.
                inject do |all, hash|
                        all.merge( hash ) do |key, old, new|
                                # rely on the fact that `up` is user defined even for a change block
                                fail "found duplicate `names` for migrations at #{old.up.source_location[0]} and #{new.up.source_location[0]}"
                        end
        end

        return migrations.values.sort_by {|m| [m.name, m.model_class.name] }
end
get_partitioned_migrations()

Returns two Arrays of migrations, the first one containing those which have already been applied, and the second containing migrations which are pending. Migrations that have been marked as applied but are (no longer) defined by a model class will be ignored.

# File lib/sequel/plugins/inline_migrations.rb, line 433
def get_partitioned_migrations

        # Get the list of applied migrations for the subclass and its descendents.
        migrating_class_names = self.all_migrating_model_classes.map( &:name ).compact
        applied_map = self.dataset.
                filter( :model_class => migrating_class_names ).
                select_hash( column, :model_class )

        # Split up the migrations by whether or not it exists in the map of applied migrations.
        # Each one is removed from the map, so it can be checked for consistency
        part_migrations = self.all_migrations.partition do |migration|
                applied_map.delete( migration.name )
        end

        # If there are any "applied" migrations left, it's likely been deleted since it was
        # applied, so just ignore it.
        unless applied_map.empty?
                applied_map.each do |migration, classname|
                        db.log_info "No %s migration defined in %s; ignoring it." %
                                [ migration, classname ]
                end
        end

        return part_migrations
end
run()

Apply all migrations to the database

# File lib/sequel/plugins/inline_migrations.rb, line 352
def run
        applied, pending = self.get_partitioned_migrations

        # If no target was specified, and there are no pending
        # migrations, return early.
        return if pending.empty? && self.target.nil?

        # If no target was specified, the last one is the target
        target     = self.target || pending.last.name
        migrations = nil
        direction  = nil

        if target == '0'
                direction = :down
                migrations = applied.reverse

        elsif tgtidx = pending.find_index {|m| m.name == target }
                migrations = pending[ 0..tgtidx ]
                direction = :up

        elsif tgtidx = applied.find_index {|m| m.name == target }
                migrations = applied[ tgtidx..-1 ].reverse
                direction = :down

        else
                raise Sequel::Error, "couldn't find migration %p"
        end

        # Run the selected migrations
        self.db.log_info "Migrating %d steps %s..." % [ migrations.length, direction ]
        migrations.each do |migration|
                start = Time.now
                self.db.log_info "Begin: %s, direction: %s" %
                        [ migration.description, direction ]

                self.db.transaction do
                        migration.apply( self.db, direction )

                        mclass = migration.model_class.name
                        if direction == :up
                                self.dataset.insert( self.column => migration.name, :model_class => mclass )
                        else
                                self.dataset.filter( self.column => migration.name, :model_class => mclass ).delete
                        end
                end

                self.db.log_info "  finished: %s, direction: %s (%0.6fs)" %
                        [ migration.description, direction, Time.now - start ]
        end
end