From c7b97567a9fb3db03b9af08b68a2fa37bd037727 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 20 Jun 2019 22:35:55 -0700 Subject: [PATCH 1/8] update swift tools version in package file, add some property wrappers, add some tests for wrappers. its all broken but worth holding onto for now. --- Package.swift | 2 +- Sources/JSONAPI/Resource/Attribute.swift | 4 +- .../JSONAPI/Resource/PropertyWrappers.swift | 115 ++++++++++++++++++ .../Attribute/AttributeTests.swift | 91 +++++++++++++- Tests/JSONAPITests/Entity/EntityTests.swift | 2 +- 5 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 Sources/JSONAPI/Resource/PropertyWrappers.swift diff --git a/Package.swift b/Package.swift index 0789b25..675e093 100644 --- a/Package.swift +++ b/Package.swift @@ -34,5 +34,5 @@ let package = Package( name: "JSONAPITestingTests", dependencies: ["JSONAPI", "JSONAPITesting"]) ], - swiftLanguageVersions: [.v5] + swiftLanguageVersions: [.version("5.1")] ) diff --git a/Sources/JSONAPI/Resource/Attribute.swift b/Sources/JSONAPI/Resource/Attribute.swift index a43c368..c05983d 100644 --- a/Sources/JSONAPI/Resource/Attribute.swift +++ b/Sources/JSONAPI/Resource/Attribute.swift @@ -17,9 +17,9 @@ public protocol AttributeType: Codable { /// A TransformedAttribute takes a Codable type and attempts to turn it into another type. public struct TransformedAttribute: AttributeType where Transformer.From == RawValue { public let rawValue: RawValue - + public let value: Transformer.To - + public init(rawValue: RawValue) throws { self.rawValue = rawValue value = try Transformer.transform(rawValue) diff --git a/Sources/JSONAPI/Resource/PropertyWrappers.swift b/Sources/JSONAPI/Resource/PropertyWrappers.swift new file mode 100644 index 0000000..6c50f18 --- /dev/null +++ b/Sources/JSONAPI/Resource/PropertyWrappers.swift @@ -0,0 +1,115 @@ +// +// PropertyWrappers.swift +// +// +// Created by Mathew Polzin on 6/20/19. +// + + +// MARK: - Transformed +@propertyWrapper +public struct Transformed { + + public typealias RawValue = Transformer.From + public typealias Value = Transformer.To + + private var _value: Value? + + public var wrappedValue: Value { + get { + guard let ret = _value else { + fatalError("Attribute read from before initialization.") + } + return ret + } + set { + _value = newValue + } + } + + public init(initialValue: Value, _ transformer: Transformer.Type) { + self._value = initialValue + } + + public init(_ transformer: Transformer.Type) { + self._value = nil + } + + public init(rawValue: RawValue, _ transformer: Transformer.Type) throws { + self._value = try Transformer.transform(rawValue) + } +} + +extension Transformed: Decodable where Transformer.From: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + let rawVal = try container.decode(Transformer.From.self) + + _value = try Transformer.transform(rawVal) + } +} + +extension Transformed: Encodable where Transformer: ReversibleTransformer, Transformer.From: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + + guard let value = _value else { + fatalError("Attribute encoded before initialization.") + } + + try container.encode(Transformer.reverse(value)) + } +} + +// MARK: - Nullable + +public protocol _Optional { + static var nilValue: Self { get } + var isNilValue: Bool { get } +} + +extension Optional: _Optional { + public static var nilValue: Self { + return .none + } + + public var isNilValue: Bool { return self == nil } +} + +protocol _Nullable {} + +@propertyWrapper +public struct Nullable: Decodable, _Optional, _Nullable { + public var wrappedValue: T? + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + + if container.decodeNil() { + wrappedValue = nil + return + } + + wrappedValue = try container.decode(T.self) + } + + public init(initialValue: T? = nil) { + wrappedValue = initialValue + } + + public static var nilValue: Self { + return .init() + } + + public var isNilValue: Bool { + return wrappedValue == nil + } +} + +extension Nullable: Encodable where T: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(wrappedValue) + } +} diff --git a/Tests/JSONAPITests/Attribute/AttributeTests.swift b/Tests/JSONAPITests/Attribute/AttributeTests.swift index 22acd71..a516f36 100644 --- a/Tests/JSONAPITests/Attribute/AttributeTests.swift +++ b/Tests/JSONAPITests/Attribute/AttributeTests.swift @@ -62,6 +62,70 @@ class AttributeTests: XCTestCase { } } +// MARK: Property Wrappers +extension AttributeTests { + func test_Transformed() { + + struct Test: Codable { + @Transformed(IntToString.self) + var value: String = "" + } + + let test = Test(value: "hello") + XCTAssertEqual(test.value, "hello") + + let test2 = try! JSONDecoder().decode(Test.self, + from: #"{"value": 12}"#.data(using: .utf8)!) + + XCTAssertEqual(test2.value, "12") + try! print(String(data: JSONEncoder().encode(test2), encoding: .utf8)!) + + let test3 = try? JSONDecoder().decode(Test.self, + from: #"{"value": null}"#.data(using: .utf8)!) + + XCTAssertNil(test3) + } + + func test_Nullable() { + struct Test: Codable { + @Nullable + var value: String? + } + + let test = Test(value: nil) + XCTAssertNil(test.value) + + let test2 = Test(value: "hello") + XCTAssertEqual(test2.value, "hello") + + let test3 = try! JSONDecoder().decode(Test.self, + from: #"{"value": "world"}"#.data(using: .utf8)!) + + XCTAssertEqual(test3.value, "world") + try! print(String(data: JSONEncoder().encode(test2), encoding: .utf8)!) + + let test4 = try? JSONDecoder().decode(Test.self, + from: #"{"value": null}"#.data(using: .utf8)!) + + XCTAssertNotNil(test4) + XCTAssertNil(test4?.value) + } + + func test_NullableTransformed() { + struct Test: Codable { +// Nullable> + let x: Transformed +// @Nullable @Transformed(IdentityTransformer.self) + @Transformed(IntToString.self) @Nullable + var value: String? + } + + let test = Test(x: .init(initialValue: "12", IntToString.self)) + + print(test.x.wrappedValue) + } +} + // MARK: Test types extension AttributeTests { enum TestTransformer: ReversibleTransformer { @@ -77,12 +141,37 @@ extension AttributeTests { } } - enum IntToString: Transformer { + enum IntToString: ReversibleTransformer { public static func transform(_ from: Int) -> String { return String(from) } + + public static func reverse(_ value: String) throws -> Int { + guard let intValue = Int(value) else { + fatalError("Reversed IntToString with invalid String value.") + } + return intValue + } } + enum OptionalIntToOptionalString: ReversibleTransformer { + public static func transform(_ from: Int?) -> String? { + return from.map(String.init) + } + + public static func reverse(_ value: String?) throws -> Int? { + guard let stringValue = value else { + return nil + } + + guard let intValue = Int(stringValue) else { + fatalError("Reversed IntToString with invalid String value.") + } + + return intValue + } + } + enum IntToInt: Transformer { public static func transform(_ from: Int) -> Int { return from + 100 diff --git a/Tests/JSONAPITests/Entity/EntityTests.swift b/Tests/JSONAPITests/Entity/EntityTests.swift index 18e313e..0110865 100644 --- a/Tests/JSONAPITests/Entity/EntityTests.swift +++ b/Tests/JSONAPITests/Entity/EntityTests.swift @@ -29,7 +29,7 @@ class EntityTests: XCTestCase { let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none) let entity = TestEntity9(attributes: .none, relationships: .init(one: entity1.pointer, nullableOne: .init(resourceObject: entity1, meta: .none, links: .none), optionalOne: .init(resourceObject: entity1, meta: .none, links: .none), optionalNullableOne: nil, optionalMany: .init(resourceObjects: [entity1, entity1], meta: .none, links: .none)), meta: .none, links: .none) - XCTAssertEqual(entity ~> \.optionalOne, entity1.id) + XCTAssertEqual(entity ~> \.optionalOne, Optional(entity1.id)) } func test_toMany_relationship_operator_access() { From 0144a2ee804ac3dfecaf54dd4f4ce23ef0225060 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Jul 2019 07:49:17 -0700 Subject: [PATCH 2/8] Remove prior experimentation with property wrappers -- feature was not baked when that experimentation was done. --- .../JSONAPI/Resource/PropertyWrappers.swift | 115 ------------------ .../Attribute/AttributeTests.swift | 64 ---------- 2 files changed, 179 deletions(-) delete mode 100644 Sources/JSONAPI/Resource/PropertyWrappers.swift diff --git a/Sources/JSONAPI/Resource/PropertyWrappers.swift b/Sources/JSONAPI/Resource/PropertyWrappers.swift deleted file mode 100644 index 6c50f18..0000000 --- a/Sources/JSONAPI/Resource/PropertyWrappers.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// PropertyWrappers.swift -// -// -// Created by Mathew Polzin on 6/20/19. -// - - -// MARK: - Transformed -@propertyWrapper -public struct Transformed { - - public typealias RawValue = Transformer.From - public typealias Value = Transformer.To - - private var _value: Value? - - public var wrappedValue: Value { - get { - guard let ret = _value else { - fatalError("Attribute read from before initialization.") - } - return ret - } - set { - _value = newValue - } - } - - public init(initialValue: Value, _ transformer: Transformer.Type) { - self._value = initialValue - } - - public init(_ transformer: Transformer.Type) { - self._value = nil - } - - public init(rawValue: RawValue, _ transformer: Transformer.Type) throws { - self._value = try Transformer.transform(rawValue) - } -} - -extension Transformed: Decodable where Transformer.From: Decodable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - let rawVal = try container.decode(Transformer.From.self) - - _value = try Transformer.transform(rawVal) - } -} - -extension Transformed: Encodable where Transformer: ReversibleTransformer, Transformer.From: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - guard let value = _value else { - fatalError("Attribute encoded before initialization.") - } - - try container.encode(Transformer.reverse(value)) - } -} - -// MARK: - Nullable - -public protocol _Optional { - static var nilValue: Self { get } - var isNilValue: Bool { get } -} - -extension Optional: _Optional { - public static var nilValue: Self { - return .none - } - - public var isNilValue: Bool { return self == nil } -} - -protocol _Nullable {} - -@propertyWrapper -public struct Nullable: Decodable, _Optional, _Nullable { - public var wrappedValue: T? - - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - - if container.decodeNil() { - wrappedValue = nil - return - } - - wrappedValue = try container.decode(T.self) - } - - public init(initialValue: T? = nil) { - wrappedValue = initialValue - } - - public static var nilValue: Self { - return .init() - } - - public var isNilValue: Bool { - return wrappedValue == nil - } -} - -extension Nullable: Encodable where T: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(wrappedValue) - } -} diff --git a/Tests/JSONAPITests/Attribute/AttributeTests.swift b/Tests/JSONAPITests/Attribute/AttributeTests.swift index a516f36..f68831c 100644 --- a/Tests/JSONAPITests/Attribute/AttributeTests.swift +++ b/Tests/JSONAPITests/Attribute/AttributeTests.swift @@ -62,70 +62,6 @@ class AttributeTests: XCTestCase { } } -// MARK: Property Wrappers -extension AttributeTests { - func test_Transformed() { - - struct Test: Codable { - @Transformed(IntToString.self) - var value: String = "" - } - - let test = Test(value: "hello") - XCTAssertEqual(test.value, "hello") - - let test2 = try! JSONDecoder().decode(Test.self, - from: #"{"value": 12}"#.data(using: .utf8)!) - - XCTAssertEqual(test2.value, "12") - try! print(String(data: JSONEncoder().encode(test2), encoding: .utf8)!) - - let test3 = try? JSONDecoder().decode(Test.self, - from: #"{"value": null}"#.data(using: .utf8)!) - - XCTAssertNil(test3) - } - - func test_Nullable() { - struct Test: Codable { - @Nullable - var value: String? - } - - let test = Test(value: nil) - XCTAssertNil(test.value) - - let test2 = Test(value: "hello") - XCTAssertEqual(test2.value, "hello") - - let test3 = try! JSONDecoder().decode(Test.self, - from: #"{"value": "world"}"#.data(using: .utf8)!) - - XCTAssertEqual(test3.value, "world") - try! print(String(data: JSONEncoder().encode(test2), encoding: .utf8)!) - - let test4 = try? JSONDecoder().decode(Test.self, - from: #"{"value": null}"#.data(using: .utf8)!) - - XCTAssertNotNil(test4) - XCTAssertNil(test4?.value) - } - - func test_NullableTransformed() { - struct Test: Codable { -// Nullable> - let x: Transformed -// @Nullable @Transformed(IdentityTransformer.self) - @Transformed(IntToString.self) @Nullable - var value: String? - } - - let test = Test(x: .init(initialValue: "12", IntToString.self)) - - print(test.x.wrappedValue) - } -} - // MARK: Test types extension AttributeTests { enum TestTransformer: ReversibleTransformer { From 9143281290f6b89d121d761520bfe184771d0260 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Jul 2019 09:12:17 -0700 Subject: [PATCH 3/8] Introduce dynamic member keypath lookup as alternative to existing subscript keypath lookup for attributes. also did a bit of tidying up. --- .../Usage.xcplaygroundpage/Contents.swift | 6 +- JSONAPI.playground/Sources/Entities.swift | 6 +- Sources/JSONAPI/Resource/ResourceObject.swift | 59 ++++++++++++---- .../Attribute/Attribute+FunctorTests.swift | 3 + .../ComputedPropertiesTests.swift | 2 + .../CustomAttributesTests.swift | 4 ++ .../ResourceObjectTests.swift} | 67 +++++++++++++++---- .../stubs/ResourceObjectStubs.swift} | 0 8 files changed, 112 insertions(+), 35 deletions(-) rename Tests/JSONAPITests/{Entity/EntityTests.swift => ResourceObject/ResourceObjectTests.swift} (93%) rename Tests/JSONAPITests/{Entity/stubs/EntityStubs.swift => ResourceObject/stubs/ResourceObjectStubs.swift} (100%) diff --git a/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift b/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift index 22bb3c0..6850045 100644 --- a/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift +++ b/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift @@ -49,15 +49,11 @@ print("-----") // MARK: - Pass successfully parsed body to other parts of the code -/* - ---- CRASHING IN XCODE 10.2 PLAYGROUND ---- - if case let .data(bodyData) = peopleResponse.body { - print("first person's name: \(bodyData.primary.values[0][\.fullName])") + print("first person's name: \(bodyData.primary.values[0].fullName)") } else { print("no body data") } - */ // MARK: - Work in the abstract diff --git a/JSONAPI.playground/Sources/Entities.swift b/JSONAPI.playground/Sources/Entities.swift index a62c993..d01f4f8 100644 --- a/JSONAPI.playground/Sources/Entities.swift +++ b/JSONAPI.playground/Sources/Entities.swift @@ -15,11 +15,11 @@ Please enjoy these examples, but allow me the forced casting and the lack of err ********/ // MARK: - String as CreatableRawIdType -var GlobalStringId: Int = 0 +var globalStringId: Int = 0 extension String: CreatableRawIdType { public static func unique() -> String { - GlobalStringId += 1 - return String(GlobalStringId) + globalStringId += 1 + return String(globalStringId) } } diff --git a/Sources/JSONAPI/Resource/ResourceObject.swift b/Sources/JSONAPI/Resource/ResourceObject.swift index 83cef3e..1450c46 100644 --- a/Sources/JSONAPI/Resource/ResourceObject.swift +++ b/Sources/JSONAPI/Resource/ResourceObject.swift @@ -56,8 +56,9 @@ public protocol ResourceObjectProxyDescription: JSONTyped { public protocol ResourceObjectDescription: ResourceObjectProxyDescription where Attributes: JSONAPI.Attributes, Relationships: JSONAPI.Relationships {} /// ResourceObjectProxy is a protocol that can be used to create -/// types that _act_ like Entities but cannot be encoded -/// or decoded as Entities. +/// types that _act_ like ResourceObject but cannot be encoded +/// or decoded as ResourceObjects. +@dynamicMemberLookup public protocol ResourceObjectProxy: Equatable, JSONTyped { associatedtype Description: ResourceObjectProxyDescription associatedtype EntityRawIdType: JSONAPI.MaybeRawId @@ -81,7 +82,7 @@ public protocol ResourceObjectProxy: Equatable, JSONTyped { } extension ResourceObjectProxy { - /// The JSON API compliant "type" of this `Entity`. + /// The JSON API compliant "type" of this `ResourceObject`. public static var jsonType: String { return Description.jsonType } } @@ -141,7 +142,7 @@ extension ResourceObject: CustomStringConvertible { } } -// MARK: Convenience initializers +// MARK: - Convenience initializers extension ResourceObject where EntityRawIdType: CreatableRawIdType { public init(attributes: Description.Attributes, relationships: Description.Relationships, meta: MetaType, links: LinksType) { self.id = ResourceObject.Id() @@ -392,7 +393,7 @@ extension ResourceObject where MetaType == NoMetadata, LinksType == NoLinks, Ent } */ -// MARK: Pointer for Relationships use. +// MARK: - Pointer for Relationships use public extension ResourceObject where EntityRawIdType: JSONAPI.RawIdType { /// An ResourceObject.Pointer is a `ToOneRelationship` with no metadata or links. @@ -416,7 +417,7 @@ public extension ResourceObject where EntityRawIdType: JSONAPI.RawIdType { } } -// MARK: Identifying Unidentified Entities +// MARK: - Identifying Unidentified Entities public extension ResourceObject where EntityRawIdType == Unidentified { /// Create a new ResourceObject from this one with a newly created /// unique Id of the given type. @@ -437,31 +438,55 @@ public extension ResourceObject where EntityRawIdType: CreatableRawIdType { } } -// MARK: Attribute Access +// MARK: - Attribute Access public extension ResourceObjectProxy { + // MARK: Keypath Subscript Lookup /// Access the attribute at the given keypath. This just /// allows you to write `resourceObject[\.propertyName]` instead - /// of `resourceObject.attributes.propertyName`. + /// of `resourceObject.attributes.propertyName.value`. subscript(_ path: KeyPath) -> T.ValueType { return attributes[keyPath: path].value } /// Access the attribute at the given keypath. This just /// allows you to write `resourceObject[\.propertyName]` instead - /// of `resourceObject.attributes.propertyName`. + /// of `resourceObject.attributes.propertyName.value`. subscript(_ path: KeyPath) -> T.ValueType? { return attributes[keyPath: path]?.value } /// Access the attribute at the given keypath. This just /// allows you to write `resourceObject[\.propertyName]` instead - /// of `resourceObject.attributes.propertyName`. + /// of `resourceObject.attributes.propertyName.value`. subscript(_ path: KeyPath) -> U? where T.ValueType == U? { // Implementation Note: Handles Transform that returns optional // type. return attributes[keyPath: path].flatMap { $0.value } } + // MARK: Dynaminc Member Keypath Lookup + /// Access the attribute at the given keypath. This just + /// allows you to write `resourceObject[\.propertyName]` instead + /// of `resourceObject.attributes.propertyName.value`. + subscript(dynamicMember path: KeyPath) -> T.ValueType { + return attributes[keyPath: path].value + } + + /// Access the attribute at the given keypath. This just + /// allows you to write `resourceObject[\.propertyName]` instead + /// of `resourceObject.attributes.propertyName.value`. + subscript(dynamicMember path: KeyPath) -> T.ValueType? { + return attributes[keyPath: path]?.value + } + + /// Access the attribute at the given keypath. This just + /// allows you to write `resourceObject[\.propertyName]` instead + /// of `resourceObject.attributes.propertyName.value`. + subscript(dynamicMember path: KeyPath) -> U? where T.ValueType == U? { + return attributes[keyPath: path].flatMap { $0.value } + } + + // MARK: Direct Keypath Subscript Lookup /// Access the storage of the attribute at the given keypath. This just /// allows you to write `resourceObject[direct: \.propertyName]` instead /// of `resourceObject.attributes.propertyName`. @@ -475,16 +500,24 @@ public extension ResourceObjectProxy { } } -// MARK: Meta-Attribute Access +// MARK: - Meta-Attribute Access public extension ResourceObjectProxy { + // MARK: Keypath Subscript Lookup /// Access an attribute requiring a transformation on the RawValue _and_ /// a secondary transformation on this entity (self). subscript(_ path: KeyPath T>) -> T { return attributes[keyPath: path](self) } + + // MARK: Dynamic Member Keypath Lookup + /// Access an attribute requiring a transformation on the RawValue _and_ + /// a secondary transformation on this entity (self). + subscript(dynamicMember path: KeyPath T>) -> T { + return attributes[keyPath: path](self) + } } -// MARK: Relationship Access +// MARK: - Relationship Access public extension ResourceObjectProxy { /// Access to an Id of a `ToOneRelationship`. /// This allows you to write `resourceObject ~> \.other` instead @@ -526,7 +559,7 @@ public extension ResourceObjectProxy { } } -// MARK: Meta-Relationship Access +// MARK: - Meta-Relationship Access public extension ResourceObjectProxy { /// Access to an Id of a `ToOneRelationship`. /// This allows you to write `resourceObject ~> \.other` instead diff --git a/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift b/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift index 4386d9f..ab2d7b7 100644 --- a/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift +++ b/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift @@ -16,6 +16,7 @@ class Attribute_FunctorTests: XCTestCase { XCTAssertNotNil(entity) XCTAssertEqual(entity?[\.computedString], "Frankie2") + XCTAssertEqual(entity?.computedString, "Frankie2") } func test_mapOptionalSuccess() { @@ -24,6 +25,7 @@ class Attribute_FunctorTests: XCTestCase { XCTAssertNotNil(entity) XCTAssertEqual(entity?[\.computedNumber], 22) + XCTAssertEqual(entity?.computedNumber, 22) } func test_mapOptionalFailure() { @@ -32,6 +34,7 @@ class Attribute_FunctorTests: XCTestCase { XCTAssertNotNil(entity) XCTAssertNil(entity?[\.computedNumber]) + XCTAssertNil(entity?.computedNumber) } } diff --git a/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift b/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift index c7ebbf4..add302d 100644 --- a/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift +++ b/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift @@ -15,6 +15,7 @@ class ComputedPropertiesTests: XCTestCase { XCTAssertEqual(entity.id, "1234") XCTAssertEqual(entity[\.name], "Sarah") + XCTAssertEqual(entity.name, "Sarah") XCTAssertEqual(entity ~> \.other, "5678") XCTAssertNoThrow(try TestType.check(entity)) } @@ -27,6 +28,7 @@ class ComputedPropertiesTests: XCTestCase { let entity = decoded(type: TestType.self, data: computed_property_attribute) XCTAssertEqual(entity[\.computed], "Sarah2") + XCTAssertEqual(entity.computed, "Sarah2") XCTAssertEqual(entity[direct: \.directSecretsOut], "shhhh") } diff --git a/Tests/JSONAPITests/Custom Attributes Tests/CustomAttributesTests.swift b/Tests/JSONAPITests/Custom Attributes Tests/CustomAttributesTests.swift index 7d6e8f0..91e8d8f 100644 --- a/Tests/JSONAPITests/Custom Attributes Tests/CustomAttributesTests.swift +++ b/Tests/JSONAPITests/Custom Attributes Tests/CustomAttributesTests.swift @@ -14,7 +14,9 @@ class CustomAttributesTests: XCTestCase { let entity = decoded(type: CustomAttributeEntity.self, data: customAttributeEntityData) XCTAssertEqual(entity[\.firstName], "Cool") + XCTAssertEqual(entity.firstName, "Cool") XCTAssertEqual(entity[\.name], "Cool Name") + XCTAssertEqual(entity.name, "Cool Name") XCTAssertNoThrow(try CustomAttributeEntity.check(entity)) } @@ -27,7 +29,9 @@ class CustomAttributesTests: XCTestCase { let entity = decoded(type: CustomKeysEntity.self, data: customAttributeEntityData) XCTAssertEqual(entity[\.firstNameSilly], "Cool") + XCTAssertEqual(entity.firstNameSilly, "Cool") XCTAssertEqual(entity[\.lastNameSilly], "Name") + XCTAssertEqual(entity.lastNameSilly, "Name") XCTAssertNoThrow(try CustomKeysEntity.check(entity)) } diff --git a/Tests/JSONAPITests/Entity/EntityTests.swift b/Tests/JSONAPITests/ResourceObject/ResourceObjectTests.swift similarity index 93% rename from Tests/JSONAPITests/Entity/EntityTests.swift rename to Tests/JSONAPITests/ResourceObject/ResourceObjectTests.swift index 5abecae..ba3cd47 100644 --- a/Tests/JSONAPITests/Entity/EntityTests.swift +++ b/Tests/JSONAPITests/ResourceObject/ResourceObjectTests.swift @@ -1,5 +1,5 @@ // -// EntityTests.swift +// ResourceObjectTests.swift // JSONAPITests // // Created by Mathew Polzin on 7/25/18. @@ -9,7 +9,7 @@ import XCTest import JSONAPI import JSONAPITesting -class EntityTests: XCTestCase { +class ResourceObjectTests: XCTestCase { func test_relationship_access() { let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none) @@ -69,6 +69,7 @@ class EntityTests: XCTestCase { let entity = UnidentifiedTestEntity(attributes: .init(me: "hello"), relationships: .none, meta: .none, links: .none) XCTAssertEqual(entity[\.me], "hello") + XCTAssertEqual(entity.me, "hello") } func test_initialization() { @@ -102,7 +103,7 @@ class EntityTests: XCTestCase { } // MARK: - Identifying entity copies -extension EntityTests { +extension ResourceObjectTests { func test_copyIdentifiedByType() { let unidentifiedEntity = UnidentifiedTestEntity(attributes: .init(me: .init(value: "hello")), relationships: .none, meta: .none, links: .none) @@ -132,7 +133,7 @@ extension EntityTests { } // MARK: - Encode/Decode -extension EntityTests { +extension ResourceObjectTests { func test_EntityNoRelationshipsNoAttributes() { let entity = decoded(type: TestEntity1.self, @@ -157,6 +158,7 @@ extension EntityTests { XCTAssert(type(of: entity.relationships) == NoRelationships.self) XCTAssertEqual(entity[\.floater], 123.321) + XCTAssertEqual(entity.floater, 123.321) XCTAssertNoThrow(try TestEntity5.check(entity)) testEncoded(entity: entity) @@ -189,7 +191,9 @@ extension EntityTests { data: entity_some_relationships_some_attributes) XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity.word, "coolio") XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual(entity.number, 992299) XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertNoThrow(try TestEntity4.check(entity)) @@ -203,15 +207,18 @@ extension EntityTests { } // MARK: Attribute omission and nullification -extension EntityTests { +extension ResourceObjectTests { func test_entityOneOmittedAttribute() { let entity = decoded(type: TestEntity6.self, data: entity_one_omitted_attribute) XCTAssertEqual(entity[\.here], "Hello") + XCTAssertEqual(entity.here, "Hello") XCTAssertNil(entity[\.maybeHere]) + XCTAssertNil(entity.maybeHere) XCTAssertEqual(entity[\.maybeNull], "World") + XCTAssertEqual(entity.maybeNull, "World") XCTAssertNoThrow(try TestEntity6.check(entity)) testEncoded(entity: entity) @@ -227,8 +234,11 @@ extension EntityTests { data: entity_one_null_attribute) XCTAssertEqual(entity[\.here], "Hello") + XCTAssertEqual(entity.here, "Hello") XCTAssertEqual(entity[\.maybeHere], "World") + XCTAssertEqual(entity.maybeHere, "World") XCTAssertNil(entity[\.maybeNull]) + XCTAssertNil(entity.maybeNull) XCTAssertNoThrow(try TestEntity6.check(entity)) testEncoded(entity: entity) @@ -244,8 +254,11 @@ extension EntityTests { data: entity_all_attributes) XCTAssertEqual(entity[\.here], "Hello") + XCTAssertEqual(entity.here, "Hello") XCTAssertEqual(entity[\.maybeHere], "World") + XCTAssertEqual(entity.maybeHere, "World") XCTAssertEqual(entity[\.maybeNull], "!") + XCTAssertEqual(entity.maybeNull, "!") XCTAssertNoThrow(try TestEntity6.check(entity)) testEncoded(entity: entity) @@ -261,8 +274,11 @@ extension EntityTests { data: entity_one_null_and_one_missing_attribute) XCTAssertEqual(entity[\.here], "Hello") + XCTAssertEqual(entity.here, "Hello") XCTAssertNil(entity[\.maybeHere]) + XCTAssertNil(entity.maybeHere) XCTAssertNil(entity[\.maybeNull]) + XCTAssertNil(entity.maybeNull) XCTAssertNoThrow(try TestEntity6.check(entity)) testEncoded(entity: entity) @@ -283,7 +299,9 @@ extension EntityTests { data: entity_null_optional_nullable_attribute) XCTAssertEqual(entity[\.here], "Hello") + XCTAssertEqual(entity.here, "Hello") XCTAssertNil(entity[\.maybeHereMaybeNull]) + XCTAssertNil(entity.maybeHereMaybeNull) XCTAssertNoThrow(try TestEntity7.check(entity)) testEncoded(entity: entity) @@ -299,7 +317,9 @@ extension EntityTests { data: entity_non_null_optional_nullable_attribute) XCTAssertEqual(entity[\.here], "Hello") + XCTAssertEqual(entity.here, "Hello") XCTAssertEqual(entity[\.maybeHereMaybeNull], "World") + XCTAssertEqual(entity.maybeHereMaybeNull, "World") XCTAssertNoThrow(try TestEntity7.check(entity)) testEncoded(entity: entity) @@ -312,17 +332,23 @@ extension EntityTests { } // MARK: Attribute Transformation -extension EntityTests { +extension ResourceObjectTests { func test_IntToString() { let entity = decoded(type: TestEntity8.self, data: entity_int_to_string_attribute) XCTAssertEqual(entity[\.string], "22") + XCTAssertEqual(entity.string, "22") XCTAssertEqual(entity[\.int], 22) + XCTAssertEqual(entity.int, 22) XCTAssertEqual(entity[\.stringFromInt], "22") + XCTAssertEqual(entity.stringFromInt, "22") XCTAssertEqual(entity[\.plus], 122) + XCTAssertEqual(entity.plus, 122) XCTAssertEqual(entity[\.doubleFromInt], 22.0) + XCTAssertEqual(entity.doubleFromInt, 22.0) XCTAssertEqual(entity[\.nullToString], "nil") + XCTAssertEqual(entity.nullToString, "nil") XCTAssertNoThrow(try TestEntity8.check(entity)) testEncoded(entity: entity) @@ -335,7 +361,7 @@ extension EntityTests { } // MARK: Attribute Validation -extension EntityTests { +extension ResourceObjectTests { func test_IntOver10_success() { XCTAssertNoThrow(decoded(type: TestEntity11.self, data: entity_valid_validated_attribute)) } @@ -350,7 +376,7 @@ extension EntityTests { } // MARK: Relationship omission and nullification -extension EntityTests { +extension ResourceObjectTests { func test_nullableRelationshipNotNullOrOmitted() { let entity = decoded(type: TestEntity9.self, data: entity_optional_not_omitted_relationship) @@ -451,7 +477,7 @@ extension EntityTests { // MARK: Relationships of same type as root entity -extension EntityTests { +extension ResourceObjectTests { func test_RleationshipsOfSameType() { let entity = decoded(type: TestEntity10.self, data: entity_self_ref_relationship) @@ -470,12 +496,13 @@ extension EntityTests { // MARK: Unidentified -extension EntityTests { +extension ResourceObjectTests { func test_UnidentifiedEntity() { let entity = decoded(type: UnidentifiedTestEntity.self, data: entity_unidentified) XCTAssertNil(entity[\.me]) + XCTAssertNil(entity.me) XCTAssertEqual(entity.id, .unidentified) XCTAssertNoThrow(try UnidentifiedTestEntity.check(entity)) @@ -492,6 +519,7 @@ extension EntityTests { data: entity_unidentified_with_attributes) XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.me, "unknown") XCTAssertEqual(entity.id, .unidentified) XCTAssertNoThrow(try UnidentifiedTestEntity.check(entity)) @@ -506,12 +534,13 @@ extension EntityTests { // MARK: With Meta and/or Links -extension EntityTests { +extension ResourceObjectTests { func test_UnidentifiedEntityWithAttributesAndMeta() { let entity = decoded(type: UnidentifiedTestEntityWithMeta.self, data: entity_unidentified_with_attributes_and_meta) XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.me, "unknown") XCTAssertEqual(entity.id, .unidentified) XCTAssertEqual(entity.meta.x, "world") XCTAssertEqual(entity.meta.y, 5) @@ -530,6 +559,7 @@ extension EntityTests { data: entity_unidentified_with_attributes_and_links) XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.me, "unknown") XCTAssertEqual(entity.id, .unidentified) XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) XCTAssertNoThrow(try UnidentifiedTestEntityWithLinks.check(entity)) @@ -547,6 +577,7 @@ extension EntityTests { data: entity_unidentified_with_attributes_and_meta_and_links) XCTAssertEqual(entity[\.me], "unknown") + XCTAssertEqual(entity.me, "unknown") XCTAssertEqual(entity.id, .unidentified) XCTAssertEqual(entity.meta.x, "world") XCTAssertEqual(entity.meta.y, 5) @@ -566,7 +597,9 @@ extension EntityTests { data: entity_some_relationships_some_attributes_with_meta) XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity.word, "coolio") XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual(entity.number, 992299) XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertEqual(entity.meta.x, "world") XCTAssertEqual(entity.meta.y, 5) @@ -585,7 +618,9 @@ extension EntityTests { data: entity_some_relationships_some_attributes_with_links) XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity.word, "coolio") XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual(entity.number, 992299) XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertEqual(entity.links.link1, .init(url: "https://image.com/image.png")) XCTAssertNoThrow(try TestEntity4WithLinks.check(entity)) @@ -603,7 +638,9 @@ extension EntityTests { data: entity_some_relationships_some_attributes_with_meta_and_links) XCTAssertEqual(entity[\.word], "coolio") + XCTAssertEqual(entity.word, "coolio") XCTAssertEqual(entity[\.number], 992299) + XCTAssertEqual(entity.number, 992299) XCTAssertEqual((entity ~> \.other).rawValue, "2DF03B69-4B0A-467F-B52E-B0C9E44FCECF") XCTAssertEqual(entity.meta.x, "world") XCTAssertEqual(entity.meta.y, 5) @@ -621,7 +658,7 @@ extension EntityTests { // MARK: With a Meta Attribute -extension EntityTests { +extension ResourceObjectTests { func test_MetaEntityAttributeAccessWorks() { let entity1 = TestEntityWithMetaAttribute(id: "even", attributes: .init(), @@ -635,13 +672,15 @@ extension EntityTests { links: .none) XCTAssertEqual(entity1[\.metaAttribute], true) + XCTAssertEqual(entity1.metaAttribute, true) XCTAssertEqual(entity2[\.metaAttribute], false) + XCTAssertEqual(entity2.metaAttribute, false) } } // MARK: With a Meta Relationship -extension EntityTests { +extension ResourceObjectTests { func test_MetaEntityRelationshipAccessWorks() { let entity1 = TestEntityWithMetaRelationship(id: "even", attributes: .none, @@ -654,7 +693,7 @@ extension EntityTests { } // MARK: - Test Types -extension EntityTests { +extension ResourceObjectTests { enum TestEntityType1: ResourceObjectDescription { static var jsonType: String { return "test_entities"} diff --git a/Tests/JSONAPITests/Entity/stubs/EntityStubs.swift b/Tests/JSONAPITests/ResourceObject/stubs/ResourceObjectStubs.swift similarity index 100% rename from Tests/JSONAPITests/Entity/stubs/EntityStubs.swift rename to Tests/JSONAPITests/ResourceObject/stubs/ResourceObjectStubs.swift From 78b9b125921fc1e506f0717fc5f53d2d776a9543 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Jul 2019 09:23:01 -0700 Subject: [PATCH 4/8] Update README --- README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 993e727..d2859e4 100644 --- a/README.md +++ b/README.md @@ -311,11 +311,18 @@ A resource object that does not have attributes can be described by adding the f typealias Attributes = NoAttributes ``` -`Attributes` can be accessed via the `subscript` operator of the `ResourceObject` type as follows: +As of Swift 5.1, `Attributes` can be accessed via dynamic member keypath lookup as follows: +```swift +let favoriteColor: String = person.favoriteColor +``` + +🗒 `Attributes` can also be accessed via the older `subscript` operator as follows: ```swift let favoriteColor: String = person[\.favoriteColor] ``` +In both cases you retain type-safety, although neither plays particularly nicely with code autocompletion. It is best practice to pick an attribute access syntax and stick with it. At some point in the future the syntax deemed less desirable may be deprecated. + #### `Transformer` Sometimes you need to use a type that does not encode or decode itself in the way you need to represent it as a serialized JSON object. For example, the Swift `Foundation` type `Date` can encode/decode itself to `Double` out of the box, but you might want to represent dates as ISO 8601 compliant `String`s instead. The Foundation library `JSONDecoder` has a setting to make this adjustment, but for the sake of an example, you could create a `Transformer`. @@ -354,7 +361,7 @@ You can also creator `Validators` and `ValidatedAttribute`s. A `Validator` is ju #### Computed `Attribute` -You can add computed properties to your `ResourceObjectDescription.Attributes` struct if you would like to expose attributes that are not explicitly represented by the JSON. These computed properties do not have to be wrapped in `Attribute`, `ValidatedAttribute`, or `TransformedAttribute`. This allows computed attributes to be of types that are not `Codable`. Here's an example of how you might take the `person[\.name]` attribute from the example above and create a `fullName` computed property. +You can add computed properties to your `ResourceObjectDescription.Attributes` struct if you would like to expose attributes that are not explicitly represented by the JSON. These computed properties do not have to be wrapped in `Attribute`, `ValidatedAttribute`, or `TransformedAttribute`. This allows computed attributes to be of types that are not `Codable`. Here's an example of how you might take the `person.name` attribute from the example above and create a `fullName` computed property. ```swift public var fullName: Attribute { @@ -362,7 +369,7 @@ public var fullName: Attribute { } ``` -If your computed property is wrapped in a `AttributeType` then you can still use the default subscript operator to access it (as would be the case with the `person[\.fullName]` example above). However, if you add a property to the `Attributes` `struct` that is not wrapped in an `AttributeType`, you must either access it from its full path (`person.attributes.newThing`) or with the "direct" subscript accessor (`person[direct: \.newThing]`). This keeps the subscript access unambiguous enough for the compiler to be helpful prior to explicitly casting, comparing, or storing the result. +If your computed property is wrapped in a `AttributeType` then you can still use the default subscript operator to access it (as would be the case with the `person.fullName` example above). However, if you add a property to the `Attributes` `struct` that is not wrapped in an `AttributeType`, you must either access it from its full path (`person.attributes.newThing`) or with the "direct" subscript accessor (`person[direct: \.newThing]`). This keeps the subscript access unambiguous enough for the compiler to be helpful prior to explicitly casting, comparing, or storing the result. ### Copying/Mutating `ResourceObjects` `ResourceObject` is a value type, so copying is its default behavior. There are two common mutations you might want to make when copying a `ResourceObject`: @@ -599,7 +606,7 @@ typealias User = JSONAPI.ResourceObject {Value}` where `{ResourceObject}` is the `JSONAPI.ResourceObject` described by the `ResourceObjectDescription` containing the meta-attribute. @@ -620,7 +627,7 @@ enum UserDescription: ResourceObjectDescription { struct Relationships: JSONAPI.Relationships { public var friend: (User) -> User.Identifier { return { user in - return User.Identifier(rawValue: user[\.friend_id]) + return User.Identifier(rawValue: user.friend_id) } } } From 569cec05cfc639f9c0a35da9ebf2b2515db58e74 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Jul 2019 09:27:56 -0700 Subject: [PATCH 5/8] ditch language version argument in package file that is not accepted by swift test. update linuxmain. --- Package.swift | 6 +- Tests/JSONAPITests/XCTestManifests.swift | 156 +++++++++++------------ 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/Package.swift b/Package.swift index d4138a9..f88e67d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -33,6 +33,6 @@ let package = Package( .testTarget( name: "JSONAPITestingTests", dependencies: ["JSONAPI", "JSONAPITesting"]) - ], - swiftLanguageVersions: [.version("5.1")] + ] +// swiftLanguageVersions: [.version("5.1")] ) diff --git a/Tests/JSONAPITests/XCTestManifests.swift b/Tests/JSONAPITests/XCTestManifests.swift index a7b9e90..2cb8f39 100644 --- a/Tests/JSONAPITests/XCTestManifests.swift +++ b/Tests/JSONAPITests/XCTestManifests.swift @@ -172,83 +172,6 @@ extension DocumentTests { ] } -extension EntityTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__EntityTests = [ - ("test_copyIdentifiedByType", test_copyIdentifiedByType), - ("test_copyIdentifiedByValue", test_copyIdentifiedByValue), - ("test_copyWithNewId", test_copyWithNewId), - ("test_entityAllAttribute", test_entityAllAttribute), - ("test_entityAllAttribute_encode", test_entityAllAttribute_encode), - ("test_entityBrokenNullableOmittedAttribute", test_entityBrokenNullableOmittedAttribute), - ("test_EntityNoRelationshipsNoAttributes", test_EntityNoRelationshipsNoAttributes), - ("test_EntityNoRelationshipsNoAttributes_encode", test_EntityNoRelationshipsNoAttributes_encode), - ("test_EntityNoRelationshipsSomeAttributes", test_EntityNoRelationshipsSomeAttributes), - ("test_EntityNoRelationshipsSomeAttributes_encode", test_EntityNoRelationshipsSomeAttributes_encode), - ("test_entityOneNullAndOneOmittedAttribute", test_entityOneNullAndOneOmittedAttribute), - ("test_entityOneNullAndOneOmittedAttribute_encode", test_entityOneNullAndOneOmittedAttribute_encode), - ("test_entityOneNullAttribute", test_entityOneNullAttribute), - ("test_entityOneNullAttribute_encode", test_entityOneNullAttribute_encode), - ("test_entityOneOmittedAttribute", test_entityOneOmittedAttribute), - ("test_entityOneOmittedAttribute_encode", test_entityOneOmittedAttribute_encode), - ("test_EntitySomeRelationshipsNoAttributes", test_EntitySomeRelationshipsNoAttributes), - ("test_EntitySomeRelationshipsNoAttributes_encode", test_EntitySomeRelationshipsNoAttributes_encode), - ("test_EntitySomeRelationshipsSomeAttributes", test_EntitySomeRelationshipsSomeAttributes), - ("test_EntitySomeRelationshipsSomeAttributes_encode", test_EntitySomeRelationshipsSomeAttributes_encode), - ("test_EntitySomeRelationshipsSomeAttributesWithLinks", test_EntitySomeRelationshipsSomeAttributesWithLinks), - ("test_EntitySomeRelationshipsSomeAttributesWithLinks_encode", test_EntitySomeRelationshipsSomeAttributesWithLinks_encode), - ("test_EntitySomeRelationshipsSomeAttributesWithMeta", test_EntitySomeRelationshipsSomeAttributesWithMeta), - ("test_EntitySomeRelationshipsSomeAttributesWithMeta_encode", test_EntitySomeRelationshipsSomeAttributesWithMeta_encode), - ("test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks", test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks), - ("test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks_encode", test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks_encode), - ("test_initialization", test_initialization), - ("test_IntOver10_encode", test_IntOver10_encode), - ("test_IntOver10_failure", test_IntOver10_failure), - ("test_IntOver10_success", test_IntOver10_success), - ("test_IntToString", test_IntToString), - ("test_IntToString_encode", test_IntToString_encode), - ("test_MetaEntityAttributeAccessWorks", test_MetaEntityAttributeAccessWorks), - ("test_MetaEntityRelationshipAccessWorks", test_MetaEntityRelationshipAccessWorks), - ("test_NonNullOptionalNullableAttribute", test_NonNullOptionalNullableAttribute), - ("test_NonNullOptionalNullableAttribute_encode", test_NonNullOptionalNullableAttribute_encode), - ("test_nullableRelationshipIsNull", test_nullableRelationshipIsNull), - ("test_nullableRelationshipIsNull_encode", test_nullableRelationshipIsNull_encode), - ("test_nullableRelationshipNotNull", test_nullableRelationshipNotNull), - ("test_nullableRelationshipNotNull_encode", test_nullableRelationshipNotNull_encode), - ("test_nullableRelationshipNotNullOrOmitted", test_nullableRelationshipNotNullOrOmitted), - ("test_nullableRelationshipNotNullOrOmitted_encode", test_nullableRelationshipNotNullOrOmitted_encode), - ("test_NullOptionalNullableAttribute", test_NullOptionalNullableAttribute), - ("test_NullOptionalNullableAttribute_encode", test_NullOptionalNullableAttribute_encode), - ("test_optional_relationship_operator_access", test_optional_relationship_operator_access), - ("test_optionalNullableRelationshipNulled", test_optionalNullableRelationshipNulled), - ("test_optionalNullableRelationshipNulled_encode", test_optionalNullableRelationshipNulled_encode), - ("test_optionalNullableRelationshipOmitted", test_optionalNullableRelationshipOmitted), - ("test_optionalToMany_relationship_opeartor_access", test_optionalToMany_relationship_opeartor_access), - ("test_optionalToManyIsNotOmitted", test_optionalToManyIsNotOmitted), - ("test_optionalToManyIsNotOmitted_encode", test_optionalToManyIsNotOmitted_encode), - ("test_pointerWithMetaAndLinks", test_pointerWithMetaAndLinks), - ("test_relationship_access", test_relationship_access), - ("test_relationship_operator_access", test_relationship_operator_access), - ("test_relationshipIds", test_relationshipIds), - ("test_RleationshipsOfSameType", test_RleationshipsOfSameType), - ("test_RleationshipsOfSameType_encode", test_RleationshipsOfSameType_encode), - ("test_toMany_relationship_operator_access", test_toMany_relationship_operator_access), - ("test_UnidentifiedEntity", test_UnidentifiedEntity), - ("test_UnidentifiedEntity_encode", test_UnidentifiedEntity_encode), - ("test_unidentifiedEntityAttributeAccess", test_unidentifiedEntityAttributeAccess), - ("test_UnidentifiedEntityWithAttributes", test_UnidentifiedEntityWithAttributes), - ("test_UnidentifiedEntityWithAttributes_encode", test_UnidentifiedEntityWithAttributes_encode), - ("test_UnidentifiedEntityWithAttributesAndLinks", test_UnidentifiedEntityWithAttributesAndLinks), - ("test_UnidentifiedEntityWithAttributesAndLinks_encode", test_UnidentifiedEntityWithAttributesAndLinks_encode), - ("test_UnidentifiedEntityWithAttributesAndMeta", test_UnidentifiedEntityWithAttributesAndMeta), - ("test_UnidentifiedEntityWithAttributesAndMeta_encode", test_UnidentifiedEntityWithAttributesAndMeta_encode), - ("test_UnidentifiedEntityWithAttributesAndMetaAndLinks", test_UnidentifiedEntityWithAttributesAndMetaAndLinks), - ("test_UnidentifiedEntityWithAttributesAndMetaAndLinks_encode", test_UnidentifiedEntityWithAttributesAndMetaAndLinks_encode), - ] -} - extension IncludedTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -407,6 +330,83 @@ extension ResourceBodyTests { ] } +extension ResourceObjectTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ResourceObjectTests = [ + ("test_copyIdentifiedByType", test_copyIdentifiedByType), + ("test_copyIdentifiedByValue", test_copyIdentifiedByValue), + ("test_copyWithNewId", test_copyWithNewId), + ("test_entityAllAttribute", test_entityAllAttribute), + ("test_entityAllAttribute_encode", test_entityAllAttribute_encode), + ("test_entityBrokenNullableOmittedAttribute", test_entityBrokenNullableOmittedAttribute), + ("test_EntityNoRelationshipsNoAttributes", test_EntityNoRelationshipsNoAttributes), + ("test_EntityNoRelationshipsNoAttributes_encode", test_EntityNoRelationshipsNoAttributes_encode), + ("test_EntityNoRelationshipsSomeAttributes", test_EntityNoRelationshipsSomeAttributes), + ("test_EntityNoRelationshipsSomeAttributes_encode", test_EntityNoRelationshipsSomeAttributes_encode), + ("test_entityOneNullAndOneOmittedAttribute", test_entityOneNullAndOneOmittedAttribute), + ("test_entityOneNullAndOneOmittedAttribute_encode", test_entityOneNullAndOneOmittedAttribute_encode), + ("test_entityOneNullAttribute", test_entityOneNullAttribute), + ("test_entityOneNullAttribute_encode", test_entityOneNullAttribute_encode), + ("test_entityOneOmittedAttribute", test_entityOneOmittedAttribute), + ("test_entityOneOmittedAttribute_encode", test_entityOneOmittedAttribute_encode), + ("test_EntitySomeRelationshipsNoAttributes", test_EntitySomeRelationshipsNoAttributes), + ("test_EntitySomeRelationshipsNoAttributes_encode", test_EntitySomeRelationshipsNoAttributes_encode), + ("test_EntitySomeRelationshipsSomeAttributes", test_EntitySomeRelationshipsSomeAttributes), + ("test_EntitySomeRelationshipsSomeAttributes_encode", test_EntitySomeRelationshipsSomeAttributes_encode), + ("test_EntitySomeRelationshipsSomeAttributesWithLinks", test_EntitySomeRelationshipsSomeAttributesWithLinks), + ("test_EntitySomeRelationshipsSomeAttributesWithLinks_encode", test_EntitySomeRelationshipsSomeAttributesWithLinks_encode), + ("test_EntitySomeRelationshipsSomeAttributesWithMeta", test_EntitySomeRelationshipsSomeAttributesWithMeta), + ("test_EntitySomeRelationshipsSomeAttributesWithMeta_encode", test_EntitySomeRelationshipsSomeAttributesWithMeta_encode), + ("test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks", test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks), + ("test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks_encode", test_EntitySomeRelationshipsSomeAttributesWithMetaAndLinks_encode), + ("test_initialization", test_initialization), + ("test_IntOver10_encode", test_IntOver10_encode), + ("test_IntOver10_failure", test_IntOver10_failure), + ("test_IntOver10_success", test_IntOver10_success), + ("test_IntToString", test_IntToString), + ("test_IntToString_encode", test_IntToString_encode), + ("test_MetaEntityAttributeAccessWorks", test_MetaEntityAttributeAccessWorks), + ("test_MetaEntityRelationshipAccessWorks", test_MetaEntityRelationshipAccessWorks), + ("test_NonNullOptionalNullableAttribute", test_NonNullOptionalNullableAttribute), + ("test_NonNullOptionalNullableAttribute_encode", test_NonNullOptionalNullableAttribute_encode), + ("test_nullableRelationshipIsNull", test_nullableRelationshipIsNull), + ("test_nullableRelationshipIsNull_encode", test_nullableRelationshipIsNull_encode), + ("test_nullableRelationshipNotNull", test_nullableRelationshipNotNull), + ("test_nullableRelationshipNotNull_encode", test_nullableRelationshipNotNull_encode), + ("test_nullableRelationshipNotNullOrOmitted", test_nullableRelationshipNotNullOrOmitted), + ("test_nullableRelationshipNotNullOrOmitted_encode", test_nullableRelationshipNotNullOrOmitted_encode), + ("test_NullOptionalNullableAttribute", test_NullOptionalNullableAttribute), + ("test_NullOptionalNullableAttribute_encode", test_NullOptionalNullableAttribute_encode), + ("test_optional_relationship_operator_access", test_optional_relationship_operator_access), + ("test_optionalNullableRelationshipNulled", test_optionalNullableRelationshipNulled), + ("test_optionalNullableRelationshipNulled_encode", test_optionalNullableRelationshipNulled_encode), + ("test_optionalNullableRelationshipOmitted", test_optionalNullableRelationshipOmitted), + ("test_optionalToMany_relationship_opeartor_access", test_optionalToMany_relationship_opeartor_access), + ("test_optionalToManyIsNotOmitted", test_optionalToManyIsNotOmitted), + ("test_optionalToManyIsNotOmitted_encode", test_optionalToManyIsNotOmitted_encode), + ("test_pointerWithMetaAndLinks", test_pointerWithMetaAndLinks), + ("test_relationship_access", test_relationship_access), + ("test_relationship_operator_access", test_relationship_operator_access), + ("test_relationshipIds", test_relationshipIds), + ("test_RleationshipsOfSameType", test_RleationshipsOfSameType), + ("test_RleationshipsOfSameType_encode", test_RleationshipsOfSameType_encode), + ("test_toMany_relationship_operator_access", test_toMany_relationship_operator_access), + ("test_UnidentifiedEntity", test_UnidentifiedEntity), + ("test_UnidentifiedEntity_encode", test_UnidentifiedEntity_encode), + ("test_unidentifiedEntityAttributeAccess", test_unidentifiedEntityAttributeAccess), + ("test_UnidentifiedEntityWithAttributes", test_UnidentifiedEntityWithAttributes), + ("test_UnidentifiedEntityWithAttributes_encode", test_UnidentifiedEntityWithAttributes_encode), + ("test_UnidentifiedEntityWithAttributesAndLinks", test_UnidentifiedEntityWithAttributesAndLinks), + ("test_UnidentifiedEntityWithAttributesAndLinks_encode", test_UnidentifiedEntityWithAttributesAndLinks_encode), + ("test_UnidentifiedEntityWithAttributesAndMeta", test_UnidentifiedEntityWithAttributesAndMeta), + ("test_UnidentifiedEntityWithAttributesAndMeta_encode", test_UnidentifiedEntityWithAttributesAndMeta_encode), + ("test_UnidentifiedEntityWithAttributesAndMetaAndLinks", test_UnidentifiedEntityWithAttributesAndMetaAndLinks), + ("test_UnidentifiedEntityWithAttributesAndMetaAndLinks_encode", test_UnidentifiedEntityWithAttributesAndMetaAndLinks_encode), + ] +} + public func __allTests() -> [XCTestCaseEntry] { return [ testCase(APIDescriptionTests.__allTests__APIDescriptionTests), @@ -415,7 +415,6 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(ComputedPropertiesTests.__allTests__ComputedPropertiesTests), testCase(CustomAttributesTests.__allTests__CustomAttributesTests), testCase(DocumentTests.__allTests__DocumentTests), - testCase(EntityTests.__allTests__EntityTests), testCase(IncludedTests.__allTests__IncludedTests), testCase(LinksTests.__allTests__LinksTests), testCase(NonJSONAPIRelatableTests.__allTests__NonJSONAPIRelatableTests), @@ -423,6 +422,7 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(PolyTests.__allTests__PolyTests), testCase(RelationshipTests.__allTests__RelationshipTests), testCase(ResourceBodyTests.__allTests__ResourceBodyTests), + testCase(ResourceObjectTests.__allTests__ResourceObjectTests), ] } #endif From fb72817de6eed8976b0a6eb7ccbc849f578395d9 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 25 Jul 2019 09:36:35 -0700 Subject: [PATCH 6/8] bump swift version badge to swift 5.1 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2859e4..2613c5a 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # JSONAPI -[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 5.0](http://img.shields.io/badge/Swift-5.0-blue.svg)](https://swift.org) [![Build Status](https://app.bitrise.io/app/c8295b9589aa401e/status.svg?token=vzcyqWD5bQ4xqQfZsaVzNw&branch=master)](https://app.bitrise.io/app/c8295b9589aa401e) +[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 5.1](http://img.shields.io/badge/Swift-5.1-blue.svg)](https://swift.org) [![Build Status](https://app.bitrise.io/app/c8295b9589aa401e/status.svg?token=vzcyqWD5bQ4xqQfZsaVzNw&branch=master)](https://app.bitrise.io/app/c8295b9589aa401e) A Swift package for encoding to- and decoding from **JSON API** compliant requests and responses. See the JSON API Spec here: https://jsonapi.org/format/ -:warning: Although I find the type-safety of this framework appealing, the Swift compiler currently has enough trouble with it that it can become difficult to reason about errors produced by small typos. Similarly, auto-complete fails to provide reasonable suggestions much of the time. If you get the code right, everything compiles, otherwise it can suck to figure out what is wrong. This is mostly a concern when creating resource objects in-code (servers and test suites must do this). Writing a client that uses this framework to ingest JSON API Compliant API responses is much less painful. :warning: +:warning: Although I find the type-safety of this framework appealing, the Swift compiler currently has enough trouble with it that it can become difficult to reason about errors produced by small typos. Similarly, auto-complete fails to provide reasonable suggestions much of the time. If you get the code right, everything compiles, otherwise it can suck to figure out what is wrong. This is mostly a concern when creating resource objects in-code (servers and test suites must do this). Writing a client that uses this framework to ingest JSON API Compliant API responses is much less painful. Note that this is a compile-time concern -- test coverage of this library's behavior is very good. :warning: ## Table of Contents From ff06c36b362c0f5695b02dfb150ea17ee58739e1 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 5 Sep 2019 22:22:55 -0700 Subject: [PATCH 7/8] uncomment out language version declaration in package manifest, 'fix' indentation. --- Package.swift | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Package.swift b/Package.swift index f88e67d..33d743d 100644 --- a/Package.swift +++ b/Package.swift @@ -6,33 +6,33 @@ import PackageDescription let package = Package( name: "JSONAPI", platforms: [ - .macOS(.v10_10), - .iOS(.v10) + .macOS(.v10_10), + .iOS(.v10) ], products: [ .library( name: "JSONAPI", targets: ["JSONAPI"]), - .library( - name: "JSONAPITesting", - targets: ["JSONAPITesting"]) + .library( + name: "JSONAPITesting", + targets: ["JSONAPITesting"]) ], dependencies: [ - .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.0.0")), + .package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.0.0")), ], targets: [ .target( name: "JSONAPI", dependencies: ["Poly"]), - .target( - name: "JSONAPITesting", - dependencies: ["JSONAPI"]), + .target( + name: "JSONAPITesting", + dependencies: ["JSONAPI"]), .testTarget( name: "JSONAPITests", dependencies: ["JSONAPI", "JSONAPITesting"]), .testTarget( name: "JSONAPITestingTests", dependencies: ["JSONAPI", "JSONAPITesting"]) - ] -// swiftLanguageVersions: [.version("5.1")] + ], + swiftLanguageVersions: [.v5] ) From 3ff1b867cab4ca77fa0f409096f906c720ad12da Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 14 Sep 2019 15:53:48 -0700 Subject: [PATCH 8/8] Bump spec version to account for breaking changes with configs and swift tools version --- JSONAPI.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/JSONAPI.podspec b/JSONAPI.podspec index f5cac91..caad3a6 100644 --- a/JSONAPI.podspec +++ b/JSONAPI.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "MP-JSONAPI" - spec.version = "1.0.0" + spec.version = "2.0.0" spec.summary = "Swift Codable JSON API framework." # This description is used to generate tags and improve search results. @@ -131,7 +131,7 @@ See the JSON API Spec here: https://jsonapi.org/format/ # where they will only apply to your library. If you depend on other Podspecs # you can include multiple dependencies to ensure it works. - spec.swift_version = "5.0" + spec.swift_version = "5.1" spec.module_name = "JSONAPI" # spec.requires_arc = true