diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 235f9d5..8308442 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -101,6 +101,18 @@ B51FE5AF1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51FE5AA1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift */; }; B5202CFA1C04688100DED140 /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; B5202CFD1C046E8400DED140 /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; + B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA51FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA61FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */; }; + B5215CA91FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */; }; + B5215CAE1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; + B5215CAF1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; + B5215CB01FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; + B5215CB11FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */; }; B5220E081D0C5F8D009BC71E /* ObjectObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */; }; B5220E091D0C5F8D009BC71E /* ObjectObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */; }; B5220E0C1D0D0D19009BC71E /* ImportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E0B1D0D0D19009BC71E /* ImportTests.swift */; }; @@ -324,10 +336,10 @@ B5519A601CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A611CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; B5519A621CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */; }; - B55514EA1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; - B55514EB1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; - B55514EC1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; - B55514ED1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */; }; + B55514EA1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; + B55514EB1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; + B55514EC1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; + B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B55514E91EED8BF900BAB888 /* From+Querying.swift */; }; B55717441D15B09E009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717451D15B09F009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; B55717461D15B0A1009BDBCA /* CoreStoreBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -765,6 +777,9 @@ B51260921E9B28F100402229 /* EntityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityIdentifier.swift; sourceTree = ""; }; B51FE5AA1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+CustomDebugStringConvertible.swift"; sourceTree = ""; }; B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+Convenience.swift"; sourceTree = ""; }; + B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchChainBuilder.swift; sourceTree = ""; }; + B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryChainBuilder.swift; sourceTree = ""; }; + B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionMonitorBuilder.swift; sourceTree = ""; }; B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectObserverTests.swift; sourceTree = ""; }; B5220E0B1D0D0D19009BC71E /* ImportTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportTests.swift; sourceTree = ""; }; B5220E0F1D0DA6AB009BC71E /* ListObserverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListObserverTests.swift; sourceTree = ""; }; @@ -814,7 +829,7 @@ B5519A5E1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSAsynchronousDataTransaction.swift; sourceTree = ""; }; B5548CD51BD65AE00077652A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B5548CD71BD65AE50077652A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; - B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainedClauseBuilder.swift; sourceTree = ""; }; + B55514E91EED8BF900BAB888 /* From+Querying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "From+Querying.swift"; sourceTree = ""; }; B55717421D15AF9C009BDBCA /* CoreStoreBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreStoreBridge.h; sourceTree = ""; }; B559CD421CAA8B6300E4D58B /* CSSetupResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSetupResult.swift; sourceTree = ""; }; B559CD481CAA8C6D00E4D58B /* CSStorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSStorageInterface.swift; sourceTree = ""; }; @@ -1149,6 +1164,17 @@ name = Observing; sourceTree = ""; }; + B5215CA21FA47BF300139E3A /* Chained Clauses */ = { + isa = PBXGroup; + children = ( + B55514E91EED8BF900BAB888 /* From+Querying.swift */, + B5215CA31FA47DFD00139E3A /* FetchChainBuilder.swift */, + B5215CA81FA4810300139E3A /* QueryChainBuilder.swift */, + B5215CAD1FA4812500139E3A /* SectionMonitorBuilder.swift */, + ); + name = "Chained Clauses"; + sourceTree = ""; + }; B52F74391E9B8724005F3DAC /* Dynamic Schema */ = { isa = PBXGroup; children = ( @@ -1431,7 +1457,7 @@ B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */, B596BBBA1DD5C39F001DCDD9 /* QueryableSource.swift */, B549F65D1E569C7400FBAB2D /* QueryableAttributeType.swift */, - B55514E91EED8BF900BAB888 /* ChainedClauseBuilder.swift */, + B5215CA21FA47BF300139E3A /* Chained Clauses */, B5A1DAC61F111BBE003CF369 /* KeyPath Utilities */, B5E84F0A1AFF847B0064E85B /* Protocol Clauses */, B5E84EFF1AFF847B0064E85B /* Concrete Clauses */, @@ -1855,7 +1881,7 @@ B5D339D81E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5D3F6451C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, B56923FA1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EA1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514EA1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBB1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -1945,6 +1971,7 @@ B5E84F0F1AFF847B0064E85B /* From.swift in Sources */, B5FAD6A91B50A4B400714891 /* Progress+Convenience.swift in Sources */, B5E84EFC1AFF846E0064E85B /* SynchronousDataTransaction.swift in Sources */, + B5215CA91FA4810300139E3A /* QueryChainBuilder.swift in Sources */, B5E222231CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, B5E84F281AFF84920064E85B /* NSManagedObject+Convenience.swift in Sources */, B52F744A1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, @@ -1953,6 +1980,7 @@ B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */, B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA41FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A011E96012400C880DE /* Relationship.swift in Sources */, B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */, B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -1981,6 +2009,7 @@ B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B52F743D1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56923FF1EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CAE1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5ECDBEC1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B56923EC1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */, @@ -2048,7 +2077,7 @@ B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */, 82BA18CE1C4BBD7100A0916E /* FetchedResultsControllerDelegate.swift in Sources */, B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EB1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514EB1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -2138,6 +2167,7 @@ 82BA18C71C4BBD5900A0916E /* CoreStore+Migration.swift in Sources */, B5E222251CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, 82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */, + B5215CAA1FA4810300139E3A /* QueryChainBuilder.swift in Sources */, 82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */, B52F744B1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, @@ -2146,6 +2176,7 @@ 82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */, B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA51FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A021E96012400C880DE /* Relationship.swift in Sources */, B559CD4B1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -2174,6 +2205,7 @@ B5ECDBEE1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CAF1FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, 82BA18D61C4BBD7100A0916E /* NSManagedObjectContext+Transaction.swift in Sources */, B56923ED1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, 82BA18B91C4BBD4A00A0916E /* From.swift in Sources */, @@ -2241,7 +2273,7 @@ B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */, B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514ED1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514ED1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, @@ -2331,6 +2363,7 @@ B52DD1CB1BE1F94600949AFE /* WeakObject.swift in Sources */, B52DD1C11BE1F94600949AFE /* Functions.swift in Sources */, B5220E1A1D130791009BC71E /* CoreStoreFetchedResultsController.swift in Sources */, + B5215CAC1FA4810300139E3A /* QueryChainBuilder.swift in Sources */, B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */, B52F744D1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */, @@ -2339,6 +2372,7 @@ B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA71FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A041E96012400C880DE /* Relationship.swift in Sources */, B52DD1C61BE1F94600949AFE /* NSManagedObjectContext+CoreStore.swift in Sources */, B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -2367,6 +2401,7 @@ B5220E201D130813009BC71E /* CSObjectMonitor.swift in Sources */, B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56924021EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CB11FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B5220E171D1306DF009BC71E /* UnsafeDataTransaction+Observing.swift in Sources */, B56923EF1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B53FBA081CAB300C00F0D40A /* CSMigrationType.swift in Sources */, @@ -2434,7 +2469,7 @@ B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */, B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, - B55514EC1EED8BF900BAB888 /* ChainedClauseBuilder.swift in Sources */, + B55514EC1EED8BF900BAB888 /* From+Querying.swift in Sources */, B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, @@ -2524,6 +2559,7 @@ B563218E1BD65216006C9394 /* SaveResult.swift in Sources */, B5E222261CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, B56321A21BD65216006C9394 /* ListObserver.swift in Sources */, + B5215CAB1FA4810300139E3A /* QueryChainBuilder.swift in Sources */, B563218A1BD65216006C9394 /* SynchronousDataTransaction.swift in Sources */, B52F744C1E9B8740005F3DAC /* CoreStoreSchema.swift in Sources */, B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, @@ -2532,6 +2568,7 @@ B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */, B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */, + B5215CA61FA47DFD00139E3A /* FetchChainBuilder.swift in Sources */, B5D33A031E96012400C880DE /* Relationship.swift in Sources */, B559CD4C1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, @@ -2560,6 +2597,7 @@ B5ECDBEF1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */, + B5215CB01FA4812500139E3A /* SectionMonitorBuilder.swift in Sources */, B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */, B56923EE1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B56321861BD65216006C9394 /* CoreStoreLogger.swift in Sources */, diff --git a/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift b/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift index 4b21375..36a7a01 100644 --- a/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift +++ b/CoreStoreTests/BaseTests/BaseTestDataTestCase.swift @@ -2,8 +2,25 @@ // BaseTestDataTestCase.swift // CoreStore // -// Created by John Rommel Estropia on 2016/06/11. -// Copyright © 2016 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import Foundation diff --git a/Sources/AnyCoreStoreKeyPath.swift b/Sources/AnyCoreStoreKeyPath.swift index 36b4add..eb86752 100644 --- a/Sources/AnyCoreStoreKeyPath.swift +++ b/Sources/AnyCoreStoreKeyPath.swift @@ -2,8 +2,25 @@ // AnyCoreStoreKeyPath.swift // CoreStore // -// Created by John Estropia on 2017/10/02. -// Copyright © 2017 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import Foundation diff --git a/Sources/CoreStore+Observing.swift b/Sources/CoreStore+Observing.swift index 01f5993..3a5da14 100644 --- a/Sources/CoreStore+Observing.swift +++ b/Sources/CoreStore+Observing.swift @@ -67,7 +67,18 @@ public extension CoreStore { return self.defaultStack.monitorList(from, fetchClauses) } - // TODO: docs + /** + Creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let monitor = CoreStore.monitorList( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public static func monitorList(_ clauseChain: B) -> ListMonitor { return self.defaultStack.monitorList(clauseChain.from, clauseChain.fetchClauses) @@ -97,7 +108,23 @@ public extension CoreStore { self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. + + ``` + CoreStore.monitorList( + { (monitor) in + self.monitor = monitor + }, + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter createAsynchronously: the closure that receives the created `ListMonitor` instance + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public static func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.defaultStack.monitorList( @@ -133,7 +160,19 @@ public extension CoreStore { return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) } - // TODO: docs + /** + Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + let monitor = CoreStore.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public static func monitorSectionedList(_ clauseChain: B) -> ListMonitor { return self.defaultStack.monitorSectionedList( @@ -169,7 +208,22 @@ public extension CoreStore { self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + CoreStore.monitorSectionedList( + { (monitor) in + self.monitor = monitor + }, + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public static func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.defaultStack.monitorSectionedList( diff --git a/Sources/CoreStoreManagedObject.swift b/Sources/CoreStoreManagedObject.swift index 4790660..6654a75 100644 --- a/Sources/CoreStoreManagedObject.swift +++ b/Sources/CoreStoreManagedObject.swift @@ -2,8 +2,25 @@ // CoreStoreManagedObject.swift // CoreStore // -// Created by John Rommel Estropia on 2017/06/04. -// Copyright © 2017 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import CoreData diff --git a/Sources/DataStack+Observing.swift b/Sources/DataStack+Observing.swift index 611130d..9946fdd 100644 --- a/Sources/DataStack+Observing.swift +++ b/Sources/DataStack+Observing.swift @@ -88,7 +88,18 @@ public extension DataStack { ) } - // TODO: docs + /** + Creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. + ``` + let monitor = dataStack.monitorList( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func monitorList(_ clauseChain: B) -> ListMonitor { return self.monitorList(clauseChain.from, clauseChain.fetchClauses) @@ -136,7 +147,23 @@ public extension DataStack { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. + + ``` + dataStack.monitorList( + { (monitor) in + self.monitor = monitor + }, + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter createAsynchronously: the closure that receives the created `ListMonitor` instance + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func monitorList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.monitorList( @@ -190,7 +217,19 @@ public extension DataStack { ) } - // TODO: docs + /** + Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + let monitor = dataStack.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(_ clauseChain: B) -> ListMonitor { return self.monitorSectionedList( @@ -245,7 +284,22 @@ public extension DataStack { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + dataStack.monitorSectionedList( + { (monitor) in + self.monitor = monitor + }, + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.monitorSectionedList( diff --git a/Sources/FetchChainBuilder.swift b/Sources/FetchChainBuilder.swift new file mode 100644 index 0000000..9ce7063 --- /dev/null +++ b/Sources/FetchChainBuilder.swift @@ -0,0 +1,74 @@ +// +// FetchChainBuilder.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - FetchChainBuilder + +/** + The fetch builder type used for fetches. A `FetchChainBuilder` is created from a `From` clause. + ``` + let people = source.fetchAll( + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + */ +public struct FetchChainBuilder: FetchChainableBuilderType { + + // MARK: FetchChainableBuilderType + + public typealias ObjectType = D + + public var from: From + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - FetchChainableBuilderType + +/** + Utility protocol for `FetchChainBuilder`. Used in fetch methods that support chained fetch builders. + */ +public protocol FetchChainableBuilderType { + + /** + The `DynamicObject` type for the fetch + */ + associatedtype ObjectType: DynamicObject + + /** + The `From` clause specifies the source entity and source persistent store for the fetch + */ + var from: From { get set } + + /** + The `FetchClause`s to be used for the fetch + */ + var fetchClauses: [FetchClause] { get set } +} diff --git a/Sources/ChainedClauseBuilder.swift b/Sources/From+Querying.swift similarity index 78% rename from Sources/ChainedClauseBuilder.swift rename to Sources/From+Querying.swift index a20974d..277f97b 100644 --- a/Sources/ChainedClauseBuilder.swift +++ b/Sources/From+Querying.swift @@ -1,5 +1,5 @@ // -// FetchCondition.swift +// From+Querying.swift // CoreStore // // Copyright © 2017 John Rommel Estropia @@ -27,85 +27,96 @@ import Foundation import CoreData -// MARK: - FetchChainableBuilderType - -public protocol FetchChainableBuilderType { - - associatedtype ObjectType: DynamicObject - - var from: From { get set } - var fetchClauses: [FetchClause] { get set } -} - - -// MARK: - QueryChainableBuilderType - -public protocol QueryChainableBuilderType { - - associatedtype ObjectType: DynamicObject - associatedtype ResultType: SelectResultType - - var from: From { get set } - var select: Select { get set } - var queryClauses: [QueryClause] { get set } -} - -@available(OSX 10.12, *) -public protocol SectionMonitorBuilderType { - - associatedtype ObjectType: DynamicObject - - var from: From { get set } - var sectionBy: SectionBy { get set } - var fetchClauses: [FetchClause] { get set } -} - - -// MARK: - FetchChainBuilder - -public struct FetchChainBuilder: FetchChainableBuilderType { - - // MARK: FetchChainableBuilderType - - public typealias ObjectType = D - - public var from: From - public var fetchClauses: [FetchClause] = [] -} - - -// MARK: - QueryChainBuilder - -public struct QueryChainBuilder: QueryChainableBuilderType { - - // MARK: QueryChainableBuilderType - - public typealias ObjectType = D - public typealias ResultType = R - - public var from: From - public var select: Select - public var queryClauses: [QueryClause] = [] -} - - -// MARK: - SectionMonitorChainBuilder - -@available(OSX 10.12, *) -public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { - - // MARK: SectionMonitorBuilderType - - public var from: From - public var sectionBy: SectionBy - public var fetchClauses: [FetchClause] = [] -} - - // MARK: - From public extension From { + /** + Creates a `FetchChainBuilder` that starts with the specified `Where` clause + + - parameter clause: the `Where` clause to create a `FetchChainBuilder` with + - returns: a `FetchChainBuilder` that starts with the specified `Where` clause + */ + public func `where`(_ clause: Where) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + /** + Creates a `FetchChainBuilder` with a predicate using the specified string format and arguments + + - parameter format: the format string for the predicate + - parameter args: the arguments for `format` + - returns: a `FetchChainBuilder` with a predicate using the specified string format and arguments + */ + public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: args)) + } + + /** + Creates a `FetchChainBuilder` with a predicate using the specified string format and arguments + + - parameter format: the format string for the predicate + - parameter argumentArray: the arguments for `format` + - returns: a `FetchChainBuilder` with a predicate using the specified string format and arguments + */ + public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { + + return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) + } + + /** + Creates a `FetchChainBuilder` with a series of `SortKey`s + + - parameter sortKey: a single `SortKey` + - parameter sortKeys: a series of other `SortKey`s + - returns: a `FetchChainBuilder` with a series of `SortKey`s + */ + public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { + + return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) + } + + /** + Creates a `FetchChainBuilder` with a closure where the `NSFetchRequest` may be configured + + - parameter fetchRequest: the block to customize the `NSFetchRequest` + - returns: a `FetchChainBuilder` with closure where the `NSFetchRequest` may be configured + */ + public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { + + return self.fetchChain(appending: Tweak(fetchRequest)) + } + + /** + Creates a `FetchChainBuilder` and immediately appending a `FetchClause` + + - parameter clause: the `FetchClause` to add to the `FetchChainBuilder` + - returns: a `FetchChainBuilder` containing the specified `FetchClause` + */ + public func appending(_ clause: FetchClause) -> FetchChainBuilder { + + return self.fetchChain(appending: clause) + } + + /** + Creates a `FetchChainBuilder` and immediately appending a series of `FetchClause`s + + - parameter clauses: the `FetchClause`s to add to the `FetchChainBuilder` + - returns: a `FetchChainBuilder` containing the specified `FetchClause`s + */ + public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { + + return self.fetchChain(appending: clauses) + } + + /** + Creates a `QueryChainBuilder` that starts with the specified `Select` clause + + - parameter clause: the `Select` clause to create a `QueryChainBuilder` with + - returns: a `QueryChainBuilder` that starts with the specified `Select` clause + */ public func select(_ clause: Select) -> QueryChainBuilder { return .init( @@ -115,11 +126,26 @@ public extension From { ) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + + - parameter resultType: the generic `SelectResultType` for the `Select` clause + - parameter selectTerm: a `SelectTerm` + - parameter selectTerms: a series of `SelectTerm`s + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + */ public func select(_ resultType: R.Type, _ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) -> QueryChainBuilder { return self.select(resultType, [selectTerm] + selectTerms) } + /** + Creates a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + + - parameter resultType: the generic `SelectResultType` for the `Select` clause + - parameter selectTerms: a series of `SelectTerm`s + - returns: a `QueryChainBuilder` that starts with a `Select` clause created from the specified `SelectTerm`s + */ public func select(_ resultType: R.Type, _ selectTerms: [SelectTerm]) -> QueryChainBuilder { return .init( @@ -129,6 +155,12 @@ public extension From { ) } + /** + Creates a `SectionMonitorChainBuilder` that starts with the `SectionBy` to use to group `ListMonitor` objects into sections + + - parameter clause: the `SectionBy` to be used by the `ListMonitor` + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ clause: SectionBy) -> SectionMonitorChainBuilder { @@ -139,12 +171,26 @@ public extension From { ) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections + + - parameter sectionKeyPath: the key path to use to group the objects into sections + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString) -> SectionMonitorChainBuilder { return self.sectionBy(sectionKeyPath, { $0 }) } + /** + Creates a `SectionMonitorChainBuilder` with the key path to use to group `ListMonitor` objects into sections, and a closure to transform the value for the key path to an appropriate section name + + - Important: Some utilities (such as `ListMonitor`s) may keep `SectionBy`s in memory and may thus introduce retain cycles if reference captures are not handled properly. + - parameter sectionKeyPath: the key path to use to group the objects into sections + - parameter sectionIndexTransformer: a closure to transform the value for the key path to an appropriate section name + - returns: a `SectionMonitorChainBuilder` that is sectioned by the specified key path + */ @available(OSX 10.12, *) public func sectionBy(_ sectionKeyPath: KeyPathString, _ sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> SectionMonitorChainBuilder { @@ -155,41 +201,6 @@ public extension From { ) } - public func `where`(_ clause: Where) -> FetchChainBuilder { - - return self.fetchChain(appending: clause) - } - - public func `where`(format: String, _ args: Any...) -> FetchChainBuilder { - - return self.fetchChain(appending: Where(format, argumentArray: args)) - } - - public func `where`(format: String, argumentArray: [Any]?) -> FetchChainBuilder { - - return self.fetchChain(appending: Where(format, argumentArray: argumentArray)) - } - - public func orderBy(_ sortKey: OrderBy.SortKey, _ sortKeys: OrderBy.SortKey...) -> FetchChainBuilder { - - return self.fetchChain(appending: OrderBy([sortKey] + sortKeys)) - } - - public func tweak(_ fetchRequest: @escaping (NSFetchRequest) -> Void) -> FetchChainBuilder { - - return self.fetchChain(appending: Tweak(fetchRequest)) - } - - public func appending(_ clause: FetchClause) -> FetchChainBuilder { - - return self.fetchChain(appending: clause) - } - - public func appending(contentsOf clauses: S) -> FetchChainBuilder where S.Element == FetchClause { - - return self.fetchChain(appending: clauses) - } - // MARK: Private @@ -367,21 +378,6 @@ public extension FetchChainBuilder where D: CoreStoreObject { public extension QueryChainBuilder { - public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { - - return self.queryChain(appending: clause) - } - - public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { - - return self.groupBy(GroupBy([keyPath] + keyPaths)) - } - - public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { - - return self.queryChain(appending: GroupBy(keyPaths)) - } - public func `where`(_ clause: Where) -> QueryChainBuilder { return self.queryChain(appending: clause) @@ -407,6 +403,21 @@ public extension QueryChainBuilder { return self.queryChain(appending: Tweak(fetchRequest)) } + public func groupBy(_ clause: GroupBy) -> QueryChainBuilder { + + return self.queryChain(appending: clause) + } + + public func groupBy(_ keyPath: KeyPathString, _ keyPaths: KeyPathString...) -> QueryChainBuilder { + + return self.groupBy(GroupBy([keyPath] + keyPaths)) + } + + public func groupBy(_ keyPaths: [KeyPathString]) -> QueryChainBuilder { + + return self.queryChain(appending: GroupBy(keyPaths)) + } + public func appending(_ clause: QueryClause) -> QueryChainBuilder { return self.queryChain(appending: clause) diff --git a/Sources/QueryChainBuilder.swift b/Sources/QueryChainBuilder.swift new file mode 100644 index 0000000..00522c4 --- /dev/null +++ b/Sources/QueryChainBuilder.swift @@ -0,0 +1,86 @@ +// +// QueryChainBuilder.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - QueryChainBuilder + +/** + The fetch builder type used for a queries. A `QueryChainBuilder` is created from a `From` clause and then a `select(...)` chain. + ``` + let averageAdultAge = dataStack.queryValue( + From() + .select(Int.self, .average(\.age)) + .where(\.age > 18) + ) + ``` + */ +public struct QueryChainBuilder: QueryChainableBuilderType { + + // MARK: QueryChainableBuilderType + + public typealias ObjectType = D + public typealias ResultType = R + + public var from: From + public var select: Select + public var queryClauses: [QueryClause] = [] +} + + +// MARK: - QueryChainableBuilderType + +/** + Utility protocol for `QueryChainBuilder`. Used in fetch methods that support chained query builders. + */ +public protocol QueryChainableBuilderType { + + /** + The `DynamicObject` type for the query + */ + associatedtype ObjectType: DynamicObject + + /** + The `SelectResultType` type for the query + */ + associatedtype ResultType: SelectResultType + + /** + The `From` clause specifies the source entity and source persistent store for the query + */ + var from: From { get set } + + /** + The `Select` clause to be used for the query + */ + var select: Select { get set } + + /** + The `QueryClause`s to be used for the query + */ + var queryClauses: [QueryClause] { get set } +} diff --git a/Sources/SectionMonitorBuilder.swift b/Sources/SectionMonitorBuilder.swift new file mode 100644 index 0000000..9530512 --- /dev/null +++ b/Sources/SectionMonitorBuilder.swift @@ -0,0 +1,81 @@ +// +// SectionMonitorBuilder.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - SectionMonitorChainBuilder + +/** + The fetch builder type used for a sectioned `ListMonitor`. A `SectionMonitorChainBuilder` is created from a `From` clause and then a `sectionBy(...)` chain. + ``` + let monitor = transaction.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + */ +@available(OSX 10.12, *) +public struct SectionMonitorChainBuilder: SectionMonitorBuilderType { + + // MARK: SectionMonitorBuilderType + + public var from: From + public var sectionBy: SectionBy + public var fetchClauses: [FetchClause] = [] +} + + +// MARK: - SectionMonitorBuilderType + +/** + Utility protocol for `SectionMonitorChainBuilder`. Used in methods that support chained fetch builders. + */ +@available(OSX 10.12, *) +public protocol SectionMonitorBuilderType { + + /** + The `DynamicObject` type for the `ListMonitor` + */ + associatedtype ObjectType: DynamicObject + + /** + The `From` clause specifies the source entity and source persistent store for the `ListMonitor` + */ + var from: From { get set } + + /** + The `SectionBy` clause to be used for the `ListMonitor` + */ + var sectionBy: SectionBy { get set } + + /** + The `FetchClause`s to be used for the `ListMonitor` + */ + var fetchClauses: [FetchClause] { get set } +} diff --git a/Sources/UnsafeDataTransaction+Observing.swift b/Sources/UnsafeDataTransaction+Observing.swift index 7ad6a67..f8a2449 100644 --- a/Sources/UnsafeDataTransaction+Observing.swift +++ b/Sources/UnsafeDataTransaction+Observing.swift @@ -83,7 +83,23 @@ public extension UnsafeDataTransaction { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed. + + ``` + transaction.monitorList( + { (monitor) in + self.monitor = monitor + }, + From() + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter createAsynchronously: the closure that receives the created `ListMonitor` instance + - parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` + */ public func monitorList(_ clauseChain: B) -> ListMonitor { return self.monitorList(clauseChain.from, clauseChain.fetchClauses) @@ -176,7 +192,19 @@ public extension UnsafeDataTransaction { ) } - // TODO: docs + /** + Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + let monitor = transaction.monitorSectionedList( + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(_ clauseChain: B) -> ListMonitor { return self.monitorSectionedList( @@ -226,7 +254,22 @@ public extension UnsafeDataTransaction { ) } - // TODO: docs + /** + Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses. + ``` + transaction.monitorSectionedList( + { (monitor) in + self.monitor = monitor + }, + From() + .sectionBy(\.age, { "\($0!) years old" }) + .where(\.age > 18) + .orderBy(.ascending(\.age)) + ) + ``` + - parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses + - returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` + */ public func monitorSectionedList(createAsynchronously: @escaping (ListMonitor) -> Void, _ clauseChain: B) { self.monitorSectionedList( diff --git a/Sources/WhereClauseType.swift b/Sources/WhereClauseType.swift index 38ece53..0df78c7 100644 --- a/Sources/WhereClauseType.swift +++ b/Sources/WhereClauseType.swift @@ -2,8 +2,25 @@ // WhereClauseType.swift // CoreStore // -// Created by John Estropia on 2017/09/29. -// Copyright © 2017 John Rommel Estropia. All rights reserved. +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. // import Foundation