Skip to content

Commit

Permalink
Merge pull request #48 from qutheory/schema
Browse files Browse the repository at this point in the history
Schema
  • Loading branch information
tanner0101 committed Jun 21, 2016
2 parents 0bf3487 + 319012f commit 0a15ab2
Show file tree
Hide file tree
Showing 42 changed files with 1,133 additions and 337 deletions.
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
Packages
.build

Database

.DS_Store
*.xcodeproj

5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ let package = Package(
.Package(url: "https://github.com/open-swift/C7.git", majorVersion: 0, minor: 9),

// Syntax for easily accessing values from generic data.
.Package(url: "https://github.com/qutheory/polymorphic.git", majorVersion: 0, minor: 2)
.Package(url: "https://github.com/qutheory/polymorphic.git", majorVersion: 0, minor: 2),

// Syntax for easily indexing arrays and dictionaries.
.Package(url: "https://github.com/qutheory/path-indexable.git", majorVersion: 0, minor: 2)
]
)
3 changes: 0 additions & 3 deletions Sources/Fluent+C7.swift

This file was deleted.

1 change: 0 additions & 1 deletion Sources/Fluent+Polymorphic.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Foundation

/**
References a database with a single `Driver`.
Statically maps `Model`s to `Database`s.
Expand Down Expand Up @@ -29,4 +31,4 @@ public class Database {
The default database for all `Model` types.
*/
public static var `default`: Database = Database(driver: PrintDriver())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,12 @@ public protocol Driver {
returns an array of results fetched,
created, or updated by the action.
*/
func execute<T: Model>(_ query: Query<T>) throws -> [[String: Value]]
@discardableResult
func query<T: Model>(_ query: Query<T>) throws -> [[String: Value]]

/**
Creates the `Schema` indicated
by the `Builder`.
*/
func schema(_ schema: Schema) throws
}
28 changes: 28 additions & 0 deletions Sources/Fluent/Database/PrintDriver.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
A dummy `Driver` useful for developing.
*/
public class PrintDriver: Driver {
public var idKey: String = "foo"

public func query<T: Model>(_ query: Query<T>) throws -> [[String : Value]] {

let sql = SQL(query: query)
let serializer = GeneralSQLSerializer(sql: sql)

let (statement, values) = serializer.serialize()
print("[Print driver]")

print("Statement: \(statement) Values: \(values)")

print("Table \(query.entity)")
print("Action \(query.action)")
print("Filters \(query.filters)")
print()

return []
}

public func schema(_ schema: Schema) throws {
//let sql = SQL(builder: builder)
}
}
24 changes: 18 additions & 6 deletions Sources/Model.swift → Sources/Fluent/Model/Model.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Foundation

