Add replacement and tapping functions for attributes and relationships.

This commit is contained in:
Mathew Polzin
2019-10-12 17:54:28 -07:00
parent 662a84ccf0
commit 0b307bd3bc
4 changed files with 232 additions and 26 deletions
@@ -12,25 +12,6 @@ import JSONAPI
********/
// Mapping functions (will be included in future version of library)
extension JSONAPI.ResourceObject {
func mapAttributes(_ transform: (Description.Attributes) -> Description.Attributes) -> Self {
return Self(id: id,
attributes: transform(attributes),
relationships: relationships,
meta: meta,
links: links)
}
func mapRelationships(_ transform: (Description.Relationships) -> Description.Relationships) -> Self {
return Self(id: id,
attributes: attributes,
relationships: transform(relationships),
meta: meta,
links: links)
}
}
// Mock up a server response
let mockDogData = """
{
@@ -62,11 +43,7 @@ var dog = parsedResponse.body.primaryResource!.value
print("Received dog named: \(dog.name)")
// change the dog's name
let changedDog = dog.mapAttributes { currentAttributes in
var ret = currentAttributes
ret.name = .init(value: "Julia")
return ret
}
let changedDog = dog.tappingAttributes { $0.name = .init(value: "Julia") }
// create a document to be used as a request body for a PATCH request
let patchRequest = MutableDogDocument(apiDescription: .none,
@@ -97,7 +74,7 @@ var dog2 = parsedResponse2.body.primaryResource!.value
print("Received dog named: \(dog2.name)")
// change the dog's name
let changedDog2 = dog2.mapAttributes { _ in
let changedDog2 = dog2.replacingAttributes { _ in
return .init(name: .init(value: "Nigel"))
}
@@ -130,7 +107,7 @@ var dog3 = parsedResponse2.body.primaryResource!.value
print("Received dog with owner: \(dog3 ~> \.owner)")
// give the dog an owner
let changedDog3 = dog3.mapRelationships { _ in
let changedDog3 = dog3.replacingRelationships { _ in
return .init(owner: .init(id: Id(rawValue: "1")))
}
@@ -0,0 +1,74 @@
//
// ResourceObject+Replacing.swift
// JSONAPI
//
// Created by Mathew Polzin on 10/12/19.
//
public extension JSONAPI.ResourceObject {
/// Return a new `ResourceObject`, having replaced `self`'s
/// `attributes` with the attributes returned by the given
/// replacement function.
///
/// - important: `self` is not mutated. A copy of self is returned.
///
/// - parameters:
/// - replacement: A function that takes the existing `attributes` and returns the replacement.
func replacingAttributes(_ replacement: (Description.Attributes) -> Description.Attributes) -> Self {
return Self(id: id,
attributes: replacement(attributes),
relationships: relationships,
meta: meta,
links: links)
}
/// Return a new `ResourceObject`, having updated `self`'s
/// `attributes` with the tap function given.
///
/// - important: `self` is not mutated. A copy of self is returned.
///
/// - parameters:
/// - tap: A function that takes a copy of the existing `attributes` and mutates them.
func tappingAttributes(_ tap: (inout Description.Attributes) -> Void) -> Self {
var newAttributes = attributes
tap(&newAttributes)
return Self(id: id,
attributes: newAttributes,
relationships: relationships,
meta: meta,
links: links)
}
/// Return a new `ResourceObject`, having replaced `self`'s
/// `relationships` with the `relationships` returned by the given
/// replacement function.
///
/// - important: `self` is not mutated. A copy of self is returned.
///
/// - parameters:
/// - replacement: A function that takes the existing relationships and returns the replacement.
func replacingRelationships(_ replacement: (Description.Relationships) -> Description.Relationships) -> Self {
return Self(id: id,
attributes: attributes,
relationships: replacement(relationships),
meta: meta,
links: links)
}
/// Return a new `ResourceObject`, having updated `self`'s
/// `relationships` with the tap function given.
///
/// - important: `self` is not mutated. A copy of self is returned.
///
/// - parameters:
/// - tap: A function that takes a copy of the existing `relationships` and mutates them.
func tappingRelationships(_ tap: (inout Description.Relationships) -> Void) -> Self {
var newRelationships = relationships
tap(&newRelationships)
return Self(id: id,
attributes: attributes,
relationships: newRelationships,
meta: meta,
links: links)
}
}
@@ -0,0 +1,155 @@
//
// ResourceObject+ReplacingTests.swift
// JSONAPITests
//
// Created by Mathew Polzin on 10/12/19.
//
import XCTest
import JSONAPI
final class ResourceObjectReplacingTests: XCTestCase {
func test_replaceMutableAttributes() {
let testResource = MutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.replacingAttributes {
var newAttributes = $0
newAttributes.name = .init(value: "Matt 2")
return newAttributes
}
XCTAssertEqual(testResource.name, "Matt")
XCTAssertEqual(mutatedResource.name, "Matt 2")
}
func test_tapMutableAttributes() {
let testResource = MutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.tappingAttributes { $0.name = .init(value: "Matt 2") }
XCTAssertEqual(testResource.name, "Matt")
XCTAssertEqual(mutatedResource.name, "Matt 2")
}
func test_replaceImmutableAttributes() {
let testResource = ImmutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.replacingAttributes {
return .init(name: $0.name.map { $0 + " 2" })
}
XCTAssertEqual(testResource.name, "Matt")
XCTAssertEqual(mutatedResource.name, "Matt 2")
}
func test_tapImmutableAttributes() {
let testResource = ImmutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.tappingAttributes { $0 = .init(name: $0.name.map { $0 + " 2" }) }
XCTAssertEqual(testResource.name, "Matt")
XCTAssertEqual(mutatedResource.name, "Matt 2")
}
func test_replaceMutableRelationships() {
let testResource = MutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.replacingRelationships {
var newRelationships = $0
newRelationships.other = .init(id: .init(rawValue: "3"))
return newRelationships
}
XCTAssertEqual(testResource ~> \.other, "2")
XCTAssertEqual(mutatedResource ~> \.other, "3")
}
func test_tapMutableRelationships() {
let testResource = MutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.tappingRelationships { $0.other = .init(id: .init(rawValue: "3")) }
XCTAssertEqual(testResource ~> \.other, "2")
XCTAssertEqual(mutatedResource ~> \.other, "3")
}
func test_replaceImmutableRelationships() {
let testResource = ImmutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.replacingRelationships { _ in
return .init(other: .init(id: .init(rawValue: "3")))
}
XCTAssertEqual(testResource ~> \.other, "2")
XCTAssertEqual(mutatedResource ~> \.other, "3")
}
func test_tapImmutableRelationships() {
let testResource = ImmutableTestType(attributes: .init(name: .init(value: "Matt")),
relationships: .init(other: .init(id: .init(rawValue: "2"))),
meta: .none,
links: .none)
let mutatedResource = testResource
.tappingRelationships { $0 = .init(other: .init(id: .init(rawValue: "3"))) }
XCTAssertEqual(testResource ~> \.other, "2")
XCTAssertEqual(mutatedResource ~> \.other, "3")
}
}
private enum MutableTestDescription: JSONAPI.ResourceObjectDescription {
static let jsonType: String = "test"
struct Attributes: JSONAPI.Attributes {
var name: Attribute<String>
}
struct Relationships: JSONAPI.Relationships {
var other: ToOneRelationship<MutableTestType, NoMetadata, NoLinks>
}
}
private typealias MutableTestType = JSONAPI.ResourceObject<MutableTestDescription, NoMetadata, NoLinks, String>
private enum ImmutableTestDescription: JSONAPI.ResourceObjectDescription {
static let jsonType: String = "test2"
struct Attributes: JSONAPI.Attributes {
let name: Attribute<String>
}
struct Relationships: JSONAPI.Relationships {
let other: ToOneRelationship<ImmutableTestType, NoMetadata, NoLinks>
}
}
private typealias ImmutableTestType = JSONAPI.ResourceObject<ImmutableTestDescription, NoMetadata, NoLinks, String>