WIP: prototype for ManagedObjectProtocol

This commit is contained in:
John Estropia
2017-04-05 21:56:19 +09:00
parent 258c237100
commit 8b77e4e5a0
44 changed files with 1244 additions and 823 deletions

View File

@@ -0,0 +1,166 @@
//
// Attribute.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreData
import Foundation
// MARK: Operators
infix operator .= : AssignmentPrecedence
postfix operator *
// MARK: - AttributeContainer
public enum AttributeContainer<O: ManagedObjectProtocol> {
// MARK: - Required
public final class Required<V: ImportableAttributeType>: AttributeProtocol {
public static func .= (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) {
attribute.value = value
}
public static postfix func * (_ attribute: AttributeContainer<O>.Required<V>) -> V {
return attribute.value
}
public let keyPath: String
public init(_ keyPath: String, `default`: V = V.cs_emptyValue()) {
self.keyPath = keyPath
self.defaultValue = `default`.cs_toImportableNativeType()
}
public var value: V {
get {
let object = self.accessRawObject()
let key = self.keyPath
let value = object.value(forKey: key)! as! V.ImportableNativeType
return V.cs_fromImportableNativeType(value)!
}
set {
let object = self.accessRawObject()
let key = self.keyPath
object.setValue(newValue.cs_toImportableNativeType(), forKey: key)
}
}
// MARK: Internal
internal static var attributeType: NSAttributeType {
return V.cs_rawAttributeType
}
internal let defaultValue: Any?
internal let isOptional = false
internal var accessRawObject: () -> NSManagedObject = {
fatalError("\(O.self) property values should not be accessed")
}
}
// MARK: - Optional
public final class Optional<V: ImportableAttributeType>: AttributeProtocol {
public static func .= (_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) {
attribute.value = value
}
public static postfix func * (_ attribute: AttributeContainer<O>.Optional<V>) -> V? {
return attribute.value
}
public let keyPath: String
public init(_ keyPath: String, `default`: V? = nil) {
self.keyPath = keyPath
self.defaultValue = `default`?.cs_toImportableNativeType()
}
public var value: V? {
get {
let object = self.accessRawObject()
let key = self.keyPath
guard let value = object.value(forKey: key) as! V.ImportableNativeType? else {
return nil
}
return V.cs_fromImportableNativeType(value)
}
set {
let object = self.accessRawObject()
let key = self.keyPath
object.setValue(newValue?.cs_toImportableNativeType(), forKey: key)
}
}
// MARK: Internal
internal static var attributeType: NSAttributeType {
return V.cs_rawAttributeType
}
internal let defaultValue: Any?
internal let isOptional = true
internal var accessRawObject: () -> NSManagedObject = {
fatalError("\(O.self) property values should not be accessed")
}
}
}
// MARK: - AttributeProtocol
internal protocol AttributeProtocol: class {
static var attributeType: NSAttributeType { get }
var keyPath: String { get }
var isOptional: Bool { get }
var defaultValue: Any? { get }
var accessRawObject: () -> NSManagedObject { get set }
}

View File