/**
Represents an entity that can be
stored and retrieved from the `Database`.
*/
public protocol Model: CustomStringConvertible {
public protocol Model: CustomStringConvertible, Preparation {
/**
The `Database` this model will use.
It can be changed at any point.
Expand Down Expand Up @@ -35,10 +37,10 @@ public protocol Model: CustomStringConvertible {
func serialize() -> [String: Value?]

/**
Attempts to initialize an entity
Initializes an entity
from the database representation.
*/
init?(serialized: [String: Value])
init(serialized: [String: Value])
}

//MARK: Defaults
Expand Down Expand Up @@ -92,14 +94,24 @@ extension Model {
public static var query: Query<Self> {
return Query()
}

/**
Creates a `Query` with a first filter.
*/
@discardableResult
public static func filter(_ field: String, _ comparison: Filter.Comparison, _ value: Value) -> Query<Self> {
return query.filter(field, comparison, value)
}

@discardableResult
public static func filter(_ field: String, _ value: Value) -> Query<Self> {
return filter(field, .equals, value)
}
}

//MARK: Database

extension Model {
/**
Used to identify this `Model`.
*/
private static var name: String {
return "\(self)"
}
Expand Down
68 changes: 68 additions & 0 deletions Sources/Fluent/Model/Value.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
A type of data that can be retrieved
or stored in a database.
*/
public protocol Value: CustomStringConvertible, StructuredDataRepresentable, Polymorphic {}

extension Int: Value {
public var structuredData: StructuredData {
return .int(self)
}
}

extension String: Value {
public var structuredData: StructuredData {
return .string(self)
}

public var description: String {
return self
}
}

extension Double: Value {
public var structuredData: StructuredData {
return .double(self)
}
}

extension Float: Value {
public var structuredData: StructuredData {
return .double(Double(self))
}
}

extension StructuredData: Fluent.Value {
public var structuredData: StructuredData {
return self
}
}

extension Bool: Value {
public var structuredData: StructuredData {
return .bool(self)
}
}

extension StructuredData: CustomStringConvertible {
public var description: String {
switch self {
case .array(let array):
return array.description
case .bool(let bool):
return bool.description
case .data(let data):
return data.description
case .dictionary(let dict):
return dict.description
case .double(let double):
return double.description
case .int(let int):
return int.description
case .null:
return "NULL"
case .string(let string):
return string
}
}
}
51 changes: 51 additions & 0 deletions Sources/Fluent/Preparation/Database+Preparation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
extension Database {
public func prepare(_ preparations: [Preparation.Type]) throws {
for preparation in preparations {
try prepare(preparation)
}
}

public func hasPrepared(_ preparation: Preparation.Type) throws -> Bool {
Migration.database = self

do {
// check to see if this preparation has already run
if let _ = try Migration.filter("name", preparation.name).first() {
return true
}
} catch {
// could not fetch migrations
// try to create `.fluent` table
try Migration.prepare(database: self)
}

return false
}

public func prepare(_ preparation: Preparation.Type) throws {
Migration.database = self

// set the current database on involved Models
if let model = preparation as? Model.Type {
model.database = self
}


if try hasPrepared(preparation) {
throw PreparationError.alreadyPrepared
}

try preparation.prepare(database: self)

// record that this preparation has run
var migration = Migration(name: preparation.name)
try migration.save()
}
}

extension Preparation {
public static var name: String {
let type = "\(self.dynamicType)"
return type.components(separatedBy: ".Type").first ?? type
}
}
15 changes: 15 additions & 0 deletions Sources/Fluent/Preparation/Migration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
final class Migration: Model {
static var entity = "fluent"

var id: Value?
var name: String

init(name: String) {
self.name = name
}

init(serialized: [String: Value]) {
id = serialized["id"]
name = serialized["name"]?.string ?? ""
}
}
79 changes: 79 additions & 0 deletions Sources/Fluent/Preparation/Model+Preparation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// MARK: Preparation

extension Model {
/**
Automates the preparation of a model.
*/
public static func prepare(database: Database) throws {
try database.create(entity) { builder in
let model = self.init()
let mirror = Mirror(reflecting: model)

for property in mirror.children {
guard let name = property.label else {
throw PreparationError.automationFailed("Unable to unwrap property name.")
}
let type = "\(property.value.dynamicType)"

if name == database.driver.idKey {
builder.id()
} else {
if type.contains("String") {
builder.string(name)
} else if type.contains("Int") {
builder.int(name)
} else if type.contains("Double") || type.contains("Float") {
builder.double(name)
} else {
throw PreparationError.automationFailed("Unable to prepare property '\(name): \(type)', only String, Int, Double, and Float are supported.")
}
}
}
}
}

/**
Automates reverting the model's preparation.
*/
public static func revert(database: Database) throws {
try database.delete(entity)
}
}

// MARK: Automation

extension Model {
private init() {
self.init(serialized: [:])
}

/**
Automates the serialization of a model.
*/
public func serialize() -> [String: Value?] {
var serialized: [String: Value?] = [:]

let mirror = Mirror(reflecting: self)
for property in mirror.children {
let name = property.label ?? ""
let type = "\(property.value.dynamicType)"

if let id = id {
serialized["id"] = id.int ?? id.string
}

if type.contains("String") {
if let string = property.value as? String {
serialized[name] = string
}
} else if type.contains("Int") {
if let int = property.value as? Int {
serialized[name] = int
}
}

}

return serialized
}
}
20 changes: 20 additions & 0 deletions Sources/Fluent/Preparation/Preparation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
A preparation prepares the database for
any task that it may need to perform during runtime.
*/
public protocol Preparation {
/**
The prepare method should call any methods
it needs on the database to prepare.
*/
static func prepare(database: Database) throws

/**
The revert method should undo any actions
caused by the prepare method.
If this is impossible, the `PreparationError.revertImpossible`
error should be thrown.
*/
static func revert(database: Database) throws
}
5 changes: 5 additions & 0 deletions Sources/Fluent/Preparation/PreparationError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public enum PreparationError: ErrorProtocol {
case alreadyPrepared
case revertImpossible
case automationFailed(String)
}
2 changes: 1 addition & 1 deletion Sources/Action.swift → Sources/Fluent/Query/Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ public enum Action {
case fetch
case delete
case create
case update
case modify
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 0a15ab2

Please sign in to comment.