Last active
May 30, 2020 14:25
-
-
Save maximkrouk/10126931f43694cf0950025032362bdf to your computer and use it in GitHub Desktop.
DEPRECATED
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Fluent | |
private struct _Migration<T>: Migration { | |
var preparation: (Database) -> EventLoopFuture<Void> | |
var revertion: (Database) -> EventLoopFuture<Void> | |
func prepare(on database: Database) -> EventLoopFuture<Void> { preparation(database) } | |
func revert(on database: Database) -> EventLoopFuture<Void> { revertion(database) } | |
} | |
public protocol MigrationProvider { | |
static func prepare(on database: Database) -> EventLoopFuture<Void> | |
static func revert(on database: Database) -> EventLoopFuture<Void> | |
} | |
extension MigrationProvider { | |
public static var migration: Migration { | |
_Migration<Self>(preparation: prepare, revertion: revert) | |
} | |
} | |
public protocol DatabaseModel: Model, MigrationProvider { | |
typealias SchemaBuilder = SchemaBuilderProxy<Self> | |
static func prepareSchema(using schemaBuilder: SchemaBuilder) -> EventLoopFuture<Void> | |
static func revertSchema(using schemaBuilder: SchemaBuilder) -> EventLoopFuture<Void> | |
} | |
extension DatabaseModel { | |
public static func prepare(on database: Database) -> EventLoopFuture<Void> { | |
prepareSchema(using: database.schema(for: Self.self)) | |
} | |
public static func revert(on database: Database) -> EventLoopFuture<Void> { | |
revertSchema(using: database.schema(for: Self.self)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SchemaBuilder+Extension.swift | |
// App | |
// | |
// Created by Maxim Krouk on 3/17/20. | |
// | |
import Fluent | |
// MARK: - Pass by reference | |
@propertyWrapper | |
private final class Ref<Object> { | |
public var wrappedValue: Object | |
public convenience init(_ object: Object) { | |
self.init(wrappedValue: object) | |
} | |
public init(wrappedValue: Object) { | |
self.wrappedValue = wrappedValue | |
} | |
} | |
@dynamicMemberLookup | |
public struct SchemaBuilderProxy<T: Fluent.Model> { | |
@Ref private var wrappedValue: SchemaBuilder | |
/// Empty model, used to access fields by keypath | |
private let model: T = .init() | |
// MARK: Initialization | |
internal init(wrappedValue: SchemaBuilder) { | |
self._wrappedValue = .init(wrappedValue) | |
} | |
// MARK: DynamicMember | |
public subscript<T>(dynamicMember keyPath: KeyPath<SchemaBuilder, T>) -> T { | |
get { wrappedValue[keyPath: keyPath] } | |
} | |
public subscript<T>(dynamicMember keyPath: WritableKeyPath<SchemaBuilder, T>) -> T { | |
get { wrappedValue[keyPath: keyPath] } | |
set { wrappedValue[keyPath: keyPath] = newValue } | |
} | |
// MARK: - FieldRepresentableSupport | |
public func field<V: FieldRepresentable>( | |
_ keyPath: KeyPath<T, V>, | |
_ dataType: DatabaseSchema.DataType, | |
_ constraints: DatabaseSchema.FieldConstraint... | |
) -> Self { | |
self.field(.definition( | |
name: .string(model[keyPath: keyPath].field.key), | |
dataType: dataType, | |
constraints: constraints | |
)) | |
} | |
public func unique<V: FieldRepresentable>(on keyPaths: KeyPath<T, V>...) -> Self { | |
wrappedValue.schema.constraints.append(.unique( | |
fields: keyPaths.map { .string(model[keyPath: $0].field.key) } | |
)) | |
return self | |
} | |
public func foreignKey<V: FieldRepresentable>( | |
_ keyPath: KeyPath<T, V>, | |
references foreignSchema: String, | |
_ foreignField: String, | |
onDelete: DatabaseSchema.Constraint.ForeignKeyAction = .noAction, | |
onUpdate: DatabaseSchema.Constraint.ForeignKeyAction = .noAction | |
) -> Self { | |
self.foreignKey( | |
model[keyPath: keyPath].field.key, | |
references: foreignSchema, | |
foreignField, | |
onDelete: onDelete, | |
onUpdate: onUpdate | |
) | |
} | |
public func deleteField<V: FieldRepresentable>(_ keyPath: KeyPath<T, V>) -> Self { | |
deleteField(model[keyPath: keyPath].field.key) | |
} | |
} | |
// MARK: - SchemaBuilderProxy.SchemeBuilderSupport | |
extension SchemaBuilderProxy { | |
public func field( | |
_ name: String, | |
_ dataType: DatabaseSchema.DataType, | |
_ constraints: DatabaseSchema.FieldConstraint... | |
) -> Self { | |
field(.definition( | |
name: .string(name), | |
dataType: dataType, | |
constraints: constraints | |
)) | |
} | |
public func field(_ field: DatabaseSchema.FieldDefinition) -> Self { | |
wrappedValue.schema.createFields.append(field) | |
return self | |
} | |
public func unique(on fields: String...) -> Self { | |
wrappedValue.schema.constraints.append(.unique( | |
fields: fields.map { .string($0) } | |
)) | |
return self | |
} | |
public func foreignKey( | |
_ field: String, | |
references foreignSchema: String, | |
_ foreignField: String, | |
onDelete: DatabaseSchema.Constraint.ForeignKeyAction = .noAction, | |
onUpdate: DatabaseSchema.Constraint.ForeignKeyAction = .noAction | |
) -> Self { | |
wrappedValue.schema.constraints.append(.foreignKey( | |
fields: [.string(field)], | |
foreignSchema: foreignSchema, | |
foreignFields: [.string(foreignField)], | |
onDelete: onDelete, | |
onUpdate: onUpdate | |
)) | |
return self | |
} | |
public func deleteField(_ name: String) -> Self { | |
deleteField(.string(name)) | |
} | |
public func deleteField(_ name: DatabaseSchema.FieldName) -> Self { | |
wrappedValue.schema.deleteFields.append(name) | |
return self | |
} | |
public func delete() -> EventLoopFuture<Void> { | |
wrappedValue.delete() | |
} | |
public func update() -> EventLoopFuture<Void> { | |
wrappedValue.update() | |
} | |
public func create() -> EventLoopFuture<Void> { | |
wrappedValue.create() | |
} | |
} | |
// MARK: - SchemaBuilder.Binding | |
extension SchemaBuilder { | |
public func bind<T: Model>(to type: T.Type) -> SchemaBuilderProxy<T> { | |
.init(wrappedValue: self) | |
} | |
} | |
// MARK: - Model.Schema | |
extension Model { | |
static var schema: String { String(describing: self) } | |
} | |
// MARK: - Database.Schema | |
extension Database { | |
public func schema<T: Model>(for type: T.Type) -> SchemaBuilderProxy<T> { | |
schema(T.schema).bind(to: type) | |
} | |
} |
BTW migrations should not be tight to models, so the implementation may cause issues and now it's DEPRECATED
The issue with your implementation is the reason why models and migrations have been decoupled in the first place - as soon as you change a column name, or delete a column then it's going to blow up
© 0xTim
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
Back to index