From 88f4f4e367671ff700b9352bea3e18f55213559d Mon Sep 17 00:00:00 2001 From: Rusty Zarse Date: Mon, 2 Dec 2019 16:41:40 -0500 Subject: [PATCH 1/3] added test to demonstrate that FuseProperty.name should be used as a key then reported properly in results. Added utility to reflect value using name as key. Updated search to use new utility. yay. --- .../xcshareddata/xcschemes/Fuse.xcscheme | 4 -- Example/Tests/Tests.swift | 60 +++++++++++++++++++ Fuse/Classes/Fuse.swift | 4 +- Fuse/Classes/FuseUtilities.swift | 5 ++ README.md | 4 +- 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Fuse.xcscheme b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Fuse.xcscheme index f924cfd..af9f7a1 100644 --- a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Fuse.xcscheme +++ b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Fuse.xcscheme @@ -29,8 +29,6 @@ shouldUseLaunchSchemeArgsEnv = "YES"> - - - - ])]() item.properties.forEach { property in - - let value = property.name + + let value = FuseUtilities.propertyStringValueUsingKey(property.name, instance: item) if let result = self.search(pattern, in: value) { let weight = property.weight == 1 ? 1 : 1 - property.weight diff --git a/Fuse/Classes/FuseUtilities.swift b/Fuse/Classes/FuseUtilities.swift index b33d6d9..7a81e32 100644 --- a/Fuse/Classes/FuseUtilities.swift +++ b/Fuse/Classes/FuseUtilities.swift @@ -83,4 +83,9 @@ class FuseUtilities { } return ranges } + + static func propertyStringValueUsingKey(_ key: String, instance: Any) -> String { + let mirror = Mirror(reflecting: instance) + return mirror.descendant(key) as? String ?? key + } } diff --git a/README.md b/README.md index b608a61..ae7b5b1 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,8 @@ struct Book: Fuseable { var properties: [FuseProperty] { return [ - FuseProperty(name: title, weight: 0.3), - FuseProperty(name: author, weight: 0.7), + FuseProperty(name: "title", weight: 0.3), + FuseProperty(name: "author", weight: 0.7), ] } } From e33d4c49704d4b95090013c8e9e31eeae7da3c3a Mon Sep 17 00:00:00 2001 From: Rusty Zarse Date: Mon, 2 Dec 2019 18:56:06 -0500 Subject: [PATCH 2/3] modified to support addressing using dot notation --- Example/Tests/Tests.swift | 42 ++++++++++++++------------------ Fuse/Classes/FuseUtilities.swift | 31 +++++++++++++++++++++-- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 0e2603b..ec72d9d 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -245,58 +245,52 @@ class Tests: XCTestCase { XCTAssert(results[1].index == 1, "The second result is the second book") } + struct Publisher { + let name: String + let year: String + } + struct Book: Fuseable { let author: String - let title: String + let title: String? + let publisher: Publisher? var properties: [FuseProperty] { return [ FuseProperty(name: "title", weight: 0.3), - FuseProperty(name: "author", weight: 0.7), + FuseProperty(name: "author", weight: 0.6), + FuseProperty(name: "publisher.name", weight: 0.1), ] } } func testListOfFuseableStruct() { - + let books: [Book] = [ - Book(author: "John X", title: "Old Man's War fiction"), - Book(author: "P.D. Mans", title: "Right Ho Jeeves"), - Book(author: "Robert M. Pirsig", title: "Lila") + Book(author: "John X", title: "Old Man's War fiction", publisher: Publisher(name: "Tor Books", year: "2005")), + Book(author: "P.D. Mans", title: "Right Ho Jeeves", publisher: Publisher(name: "Herbert Jenkins", year: "1934")), + Book(author: "Robert M. Pirsig", title: "Lila", publisher: Publisher(name: "Bantom Books", year: "1991")), + Book(author: "Yuval Noah Harari", title: "Sapiens", publisher: Publisher(name: "Harper", year: "2015")), + Book(author: "Homer", title: "The Odyssey", publisher: nil) ] let fuse = Fuse() let results = fuse.search("man", in: books) - /* + results.forEach { item in - print("-- Fuse Result -----") + print("-- Fuseable Result -----") print("index: \(item.index)") print("score: \(item.score)") print("results: \(item.results)") print("--------------------") } - */ - - // Expected Output: - // ... - // index: 0 - // score: 0.028 - // results: [(key: "title", score: 0.027999999999999997, ranges: [CountableClosedRange(4...6)])] - // ... - - // Actual Output - // ... - // index: 0 - // score: 0.027999999999999997 - // results: [(key: "Old Man\'s War fiction", score: 0.027999999999999997, ranges: [ClosedRange(4...6)])] - // ... // two matches - XCTAssertEqual(results.count, 2) + XCTAssertEqual(results.count, 3) // the key should be the name of the property XCTAssertEqual(results[0].results[0].key, "author") diff --git a/Fuse/Classes/FuseUtilities.swift b/Fuse/Classes/FuseUtilities.swift index 7a81e32..919e597 100644 --- a/Fuse/Classes/FuseUtilities.swift +++ b/Fuse/Classes/FuseUtilities.swift @@ -85,7 +85,34 @@ class FuseUtilities { } static func propertyStringValueUsingKey(_ key: String, instance: Any) -> String { - let mirror = Mirror(reflecting: instance) - return mirror.descendant(key) as? String ?? key + + // values containing periods Also have spaces. Here to support using value rather than key in FuseProperty + if(key.contains(" ")){ return key } + + var mirror = Mirror(reflecting: instance) + var propertyValue: Any = mirror.descendant(key) ?? key + // walk key path if dot notation is present + let keyFragments = key.components(separatedBy: ".") + // only do the work if there were key path fragments + if(keyFragments.count > 1){ + // iterate fragments + keyFragments.forEach{ keyFragment in + // retrieve property value + propertyValue = mirror.descendant(keyFragment) ?? "" + // reflect on property value + mirror = Mirror(reflecting: propertyValue) + // if optional, descendents aren't there ;-\ + if(mirror.displayStyle == .optional) { + // unwrap optional + if let some = mirror.children.first?.value { + // use the wrapped value + propertyValue = some + // reflect on the unwrapped value + mirror = Mirror(reflecting: propertyValue) + } + } + } + } + return propertyValue as? String ?? key } } From b1235e7fe594e23957d73e2a0c26944439187af9 Mon Sep 17 00:00:00 2001 From: Rusty Zarse Date: Mon, 2 Dec 2019 20:17:38 -0500 Subject: [PATCH 3/3] async method required same modifications --- Example/Tests/Tests.swift | 31 +++++++++++++++++++++++++++++++ Fuse/Classes/Fuse.swift | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index ec72d9d..047787c 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -295,7 +295,38 @@ class Tests: XCTestCase { // the key should be the name of the property XCTAssertEqual(results[0].results[0].key, "author") + } + + func testListOfFuseableStructASync() { + + let books: [Book] = [ + Book(author: "John X", title: "Old Man's War fiction", publisher: Publisher(name: "Tor Books", year: "2005")), + Book(author: "P.D. Mans", title: "Right Ho Jeeves", publisher: Publisher(name: "Herbert Jenkins", year: "1934")), + Book(author: "Robert M. Pirsig", title: "Lila", publisher: Publisher(name: "Bantom Books", year: "1991")), + Book(author: "Yuval Noah Harari", title: "Sapiens", publisher: Publisher(name: "Harper", year: "2015")), + Book(author: "Homer", title: "The Odyssey", publisher: nil) + ] + let fuse = Fuse() + + // XCTest async + let expectation = self.expectation(description: #function) + var asyncResult: [Fuse.FusableSearchResult] = [] + + fuse.search("man", in: books){ results in + asyncResult = results + expectation.fulfill() + } + + // Then + waitForExpectations(timeout: 10) + + + // two matches + XCTAssertEqual(asyncResult.count, 3) + + // the key should be the name of the property + XCTAssertEqual(asyncResult[0].results[0].key, "author") } diff --git a/Fuse/Classes/Fuse.swift b/Fuse/Classes/Fuse.swift index cc7fe50..84ad52d 100644 --- a/Fuse/Classes/Fuse.swift +++ b/Fuse/Classes/Fuse.swift @@ -505,7 +505,7 @@ extension Fuse { item.properties.forEach { property in - let value = property.name + let value = FuseUtilities.propertyStringValueUsingKey(property.name, instance: item) if let result = self.search(pattern, in: value) { let weight = property.weight == 1 ? 1 : 1 - property.weight