From 23d057c47b786cf8616884390c4ccdd104d44d1c Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sun, 18 Nov 2018 21:47:00 -0800 Subject: [PATCH] Adding more custom string convertible implementations to reduce the verbosity of printing to terminal. expose JSONAPIDocument.Include so that its constructor can be used. --- JSONAPI.playground/Contents.swift | 10 +- JSONAPI.playground/Sources/Entities.swift | 8 +- Sources/JSONAPI/Document/Document.swift | 14 ++- Sources/JSONAPI/Document/Includes.swift | 110 +++++++++++++++++++- Sources/JSONAPI/Document/ResourceBody.swift | 14 +++ Sources/JSONAPI/Resource/Attribute.swift | 6 ++ Sources/JSONAPI/Resource/Entity.swift | 6 ++ Sources/JSONAPI/Resource/Relationship.swift | 4 + 8 files changed, 162 insertions(+), 10 deletions(-) diff --git a/JSONAPI.playground/Contents.swift b/JSONAPI.playground/Contents.swift index 9323a13..5a529ab 100644 --- a/JSONAPI.playground/Contents.swift +++ b/JSONAPI.playground/Contents.swift @@ -12,6 +12,7 @@ Please enjoy these examples, but allow me the forced casting and the lack of err let dogFromCode = try! Dog(name: "Buddy", owner: nil) typealias SingleDogDocument = JSONAPIDocument, NoIncludes, BasicJSONAPIError> + let singleDogDocument = SingleDogDocument(body: SingleResourceBody(entity: dogFromCode)) let singleDogData = try! JSONEncoder().encode(singleDogDocument) @@ -21,10 +22,15 @@ let dogResponse = try! JSONDecoder().decode(SingleDogDocument.self, from: single let dogFromData = dogResponse.body.data?.primary.value // MARK: - Create a request or response with multiple people and dogs and houses included -//let people +let personIds = [Person.Identifier(), Person.Identifier()] +let dogs = try [Dog(name: "Buddy", owner: personIds[0]), Dog(name: "Joy", owner: personIds[0]), Dog(name: "Travis", owner: personIds[1])] +let houses = [House(), House()] +let people = try [Person(id: personIds[0], name: ["Gary", "Doe"], favoriteColor: "Orange-Red", friends: [], dogs: [dogs[0], dogs[1]], home: houses[0]), Person(id: personIds[1], name: ["Elise", "Joy"], favoriteColor: "Red", friends: [], dogs: [dogs[2]], home: houses[1])] -//typealias BatchPeopleDocument = JSONAPIDocument, Include2, BasicJSONAPIError> +typealias BatchPeopleDocument = JSONAPIDocument, Include2, BasicJSONAPIError> +let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) } +let batchPeopleDocument = BatchPeopleDocument(body: .init(entities: people), includes: .init(values: includes)) // MARK: - Parse a request or response body with multiple people in it and dogs and houses included diff --git a/JSONAPI.playground/Sources/Entities.swift b/JSONAPI.playground/Sources/Entities.swift index 78f2cca..8714611 100644 --- a/JSONAPI.playground/Sources/Entities.swift +++ b/JSONAPI.playground/Sources/Entities.swift @@ -46,8 +46,8 @@ public enum PersonDescription: EntityDescription { public typealias Person = ExampleEntity public extension Entity where Description == PersonDescription, Identifier == Id { - public init(name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws { - self = try Person(attributes: .init(name: .init(rawValue: name), favoriteColor: .init(rawValue: favoriteColor)), relationships: .init(friends: .init(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home))) + public init(id: Person.Identifier? = nil,name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws { + self = try Person(id: id ?? Person.Identifier(), attributes: .init(name: .init(rawValue: name), favoriteColor: .init(rawValue: favoriteColor)), relationships: .init(friends: .init(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home))) } } @@ -70,6 +70,10 @@ public extension Entity where Description == DogDescription, Identifier == Id: Equatable { +public struct JSONAPIDocument: Equatable { + public typealias Include = IncludeType + public let body: Body // public let meta: Meta? // public let jsonApi: APIDescription? @@ -42,7 +44,7 @@ public struct JSONAPIDocument: Codable, Equatable { +public struct Includes: Codable, Equatable { public static var none: Includes { return .init(values: []) } let values: [I] - private init(values: [I]) { + public init(values: [I]) { self.values = values } @@ -52,6 +52,12 @@ public struct Includes: Codable, Equatable { } } +extension Includes: CustomStringConvertible { + public var description: String { + return "Includes(\(String(describing: values))" + } +} + extension Includes where I == NoIncludes { public init() { values = [] @@ -77,7 +83,7 @@ func decode(_ type: Entity.Type, from container: Sin // MARK: - 0 includes -public protocol _Include0: IncludeDecoder { } +public protocol _Include0: Include { } public struct Include0: _Include0 { public init() {} @@ -95,6 +101,8 @@ public typealias NoIncludes = Include0 public protocol _Include1: _Include0 { associatedtype A: EntityType var a: A? { get } + + init(_ a: A) } public enum Include1: _Include1 { case a(A) @@ -103,6 +111,10 @@ public enum Include1: _Include1 { guard case let .a(ret) = self else { return nil } return ret } + + public init(_ a: A) { + self = .a(a) + } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() @@ -130,6 +142,8 @@ extension Includes where I: _Include1 { public protocol _Include2: _Include1 { associatedtype B: EntityType var b: B? { get } + + init(_ b: B) } public enum Include2: _Include2 { case a(A) @@ -139,11 +153,19 @@ public enum Include2: _Include2 { guard case let .a(ret) = self else { return nil } return ret } + + public init(_ a: A) { + self = .a(a) + } public var b: B? { guard case let .b(ret) = self else { return nil } return ret } + + public init(_ b: B) { + self = .b(b) + } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() @@ -185,6 +207,8 @@ extension Includes where I: _Include2 { public protocol _Include3: _Include2 { associatedtype C: EntityType var c: C? { get } + + init(_ c: C) } public enum Include3: _Include3 { case a(A) @@ -195,16 +219,28 @@ public enum Include3: _Include3 { guard case let .a(ret) = self else { return nil } return ret } + + public init(_ a: A) { + self = .a(a) + } public var b: B? { guard case let .b(ret) = self else { return nil } return ret } + + public init(_ b: B) { + self = .b(b) + } public var c: C? { guard case let .c(ret) = self else { return nil } return ret } + + public init(_ c: C) { + self = .c(c) + } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() @@ -249,6 +285,8 @@ extension Includes where I: _Include3 { public protocol _Include4: _Include3 { associatedtype D: EntityType var d: D? { get } + + init(_ d: D) } public enum Include4: _Include4 { case a(A) @@ -260,21 +298,37 @@ public enum Include4 guard case let .a(ret) = self else { return nil } return ret } + + public init(_ a: A) { + self = .a(a) + } public var b: B? { guard case let .b(ret) = self else { return nil } return ret } + + public init(_ b: B) { + self = .b(b) + } public var c: C? { guard case let .c(ret) = self else { return nil } return ret } + + public init(_ c: C) { + self = .c(c) + } public var d: D? { guard case let .d(ret) = self else { return nil } return ret } + + public init(_ d: D) { + self = .d(d) + } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() @@ -322,6 +376,8 @@ extension Includes where I: _Include4 { public protocol _Include5: _Include4 { associatedtype E: EntityType var e: E? { get } + + init(_ e: E) } public enum Include5: _Include5 { case a(A) @@ -334,26 +390,46 @@ public enum Include5: _Include6 { case a(A) @@ -417,31 +495,55 @@ public enum Include6 \(String(describing: Transformer.To.self))>(\(String(describing: value)))" + } +} + extension TransformAttribute: Equatable where Transformer.From: Equatable, Transformer.To: Equatable {} public typealias Attribute = TransformAttribute> diff --git a/Sources/JSONAPI/Resource/Entity.swift b/Sources/JSONAPI/Resource/Entity.swift index fee00dd..19954eb 100644 --- a/Sources/JSONAPI/Resource/Entity.swift +++ b/Sources/JSONAPI/Resource/Entity.swift @@ -70,6 +70,12 @@ public struct Entity(id: \(String(describing: id)), attributes: \(String(describing: attributes)), relationships: \(String(describing: relationships)))" + } +} + // MARK: Convenience initializers extension Entity where Identifier: CreatableIdType { public init(attributes: Description.Attributes, relationships: Description.Relationships) { diff --git a/Sources/JSONAPI/Resource/Relationship.swift b/Sources/JSONAPI/Resource/Relationship.swift index 776f0c7..1258284 100644 --- a/Sources/JSONAPI/Resource/Relationship.swift +++ b/Sources/JSONAPI/Resource/Relationship.swift @@ -16,6 +16,10 @@ public struct ToOneRelationship: Equatable public var ids: [Relatable.WrappedIdentifier] { return [id] } + + public init(id: Relatable.WrappedIdentifier) { + self.id = id + } } extension ToOneRelationship where Relatable.WrappedIdentifier == Relatable.Identifier {