Module: Sequel::Migrator

Defined in:
lib/sequel/migration.rb

Overview

The Migrator module performs migrations based on migration files in a specified directory. The migration files should be named using the following pattern (in similar fashion to ActiveRecord migrations):

<version>_<title>.rb

For example, the following files are considered migration files:

001_create_sessions.rb
002_add_data_column.rb
...

The migration files should contain one or more migration classes based on Sequel::Migration.

To apply a migration, the #apply method must be invoked with the database instance, the directory of migration files and the target version. If no current version is supplied, it is read from the database. The migrator automatically creates a schema_info table in the database to keep track of the current migration version. If no migration version is stored in the database, the version is considered to be 0. If no target version is specified, the database is migrated to the latest version available in the migration directory.

For example, to migrate the database to the latest version:

Sequel::Migrator.apply(DB, '.')

To migrate the database from version 1 to version 5:

Sequel::Migrator.apply(DB, '.', 5, 1)

Constant Summary collapse

MIGRATION_FILE_PATTERN =
'[0-9][0-9][0-9]_*.rb'.freeze

Class Method Summary collapse

Class Method Details

.apply(db, directory, target = nil, current = nil) ⇒ Object

Migrates the supplied database in the specified directory from the current version to the target version. If no current version is supplied, it is extracted from a schema_info table. The schema_info table is automatically created and maintained by the apply function.

Raises:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/sequel/migration.rb', line 105

def self.apply(db, directory, target = nil, current = nil)
  # determine current and target version and direction
  current ||= get_current_migration_version(db)
  target ||= latest_migration_version(directory)
  raise Error, "No current version available" if current.nil?
  raise Error, "No target version available" if target.nil?

  direction = current < target ? :up : :down
  
  classes = migration_classes(directory, target, current, direction)
  
  db.transaction do
    classes.each {|c| c.apply(db, direction)}
    set_current_migration_version(db, target)
  end
  
  target
end

.get_current_migration_version(db) ⇒ Object

Gets the current migration version stored in the database. If no version number is stored, 0 is returned.



166
167
168
169
# File 'lib/sequel/migration.rb', line 166

def self.get_current_migration_version(db)
  r = schema_info_dataset(db).first
  r ? r[:version] : 0
end

.latest_migration_version(directory) ⇒ Object

Returns the latest version available in the specified directory.



159
160
161
162
# File 'lib/sequel/migration.rb', line 159

def self.latest_migration_version(directory)
  l = migration_files(directory).last
  l ? File.basename(l).to_i : nil
end

.migration_classes(directory, target, current, direction) ⇒ Object

Returns a list of migration classes filtered for the migration range and ordered according to the migration direction.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/sequel/migration.rb', line 126

def self.migration_classes(directory, target, current, direction)
  range = direction == :up ?
    (current + 1)..target : (target + 1)..current

  # Remove class definitions
  Migration.descendants.each do |c|
    Object.send(:remove_const, c.to_s) rescue nil
  end
  Migration.descendants.clear # remove any defined migration classes

  # load migration files
  migration_files(directory, range).each {|fn| load(fn)}
  
  # get migration classes
  classes = Migration.descendants
  classes.reverse! if direction == :down
  classes
end

.migration_files(directory, range = nil) ⇒ Object

Returns any found migration files in the supplied directory.



148
149
150
151
152
153
154
155
156
# File 'lib/sequel/migration.rb', line 148

def self.migration_files(directory, range = nil)
  pattern = File.join(directory, MIGRATION_FILE_PATTERN)
  files = Dir[pattern].inject([]) do |m, path|
    m[File.basename(path).to_i] = path
    m
  end
  filtered = range ? files[range] : files
  filtered ? filtered.compact : []
end

.schema_info_dataset(db) ⇒ Object

Returns the dataset for the schema_info table. If no such table exists, it is automatically created.



183
184
185
186
187
188
189
# File 'lib/sequel/migration.rb', line 183

def self.schema_info_dataset(db)
  unless db.table_exists?(:schema_info)
    db.create_table(:schema_info) {integer :version}
  end

  db[:schema_info]
end

.set_current_migration_version(db, version) ⇒ Object

Sets the current migration version stored in the database.



172
173
174
175
176
177
178
179
# File 'lib/sequel/migration.rb', line 172

def self.set_current_migration_version(db, version)
  dataset = schema_info_dataset(db)
  if dataset.first
    dataset.update(:version => version)
  else
    dataset << {:version => version}
  end
end