feat(orm): add migrations with schema definitions and migrator
This commit is contained in:
1 parent
b301c51b3c
commit
57860f167d
2 files changed
+120
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user