feat(orm): add migrations with schema definitions and migrator

This commit is contained in:
dualfroz committed 2026-08-25 09:00:00 +02:00
1 parent b301c51b3c
commit 57860f167d
2 files changed
+120

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
module Prism
module Orm
class Migration
class TableDefinition
attr_reader :columns
def initialize
@columns = ['id']
end
def column(name, _type = :string)
@columns << name.to_s
end
%i[string text integer float boolean datetime].each do |type|
define_method(type) { |name| column(name, type) }
end
def timestamps
column(:created_at)
column(:updated_at)
end
end
def initialize(store)
@store = store
end
def up
raise NotImplementedError, "#{self.class} must implement #up"
end
def down
raise NotImplementedError, "#{self.class} must implement #down"
end
private
attr_reader :store
def create_table(name)
definition = TableDefinition.new
yield(definition) if block_given?
store.create_table(name, definition.columns)
end
def drop_table(name)
store.drop_table(name)
end
def add_column(table, column, _type = :string, default: nil)
store.add_column(table, column, default: default)
end
def remove_column(table, column)
store.remove_column(table, column)
end
end
end
end
+60
View File
@@ -0,0 +1,60 @@
module Prism
module Orm
class Migrator
Entry = Struct.new(:version, :name, :path)
def initialize(store, migrations_path)
@store = store
@migrations_path = migrations_path
end
def entries
Dir.glob(File.join(@migrations_path, '*.rb')).sort.map do |path|
version, name = File.basename(path, '.rb').split('_', 2)
Entry.new(version, name, path)
end
end
def pending
applied = @store.applied_migrations
entries.reject { |entry| applied.include?(entry.version) }
end
def migrate!
applied = pending.map { |entry| apply(entry) }
applied.map(&:version)
end
def rollback!(steps = 1)
versions = @store.applied_migrations.sort.last(steps).reverse
versions.map do |version|
entry = entries.find { |candidate| candidate.version == version }
next unless entry
revert(entry)
entry.version
end.compact
end
private
def apply(entry)
migration = load_migration(entry)
migration.up
@store.record_migration(entry.version)
entry
end
def revert(entry)
migration = load_migration(entry)
migration.down
@store.forget_migration(entry.version)
end
def load_migration(entry)
class_name = Inflector.camelize(entry.name)
load entry.path
Object.const_get(class_name).new(@store)
end
end
end
end