class Mimi::DB::Dictate::Migrator

Constants

DEFAULTS

Attributes

from_schema[R]
options[R]
table_name[R]
to_schema[R]

Public Class Methods

db_schema_definition(table_name) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 65
def self.db_schema_definition(table_name)
  db_schema_definitions[table_name] ||=
    Mimi::DB::Dictate::Explorer.discover_schema(table_name)
end
db_schema_definitions() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 70
def self.db_schema_definitions
  @db_schema_definitions ||= {}
end
new(table_name, options) click to toggle source

Creates a migrator to update table schema from DB state to defined state

@param table_name [String,Symbol] table name @param options [Hash]

# File lib/mimi/db/dictate/migrator.rb, line 24
def initialize(table_name, options)
  @table_name = table_name.to_sym
  @options = DEFAULTS.merge(options.dup)
  @from_schema = self.class.db_schema_definition(@table_name)
  @to_schema   = Mimi::DB::Dictate.schema_definitions[@table_name]
  if from_schema.nil? && to_schema.nil?
    raise "Failed to migrate '#{@table_name}', no DB or target schema found"
  end
end
reset_db_schema_definition!(table_name) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 74
def self.reset_db_schema_definition!(table_name)
  db_schema_definitions[table_name] = nil
end

Public Instance Methods

db_connection() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 38
def db_connection
  Mimi::DB.connection
end
destructive?(key) click to toggle source

Returns true if the Migrator is permitted to do destructive operations (DROP …) on resources identified by :key

# File lib/mimi/db/dictate/migrator.rb, line 51
def destructive?(key)
  options[:destructive] == true ||
    (options[:destructive].is_a?(Hash) && options[:destructive][key])
end
dry_run?() click to toggle source

Returns true if the Migrator is configured to do a dry run (no actual changes to DB)

# File lib/mimi/db/dictate/migrator.rb, line 44
def dry_run?
  options[:dry_run]
end
logger() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 34
def logger
  @logger ||= options[:logger] || ActiveRecord::Base.logger
end
run!() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 56
def run!
  db_ddl_transaction do
    run_drop_table! if from_schema && to_schema.nil?
    run_change_table! if from_schema && to_schema
    run_create_table! if from_schema.nil? && to_schema
  end
  self.class.reset_db_schema_definition!(table_name)
end

Private Instance Methods

add_column!(table_name, column) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 150
def add_column!(table_name, column)
  logger.info "-- add column: #{table_name}.#{column}"
  return if dry_run?
  db_connection.add_column(table_name, column.name, column.sequel_type, column.to_sequel_params)
end
add_index!(table_name, idx) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 166
def add_index!(table_name, idx)
  params = idx.params.select { |_, v| v }.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')
  idx_column_names = idx.columns.join(', ')
  logger.info "-- add index: #{idx.name} on #{table_name}(#{idx_column_names}), #{params}"
  return if dry_run?
  db_connection.add_index(table_name, idx.columns, idx.params)
end
change_column!(table_name, column) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 136
def change_column!(table_name, column)
  logger.info "-- change column: #{table_name}.#{column}"
  return if dry_run?
  db_connection.alter_table(table_name) do
    set_column_type column.name, column.sequel_type, column.to_sequel_params.except(:default, :null)
    set_column_default column.name, column.params[:default]
    if column.to_sequel_params[:null]
      set_column_allow_null column.name
    else
      set_column_not_null column.name
    end
  end
end
db_ddl_transaction() { || ... } click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 174
def db_ddl_transaction(&_block)
  supports_transactional_ddl =
    db_connection.respond_to?(:supports_transactional_ddl?) &&
    db_connection.supports_transactional_ddl?
  return yield unless supports_transactional_ddl
  db_connection.transaction { yield }
end
drop_column!(table_name, column_name) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 130
def drop_column!(table_name, column_name)
  logger.info "-- drop column: #{table_name}.#{column_name}"
  return if dry_run? || !destructive?(:columns)
  db_connection.drop_column(table_name, column_name)
end
drop_index!(table_name, idx) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 156
def drop_index!(table_name, idx)
  idx_column_names = idx.columns.join(', ')
  logger.info "-- drop index: #{idx.name} on #{table_name}(#{idx_column_names})"
  return if dry_run?
  drop_index_params = {}
  drop_index_params[:name] = idx.name if idx.name
  drop_index_params[:cascade] = true # TODO: always cascade?
  db_connection.drop_index(table_name, idx.columns, drop_index_params)
end
run_change_table!() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 86
def run_change_table!
  diff = Mimi::DB::Dictate::SchemaDiff.diff(from_schema, to_schema)
  if diff[:columns].empty? && diff[:indexes].empty?
    logger.info "- no changes: #{table_name}"
    return
  end
  logger.info "- ALTER TABLE: #{table_name}"
  run_change_table_columns!(diff[:columns]) unless diff[:columns].empty?
  run_change_table_indexes!(diff[:indexes]) unless diff[:indexes].empty?
end
run_change_table_columns!(diff_columns) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 97
def run_change_table_columns!(diff_columns)
  diff_columns.each do |c, diff|
    drop_column!(table_name, c) if diff[:from] && diff[:to].nil?
    change_column!(table_name, diff[:to]) if diff[:from] && diff[:to]
    add_column!(table_name, diff[:to]) if diff[:from].nil? && diff[:to]
  end
end
run_change_table_indexes!(diff_indexes) click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 105
def run_change_table_indexes!(diff_indexes)
  diff_indexes.each do |i, diff|
    drop_index!(table_name, diff[:from]) if diff[:from] && diff[:to].nil?
    add_index!(table_name, diff[:to]) if diff[:from].nil? && diff[:to]
  end
end
run_create_table!() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 112
def run_create_table!
  columns    = to_schema.columns.values
  column_pk  = to_schema.primary_key

  # issue CREATE TABLE with primary key field
  logger.info "- CREATE TABLE: #{table_name}"
  logger.info "-- add column: #{table_name}.#{column_pk}"
  unless dry_run?
    db_connection.create_table(table_name) do |_|
      column column_pk.name, column_pk.sequel_type, column_pk.to_sequel_params
    end
  end

  # create rest of the columns and indexes
  (columns - [column_pk]).each { |c| add_column!(table_name, c) }
  to_schema.indexes.each { |i| add_index!(table_name, i) }
end
run_drop_table!() click to toggle source
# File lib/mimi/db/dictate/migrator.rb, line 80
def run_drop_table!
  logger.info "- DROP TABLE: #{table_name}"
  return if dry_run? || !destructive?(:tables)
  db_connection.drop_table(table_name)
end