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