Prepping for ToOneRelationships that have optional related types for use in situations where null is an acceptable relationship value.

This commit is contained in:
Mathew Polzin
2018-11-14 23:39:12 -08:00
parent fd82d5d7de
commit bf44c2fcdd
9 changed files with 137 additions and 47 deletions
+6
View File
@@ -21,6 +21,12 @@ public struct Includes<I: IncludeDecoder>: Decodable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
// If not parsing includes, no need to loop over them.
guard I.self != NoIncludes.self else {
values = []
return
}
var valueAggregator = [I]()
while !container.isAtEnd {
valueAggregator.append(try container.decode(I.self))
+4 -4
View File
@@ -129,7 +129,7 @@ extension Entity where EntityType.Attributes == NoAttributes, EntityType.Relatio
public extension Entity where EntityType.Identifier: IdType {
/// Get a pointer to this entity that can be used as a
/// relationship to another entity.
public var pointer: ToOneRelationship<EntityType> {
public var pointer: ToOneRelationship<Entity> {
return ToOneRelationship(entity: self)
}
}
@@ -163,14 +163,14 @@ public extension Entity {
/// Access to an Id of a `ToOneRelationship`.
/// This allows you to write `entity ~> \.other` instead
/// of `entity.relationships.other.id`.
public static func ~><OtherEntityType: JSONAPI.EntityDescription>(entity: Entity<EntityType>, path: KeyPath<EntityType.Relationships, ToOneRelationship<OtherEntityType>>) -> OtherEntityType.Identifier {
public static func ~><OtherEntity: Relatable>(entity: Entity<EntityType>, path: KeyPath<EntityType.Relationships, ToOneRelationship<OtherEntity>>) -> OtherEntity.Description.Identifier {
return entity.relationships[keyPath: path].id
}
/// Access to all Ids of a `ToManyRelationship`.
/// This allows you to write `entity ~> \.others` instead
/// of `entity.relationships.others.ids`.
public static func ~><OtherEntityType: JSONAPI.EntityDescription>(entity: Entity<EntityType>, path: KeyPath<EntityType.Relationships, ToManyRelationship<OtherEntityType>>) -> [OtherEntityType.Identifier] {
public static func ~><OtherEntity: Relatable>(entity: Entity<EntityType>, path: KeyPath<EntityType.Relationships, ToManyRelationship<OtherEntity>>) -> [OtherEntity.Description.Identifier] {
return entity.relationships[keyPath: path].ids
}
}
+8 -2
View File
@@ -24,17 +24,23 @@ extension String: RawIdType {}
public protocol Identifier: Codable, Equatable {}
public struct Unidentified: Identifier {
public struct Unidentified: Identifier, CustomStringConvertible {
public init() {}
public var description: String { return "Id(Unidentified)" }
}
public protocol IdType: Identifier {
public protocol IdType: Identifier, CustomStringConvertible {
associatedtype EntityType: JSONAPI.EntityDescription
associatedtype RawType: RawIdType
var rawValue: RawType { get }
}
public extension IdType {
var description: String { return "Id(\(String(describing: rawValue)))" }
}
public protocol CreatableIdType: IdType {
init()
}
+34 -3
View File
@@ -10,7 +10,7 @@
/// You should use the `ToOneRelationship` and `ToManyRelationship`
/// concrete types.
/// See https://jsonapi.org/format/#document-resource-object-linkage
public protocol Relationship: Equatable, Encodable {
public protocol Relationship: Equatable, Encodable, CustomStringConvertible {
associatedtype EntityType: JSONAPI.EntityDescription where EntityType.Identifier: IdType
var ids: [EntityType.Identifier] { get }
}
@@ -19,7 +19,9 @@ public protocol Relationship: Equatable, Encodable {
/// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `One<EntityDescription>`
public struct ToOneRelationship<EntityType: JSONAPI.EntityDescription>: Equatable, Relationship, Decodable where EntityType.Identifier: IdType {
public struct ToOneRelationship<Relatable: JSONAPI.OptionalRelatable>: Equatable, Relationship, Decodable {
public typealias EntityType = Relatable.Description
public let id: EntityType.Identifier
public init(entity: Entity<EntityType>) {
@@ -35,7 +37,9 @@ public struct ToOneRelationship<EntityType: JSONAPI.EntityDescription>: Equatabl
/// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `Many<EntityDescription>`
public struct ToManyRelationship<EntityType: JSONAPI.EntityDescription>: Equatable, Relationship, Decodable where EntityType.Identifier: IdType {
public struct ToManyRelationship<Relatable: JSONAPI.Relatable>: Equatable, Relationship, Decodable {
public typealias EntityType = Relatable.Description
public let ids: [EntityType.Identifier]
public init(entities: [Entity<EntityType>]) {
@@ -51,6 +55,24 @@ public struct ToManyRelationship<EntityType: JSONAPI.EntityDescription>: Equatab
}
}
/// The OptionalRelatable protocol ONLY describes
/// Optional<T: Relatable> types.
public protocol OptionalRelatable {
associatedtype Description: EntityDescription where Description.Identifier: IdType
}
/// The Relatable protocol describes anything that
/// has an EntityDescription
public protocol Relatable: OptionalRelatable {}
extension Entity: Relatable, OptionalRelatable where EntityType.Identifier: IdType {
public typealias Description = EntityType
}
extension Optional: OptionalRelatable where Wrapped: Relatable {
public typealias Description = Wrapped.Description
}
// MARK: Codable
private enum ResourceLinkageCodingKeys: String, CodingKey {
case data = "data"
@@ -120,3 +142,12 @@ extension ToManyRelationship {
}
}
}
// MARK: CustomStringDescribable
public extension ToOneRelationship {
var description: String { return "Relationship(\(String(describing: id)))" }
}
public extension ToManyRelationship {
var description: String { return "Relationship(\(String(describing: ids)))" }
}