From a628992fcb4e70e7596950f66dc757de7c558549 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 28 Nov 2018 09:09:23 -0800 Subject: [PATCH] Make Attribute a Functor to make computed attributes easier to write. --- .../JSONAPI/Resource/Attribute+Functor.swift | 21 ++++++ .../Attribute/Attribute+FunctorTests.swift | 70 +++++++++++++++++++ .../ComputedPropertiesTests.swift | 4 +- 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 Sources/JSONAPI/Resource/Attribute+Functor.swift create mode 100644 Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift diff --git a/Sources/JSONAPI/Resource/Attribute+Functor.swift b/Sources/JSONAPI/Resource/Attribute+Functor.swift new file mode 100644 index 0000000..39f2a81 --- /dev/null +++ b/Sources/JSONAPI/Resource/Attribute+Functor.swift @@ -0,0 +1,21 @@ +// +// Attribute+Functor.swift +// JSONAPI +// +// Created by Mathew Polzin on 11/28/18. +// + +public extension TransformedAttribute { + /// Map an Attribute to a new wrapped type. + /// Note that the resulting Attribute will have no transformer, even if the + /// source Attribute has a transformer. + /// You are mapping the output of the source transform into + /// the RawValue of a new transformerless Attribute. + /// + /// Generally, this is the most useful operation. The transformer gives you + /// control over the decoding of the Attribute, but once the Attribute exists, + /// mapping on it is most useful for creating computed Attribute properties. + public func map(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute { + return Attribute(value: try transform(value)) + } +} diff --git a/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift b/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift new file mode 100644 index 0000000..c6dfc55 --- /dev/null +++ b/Tests/JSONAPITests/Attribute/Attribute+FunctorTests.swift @@ -0,0 +1,70 @@ +// +// Attribute+FunctorTests.swift +// JSONAPITests +// +// Created by Mathew Polzin on 11/28/18. +// + +import XCTest +import JSONAPI +import JSONAPITestLib + +class Attribute_FunctorTests: XCTestCase { + func test_mapGuaranteed() { + let entity = try? TestType(attributes: .init(name: "Frankie", number: .init(rawValue: 22.0))) + + XCTAssertNotNil(entity) + + XCTAssertEqual(entity?[\.computedString], "Frankie2") + } + + func test_mapOptionalSuccess() { + let entity = try? TestType(attributes: .init(name: "Frankie", number: .init(rawValue: 22.0))) + + XCTAssertNotNil(entity) + + XCTAssertEqual(entity?[\.computedNumber], 22) + } + + func test_mapOptionalFailure() { + let entity = try? TestType(attributes: .init(name: "Frankie", number: .init(rawValue: 22.5))) + + XCTAssertNotNil(entity) + + XCTAssertNil(entity?[\.computedNumber]) + } +} + +// MARK: Test types +extension Attribute_FunctorTests { + enum TestTypeDescription: EntityDescription { + public static var type: String { return "test" } + + public struct Attributes: JSONAPI.Attributes { + let name: Attribute + let number: TransformedAttribute + var computedString: Attribute { + return name.map { $0 + "2" } + } + var computedNumber: Attribute? { + return try? number.map { string in + let num = Double(string).flatMap { Int(exactly: $0) } + guard let ret = num else { + throw DecodingError.typeMismatch(Int.self, .init(codingPath: [], debugDescription: "String was not an Int.")) + } + return ret + } + } + } + + public typealias Relationships = NoRelationships + } + + typealias TestType = Entity + + enum DoubleToString: Transformer { + public static func transform(_ from: Double) -> String { + return String(from) + } + } +} diff --git a/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift b/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift index 5484214..a72b282 100644 --- a/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift +++ b/Tests/JSONAPITests/Computed Properties/ComputedPropertiesTests.swift @@ -26,7 +26,7 @@ class ComputedPropertiesTests: XCTestCase { func test_ComputedAttributeAccess() { let entity = decoded(type: TestType.self, data: computed_property_attribute) - XCTAssertEqual(entity[\.computed], "Sarah") + XCTAssertEqual(entity[\.computed], "Sarah2") } func test_ComputedRelationshipAccess() { @@ -44,7 +44,7 @@ extension ComputedPropertiesTests { public struct Attributes: JSONAPI.Attributes { public let name: Attribute public var computed: Attribute { - return name + return name.map { $0 + "2" } } }