@@ -0,0 +1,198 @@
//
// Entity.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreData
import Foundation
import ObjectiveC
// MARK: - EntityProtocol
public protocol EntityProtocol {
var entityDescription: NSEntityDescription { get }
}
// MARK: Entity
public struct Entity<O: ManagedObject>: EntityProtocol {
public let entityDescription: NSEntityDescription
internal var dynamicClass: AnyClass {
return NSClassFromString(self.entityDescription.managedObjectClassName!)!
}
public init(_ entityName: String) {
let dynamicClassName = String(reflecting: O.self)
.appending("__\(entityName)")
.replacingOccurrences(of: ".", with: "_")
.replacingOccurrences(of: "<", with: "_")
.replacingOccurrences(of: ">", with: "_")
// TODO: assign entityName through ModelVersion and
// TODO: set NSEntityDescription.userInfo AnyEntity
let newClass: AnyClass?
if NSClassFromString(dynamicClassName) == nil {
newClass = objc_allocateClassPair(NSManagedObject.self, dynamicClassName, 0)
}
else {
newClass = nil
}
defer {
if let newClass = newClass {
objc_registerClassPair(newClass)
}
}
let entityDescription = NSEntityDescription()
entityDescription.userInfo = [
EntityIdentifier.UserInfoKey.CoreStoreManagedObjectName: String(reflecting: O.self)
]
entityDescription.name = entityName
entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self)
// entityDescription.managedObjectClassName = dynamicClassName // TODO: return to NSManagedObject
entityDescription.properties = type(of: self).initializeAttributes(Mirror(reflecting: O.meta))
self.entityDescription = entityDescription
}
private static func initializeAttributes(_ mirror: Mirror) -> [NSAttributeDescription] {
var attributeDescriptions: [NSAttributeDescription] = []
for child in mirror.children {
guard case let property as AttributeProtocol = child.value else {
continue
}
let attributeDescription = NSAttributeDescription()
attributeDescription.name = property.keyPath
attributeDescription.attributeType = type(of: property).attributeType
attributeDescription.isOptional = property.isOptional
attributeDescription.defaultValue = property.defaultValue
attributeDescriptions.append(attributeDescription)
}
if let baseEntityAttributeDescriptions = mirror.superclassMirror.flatMap(self.initializeAttributes) {
attributeDescriptions.append(contentsOf: baseEntityAttributeDescriptions)
}
return attributeDescriptions
}
}
// MARK: - EntityIdentifier
internal struct EntityIdentifier: Hashable {
// MARK: - Category
internal enum Category: Int {
case coreData
case coreStore
}
// MARK: -
internal let category: Category
internal let interfacedClassName: String
internal init(_ type: NSManagedObject.Type) {
self.category = .coreData
self.interfacedClassName = String(reflecting: type)
}
internal init(_ type: ManagedObject.Type) {
self.category = .coreStore
self.interfacedClassName = String(reflecting: type)
}
internal init(_ type: ManagedObjectProtocol.Type) {
switch type {
case let type as NSManagedObject.Type:
self.init(type)
case let type as ManagedObject.Type:
self.init(type)
default:
CoreStore.abort("\(cs_typeName(ManagedObjectProtocol.self)) is not meant to be implemented by external types.")
}
}
internal init(_ entityDescription: NSEntityDescription) {
if let coreStoreManagedObjectName = entityDescription.userInfo?[EntityIdentifier.UserInfoKey.CoreStoreManagedObjectName] as! String? {
self.category = .coreStore
self.interfacedClassName = coreStoreManagedObjectName
}
else {
self.category = .coreData
self.interfacedClassName = entityDescription.managedObjectClassName!
}
}
// MARK: Equatable
static func == (lhs: EntityIdentifier, rhs: EntityIdentifier) -> Bool {
return lhs.category == rhs.category
&& lhs.interfacedClassName == rhs.interfacedClassName
}
// MARK: Hashable
var hashValue: Int {
return self.category.hashValue
^ self.interfacedClassName.hashValue
}
// MARK: FilePrivate
fileprivate enum UserInfoKey {
fileprivate static let CoreStoreManagedObjectName = "CoreStoreManagedObjectName"
}
}

View File

@@ -0,0 +1,68 @@
//
// ManagedObject.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreData
import Foundation
// MARK: - ManagedObject
open class ManagedObject: ManagedObjectProtocol {
public required init(_ object: NSManagedObject) {
self.isMeta = false
self.rawObject = object
self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object })
}
public required init(asMeta: Void) {
self.isMeta = true
self.rawObject = nil
}
// MARK: Internal
internal let rawObject: NSManagedObject?
internal let isMeta: Bool
// MARK: Private
private func initializeAttributes(_ mirror: Mirror, _ accessRawObject: @escaping () -> NSManagedObject) {
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, accessRawObject) })
for child in mirror.children {
guard case let property as AttributeProtocol = child.value else {
continue
}
property.accessRawObject = accessRawObject
}
}
}

