Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

Add function to create array of RawRepresentables #61

Merged
merged 1 commit into from
Jul 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
- Add `@noescape` to transformation closures
[Keith Smiley](https://github.com/keith)
[#60](https://github.com/lyft/mapper/pull/60)
- Add `from` for arrays of `RawRepresentable`s
[Keith Smiley](https://github.com/keith)
[#61](https://github.com/lyft/mapper/pull/61)

## Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ install-tvOS:
true

install-lint:
brew remove swiftlint --force || true
brew install https://raw.githubusercontent.com/Homebrew/homebrew/fffa4b271ba57c7633e8e24cae543a197a9e3e01/Library/Formula/swiftlint.rb

install-carthage:
Expand Down
31 changes: 29 additions & 2 deletions Sources/Mapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,33 @@ public struct Mapper {
return nil
}

/**
Get an array of RawRepresentable values from a field in the the source data.

- note: If T.init(rawValue:) fails given the T.RawValue from the array of source data, that value will be
replaced by the passed defaultValue, which defaults to nil. The resulting array is flatMapped and
all nils are removed. This means that any unrecognized values will be removed or replaced with a
default. This ensures backwards compatibility if your source data has keys that your mapping
layer doesn't know about yet.

- parameter field: The field to use from the source data
- parameter defaultValue: The value to use if the rawValue initializer fails

- returns: An array of the RawRepresentable value, with all nils removed
*/
@warn_unused_result
public func from<T: RawRepresentable where T.RawValue: Convertible,
T.RawValue == T.RawValue.ConvertedType>(field: String, defaultValue: T? = nil) throws -> [T]
{
let value = try self.JSONFromField(field)
guard let array = value as? [AnyObject] else {
throw MapperError.TypeMismatchError(field: field, value: value, type: [AnyObject].self)
}

let rawValues = try array.map { try T.RawValue.fromMap($0) }
return rawValues.flatMap { T(rawValue: $0) ?? defaultValue }
}

// MARK: - T: Mappable

/**
Expand Down Expand Up @@ -112,7 +139,7 @@ public struct Mapper {

This allows you to transparently have nested arrays of Mappable values

Note: If any value in the array of NSDictionaries is invalid, this method throws
- note: If any value in the array of NSDictionaries is invalid, this method throws

- parameter field: The field to retrieve from the source data, can be an empty string to return the
entire data set
Expand Down Expand Up @@ -153,7 +180,7 @@ public struct Mapper {

This allows you to transparently have nested arrays of Mappable values

Note: If any value in the provided array of NSDictionaries is invalid, this method returns nil
- note: If any value in the provided array of NSDictionaries is invalid, this method returns nil

- parameter field: The field to retrieve from the source data, can be an empty string to return the
entire data set
Expand Down
111 changes: 111 additions & 0 deletions Tests/Mapper/RawRepresentibleValueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,115 @@ final class RawRepresentibleValueTests: XCTestCase {
let test = try! Test(map: Mapper(JSON: [:]))
XCTAssertNil(test.value)
}

func testArrayOfValuesWithMissingKey() {
struct Test: Mappable {
let value: [Value]
init(map: Mapper) throws {
self.value = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
_ = try Test(map: Mapper(JSON: [:]))
XCTFail("Expected initialization to fail")
} catch MapperError.MissingFieldError(let field) {
XCTAssertEqual(field, "a")
} catch let error {
XCTFail("Expected only missing field error, got \(error)")
}
}

func testArrayOfValuesInvalidArray() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
_ = try Test(map: Mapper(JSON: ["a": 1]))
XCTFail("Expected initialization to fail")
} catch MapperError.TypeMismatchError(let field, let value, let type) {
XCTAssertEqual(field, "a")
XCTAssertEqual(value as? Int, 1)
XCTAssert(type == [AnyObject].self)
} catch let error {
XCTFail("Expected only missing field error, got \(error)")
}
}

func testArrayOfValuesFailedConvertible() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
_ = try Test(map: Mapper(JSON: ["a": [1]]))
XCTFail("Expected initialization to fail")
} catch MapperError.ConvertibleError(let value, let type) {
XCTAssertEqual(value as? Int, 1)
XCTAssert(type == String.self)
} catch let error {
XCTFail("Expected only missing field error, got \(error)")
}
}

func testArrayOfValuesFiltersNilsWithoutDefault() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
let test = try Test(map: Mapper(JSON: ["a": ["hi", "invalid"]]))
XCTAssertEqual(test.values.count, 1)
XCTAssert(test.values.contains(.First))
} catch let error {
XCTFail("Expected no errors, got \(error)")
}
}

func testArrayOfValuesInsertsDefault() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a", defaultValue: .First)
}
}

enum Value: String {
case First = "hi"
}

do {
let test = try Test(map: Mapper(JSON: ["a": ["invalid"]]))
XCTAssertEqual(test.values.count, 1)
XCTAssert(test.values.contains(.First))
} catch let error {
XCTFail("Expected no errors, got \(error)")
}
}
}