Class: RubynCode::DB::Migrator

Inherits:
Object
  • Object
show all
Defined in:
lib/rubyn_code/db/migrator.rb

Overview

Reads migration files from db/migrations/, tracks applied versions in a schema_migrations table, and applies new migrations in order.

Supports two migration formats:

  • ‘.sql` files: executed statement-by-statement inside a transaction

  • ‘.rb` files: loaded and called via `ModuleName.up(connection)`

Constant Summary collapse

MIGRATIONS_DIR =

Returns absolute path to the migrations directory.

Returns:

  • (String)

    absolute path to the migrations directory

File.expand_path('../../../db/migrations', __dir__).freeze

Instance Method Summary collapse

Constructor Details

#initialize(connection) ⇒ Migrator

Returns a new instance of Migrator.

Parameters:

  • connection (Connection)

    the database connection to migrate



16
17
18
19
# File 'lib/rubyn_code/db/migrator.rb', line 16

def initialize(connection)
  @connection = connection
  ensure_schema_migrations_table
end

Instance Method Details

#applied_versionsSet<Integer>

Returns the set of already-applied migration versions.

Returns:

  • (Set<Integer>)


47
48
49
50
51
52
# File 'lib/rubyn_code/db/migrator.rb', line 47

def applied_versions
  rows = @connection.query(
    'SELECT version FROM schema_migrations ORDER BY version'
  ).to_a
  rows.to_set { |row| row['version'] }
end

#available_migrationsArray<Array(Integer, String)>

Lists all available migration files sorted by version.

Returns:

  • (Array<Array(Integer, String)>)

    pairs of [version, file_path]



67
68
69
70
71
72
73
# File 'lib/rubyn_code/db/migrator.rb', line 67

def available_migrations
  all = Dir.glob(File.join(MIGRATIONS_DIR, '*'))
           .select { |path| path.end_with?('.sql', '.rb') }
           .filter_map { |path| parse_migration_file(path) }

  deduplicate_migrations(all)
end

#current_versionInteger?

Returns the current schema version (highest applied migration).

Returns:

  • (Integer, nil)


57
58
59
60
61
62
# File 'lib/rubyn_code/db/migrator.rb', line 57

def current_version
  row = @connection.query(
    'SELECT MAX(version) AS max_version FROM schema_migrations'
  ).to_a.first
  row && row['max_version']
end

#deduplicate_migrations(all) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/rubyn_code/db/migrator.rb', line 75

def deduplicate_migrations(all)
  by_version = {}
  all.each do |version, path|
    by_version[version] = [version, path] if !by_version[version] || path.end_with?('.rb')
  end
  by_version.values.sort_by(&:first)
end

#migrate!Array<Integer>

Applies all pending migrations in version order.

Returns:

  • (Array<Integer>)

    list of newly applied migration versions



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rubyn_code/db/migrator.rb', line 24

def migrate!
  pending = pending_migrations
  return [] if pending.empty?

  applied = []
  pending.each do |version, path|
    apply_migration(version, path)
    applied << version
  end
  applied
end

#pending_migrationsArray<Array(Integer, String)>

Returns migration versions that have not yet been applied.

Returns:

  • (Array<Array(Integer, String)>)

    pairs of [version, file_path]



39
40
41
42
# File 'lib/rubyn_code/db/migrator.rb', line 39

def pending_migrations
  applied = applied_versions
  available_migrations.reject { |version, _| applied.include?(version) } # rubocop:disable Style/HashExcept
end