View File

@@ -0,0 +1,119 @@
//
// ManagedObjectProtocol.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
// MARK: - ManagedObjectProtocol
public protocol ManagedObjectProtocol: class {
static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self
static func cs_from(object: NSManagedObject) -> Self
}
public extension ManagedObjectProtocol where Self: ManagedObject {
public typealias Attribute = AttributeContainer<Self>
@inline(__always)
public static func keyPath<O: ManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Required<V>) -> String {
return attribute(self.meta).keyPath
}
@inline(__always)
public static func keyPath<O: ManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Optional<V>) -> String {
return attribute(self.meta).keyPath
}
@inline(__always)
public static func `where`(_ condition: (Self) -> Where) -> Where {
return condition(self.meta)
}
// MARK: Internal
internal static var meta: Self {
return self.init(asMeta: ())
}
}
// MARK: - NSManagedObject
extension NSManagedObject: ManagedObjectProtocol {
// MARK: ManagedObjectProtocol
public static func cs_from(object: NSManagedObject) -> Self {
@inline(__always)
func forceCast<T: NSManagedObject>(_ value: Any) -> T {
return value as! T
}
return forceCast(object)
}
public static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self {
let object = self.init(entity: entityDescription, insertInto: context)
defer {
context.assign(object, to: store)
}
return object
}
}
// MARK: - ManagedObject
extension ManagedObject {
// MARK: ManagedObjectProtocol
public static func cs_from(object: NSManagedObject) -> Self {
return self.init(object)
}
public static func cs_forceCreate(entityDescription: NSEntityDescription, into context: NSManagedObjectContext, assignTo store: NSPersistentStore) -> Self {
let type = NSClassFromString(entityDescription.managedObjectClassName!)! as! NSManagedObject.Type
let object = type.init(entity: entityDescription, insertInto: context)
defer {
context.assign(object, to: store)
}
return self.init(object)
}
}

View File

@@ -0,0 +1,76 @@
//
// ObjectModel.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreGraphics
import Foundation
// MARK: - ObjectModel
public final class ObjectModel {
public let version: String
public convenience init(version: String, entities: [EntityProtocol]) {
self.init(version: version, configurationEntities: [DataStack.defaultConfigurationName: entities])
}
public required init(version: String, configurationEntities: [String: [EntityProtocol]]) {
self.version = version
var entityConfigurations: [String: Set<NSEntityDescription>] = [:]
for (configuration, entities) in configurationEntities {
entityConfigurations[configuration] = Set(entities.map({ $0.entityDescription }))
}
let allEntities = Set(entityConfigurations.map({ $0.value }).joined())
entityConfigurations[DataStack.defaultConfigurationName] = allEntities
self.entityConfigurations = entityConfigurations
self.entities = allEntities
}
// MARK: Internal
internal let entities: Set<NSEntityDescription>
internal let entityConfigurations: [String: Set<NSEntityDescription>]
internal func createModel() -> NSManagedObjectModel {
let model = NSManagedObjectModel()
model.entities = self.entities.sorted(by: { $0.name! < $1.name! })
for (configuration, entities) in self.entityConfigurations {
model.setEntities(
entities.sorted(by: { $0.name! < $1.name! }),
forConfigurationName: configuration
)
}
return model
}
}

View File

@@ -1,324 +0,0 @@
//
// Prototype.swift
// CoreStore
//
// Created by John Estropia on 2017/04/03.
// Copyright © 2017 John Rommel Estropia. All rights reserved.
//
import CoreGraphics
import Foundation
import ObjectiveC
public protocol ManagedObjectProtocol: class {}
public protocol EntityProtocol {
var entityDescription: NSEntityDescription { get }
}
internal protocol AttributeProtocol: class {
static var attributeType: NSAttributeType { get }
var keyPath: String { get }
var defaultValue: Any? { get }
var accessRawObject: () -> NSManagedObject { get set }
}
open class CoreStoreManagedObject: ManagedObjectProtocol {
internal let rawObject: NSManagedObject?
internal let isMeta: Bool
public required init(_ object: NSManagedObject?) {
self.isMeta = object == nil
self.rawObject = object
guard let object = object else {
return
}
self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object })
}
private func initializeAttributes(_ mirror: Mirror, _ accessRawObject: @escaping () -> NSManagedObject) {
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, accessRawObject) })
for child in mirror.children {
guard case let property as AttributeProtocol = child.value else {
continue
}
property.accessRawObject = accessRawObject
}
}
}
public struct Entity<O: CoreStoreManagedObject>: EntityProtocol {
public let entityDescription: NSEntityDescription
internal var dynamicClass: AnyClass {
return NSClassFromString(self.entityDescription.managedObjectClassName!)!
}
public init(_ entityName: String) {
let dynamicClassName = String(reflecting: O.self)
.appending("__\(entityName)")
.replacingOccurrences(of: ".", with: "_")
.replacingOccurrences(of: "<", with: "_")
.replacingOccurrences(of: ">", with: "_")
// TODO: assign entityName through ModelVersion and
// TODO: set NSEntityDescription.userInfo AnyEntity
let newClass: AnyClass?
if NSClassFromString(dynamicClassName) == nil {
newClass = objc_allocateClassPair(NSManagedObject.self, dynamicClassName, 0)
}
else {
newClass = nil
}
defer {
if let newClass = newClass {
objc_registerClassPair(newClass)
}
}
let entityDescription = NSEntityDescription()
entityDescription.name = entityName
entityDescription.managedObjectClassName = dynamicClassName // TODO: return to NSManagedObject
entityDescription.properties = type(of: self).initializeAttributes(Mirror(reflecting: O.meta))
self.entityDescription = entityDescription
}
private static func initializeAttributes(_ mirror: Mirror) -> [NSAttributeDescription] {
var attributeDescriptions: [NSAttributeDescription] = []
for child in mirror.children {
guard case let property as AttributeProtocol = child.value else {
continue
}
let attributeDescription = NSAttributeDescription()
attributeDescription.name = property.keyPath
attributeDescription.attributeType = type(of: property).attributeType
attributeDescription.isOptional = false
attributeDescription.defaultValue = property.defaultValue
attributeDescriptions.append(attributeDescription)
}
if let baseEntityAttributeDescriptions = mirror.superclassMirror.flatMap(self.initializeAttributes) {
attributeDescriptions.append(contentsOf: baseEntityAttributeDescriptions)
}
return attributeDescriptions
}
}
public enum AttributeContainer<O: ManagedObjectProtocol> {
public final class Required<V: ImportableAttributeType>: AttributeProtocol {
static var attributeType: NSAttributeType { return V.cs_rawAttributeType }
let keyPath: String
let defaultValue: Any?
var accessRawObject: () -> NSManagedObject = { fatalError("\(O.self) property values should not be accessed") }
var value: V {
get {
let object = self.accessRawObject()
let key = self.keyPath
let value = object.value(forKey: key)! as! V.ImportableNativeType
return V.cs_fromImportableNativeType(value)!
}
set {
let object = self.accessRawObject()
let key = self.keyPath
object.setValue(newValue.cs_toImportableNativeType(), forKey: key)
}
}
public init(_ keyPath: String, `default`: V = V.cs_emptyValue()) {
self.keyPath = keyPath
self.defaultValue = `default`
}
}
public final class Optional<V: ImportableAttributeType>: AttributeProtocol {
static var attributeType: NSAttributeType { return V.cs_rawAttributeType }
let keyPath: String
let defaultValue: Any?
var accessRawObject: () -> NSManagedObject = { fatalError("\(O.self) property values should not be accessed") }
var value: V? {
get {
let object = self.accessRawObject()
let key = self.keyPath
guard let value = object.value(forKey: key) as! V.ImportableNativeType? else {
return nil
}
return V.cs_fromImportableNativeType(value)
}
set {
let object = self.accessRawObject()
let key = self.keyPath
object.setValue(newValue?.cs_toImportableNativeType(), forKey: key)
}
}
public init(_ keyPath: String, `default`: V? = nil) {
self.keyPath = keyPath
self.defaultValue = `default`
}
}
}
public extension ManagedObjectProtocol where Self: CoreStoreManagedObject {
public typealias Attribute = AttributeContainer<Self>
internal static var meta: Self {
return self.init(nil)
}
@inline(__always)
public static func keyPath<O: CoreStoreManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Required<V>) -> String {
return attribute(self.meta).keyPath
}
@inline(__always)
public static func keyPath<O: CoreStoreManagedObject, V: ImportableAttributeType>(_ attribute: (Self) -> AttributeContainer<O>.Optional<V>) -> String {
return attribute(self.meta).keyPath
}
@inline(__always)
public static func `where`(_ condition: (Self) -> Where) -> Where {
return condition(self.meta)
}
}
//: ### Convenience Operators
infix operator .= : AssignmentPrecedence
public func .= <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Required<V>, _ value: V) {
attribute.value = value
}
public func .= <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) {
attribute.value = value
}
postfix operator *
public postfix func * <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Required<V>) -> V {
return attribute.value
}
public postfix func * <O: ManagedObjectProtocol, V: ImportableAttributeType>(_ attribute: AttributeContainer<O>.Optional<V>) -> V? {
return attribute.value
}
public extension AttributeContainer.Required where V: CVarArg {
public static func == (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
return Where(attribute.keyPath, isEqualTo: value)
}
public static func < (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
return Where("%K < %@", attribute.keyPath, value)
}
public static func > (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
return Where("%K > %@", attribute.keyPath, value)
}
public static func <= (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
return Where("%K <= %@", attribute.keyPath, value)
}
public static func >= (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
return Where("%K >= %@", attribute.keyPath, value)
}
public static func != (_ attribute: AttributeContainer<O>.Required<V>, _ value: V) -> Where {
return Where("%K != %@", attribute.keyPath, value)
}
}
public extension AttributeContainer.Optional where V: CVarArg {
public static func == (_ attribute: AttributeContainer<O>.Optional<V>, _ value: V?) -> Where {
return Where(attribute.keyPath, isEqualTo: value)
}
}
public final class ModelVersion {
public let version: String
internal let entities: Set<NSEntityDescription>
internal let entityConfigurations: [String: Set<NSEntityDescription>]
public convenience init(version: String, entities: [EntityProtocol]) {
self.init(version: version, configurationEntities: [Into.defaultConfigurationName: entities])
}
public required init(version: String, configurationEntities: [String: [EntityProtocol]]) {
self.version = version
var entityConfigurations: [String: Set<NSEntityDescription>] = [:]
for (configuration, entities) in configurationEntities {
entityConfigurations[configuration] = Set(entities.map({ $0.entityDescription }))
}
let allEntities = Set(entityConfigurations.map({ $0.value }).joined())
entityConfigurations[Into.defaultConfigurationName] = allEntities
self.entityConfigurations = entityConfigurations
self.entities = allEntities
}
internal func createModel() -> NSManagedObjectModel {
let model = NSManagedObjectModel()
model.entities = self.entities.sorted(by: { $0.name! < $1.name! })
for (configuration, entities) in self.entityConfigurations {
model.setEntities(
entities.sorted(by: { $0.name! < $1.name! }),
forConfigurationName: configuration
)
}
return model
}
}