diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..9ede81c --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,7 @@ +on: push +jobs: + test: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - run: swift test diff --git a/.gitignore b/.gitignore index 47042c2..b69aff0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store xcuserdata +*.xcodeproj/ +.build/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e193ee2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: objective-c -before_install: - - gem install cocoapods --no-rdoc --no-ri --no-document --quiet --pre - - gem install xcpretty --no-rdoc --no-ri --no-document --quiet -script: - - set -o pipefail - - xcodebuild -project QueryKit.xcodeproj -scheme QueryKit test -sdk macosx | xcpretty -c - - xcodebuild -project QueryKit.xcodeproj -scheme QueryKit test -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c - - pod lib lint QueryKit.podspec --quick - diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8001372 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# QueryKit Changelog + +## 0.14.1 + +### Bug Fixes + +* Prevents fatal error when handling operators with optional `Date` types. + +## 0.14.0 + +### Breaking + +* Drops support for Swift 3 and Swift 4. Swift 5 or newer must be used. + +### Enhancements + +* Added support for ordering by Swift KeyPath, for example: + + ```swift + queryset.orderBy(\.createdAt, ascending: true) + ``` + +* Added support for filtering and excluding by Swift KeyPath, for example: + + ```swift + queryset.exclude(\.name == "Kyle") + queryset.filter(\.createdAt > Date()) + ``` diff --git a/Configurations/UniversalFramework_Base.xcconfig b/Configurations/UniversalFramework_Base.xcconfig deleted file mode 100644 index 4e8b5e9..0000000 --- a/Configurations/UniversalFramework_Base.xcconfig +++ /dev/null @@ -1,18 +0,0 @@ -// -// UniversalFramework_Base.xcconfig -// QueryKit -// -// Created by Marius Rackwitz on 29/11/14. -// Copyright (c) 2014 Marius Rackwitz. All rights reserved. -// - -// Make it universal -SUPPORTED_PLATFORMS = iphonesimulator iphoneos macosx -VALID_ARCHS[sdk=iphoneos*] = arm64 armv7 armv7s -VALID_ARCHS[sdk=iphonesimulator*] = arm64 armv7 armv7s -VALID_ARCHS[sdk=macosx*] = i386 x86_64 - -// Dynamic linking uses different default copy paths -LD_RUNPATH_SEARCH_PATHS[sdk=iphoneos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -LD_RUNPATH_SEARCH_PATHS[sdk=iphonesimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' -LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' diff --git a/Configurations/UniversalFramework_Framework.xcconfig b/Configurations/UniversalFramework_Framework.xcconfig deleted file mode 100644 index ead2bdf..0000000 --- a/Configurations/UniversalFramework_Framework.xcconfig +++ /dev/null @@ -1,18 +0,0 @@ -// -// UniversalFramework_Framework.xcconfig -// QueryKit -// -// Created by Marius Rackwitz on 29/11/14. -// Copyright (c) 2014 Marius Rackwitz. All rights reserved. -// - -#include "UniversalFramework_Base.xcconfig" - -// iOS-specific default settings -CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer -TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*] = 1,2 -TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2 - -// OSX-specific default settings -FRAMEWORK_VERSION[sdk=macosx*] = A -COMBINE_HIDPI_IMAGES[sdk=macosx*] = YES diff --git a/Configurations/UniversalFramework_Test.xcconfig b/Configurations/UniversalFramework_Test.xcconfig deleted file mode 100644 index 26a6518..0000000 --- a/Configurations/UniversalFramework_Test.xcconfig +++ /dev/null @@ -1,16 +0,0 @@ -// -// UniversalFramework_Test.xcconfig -// QueryKit -// -// Created by Marius Rackwitz on 29/11/14. -// Copyright (c) 2014 Marius Rackwitz. All rights reserved. -// - -#include "UniversalFramework_Base.xcconfig" - -FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*] = $(inherited) '$(SDKROOT)/Developer/Library/Frameworks' -FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*] = $(inherited) '$(SDKROOT)/Developer/Library/Frameworks' -FRAMEWORK_SEARCH_PATHS[sdk=macosx*] = $(inherited) '$(DEVELOPER_FRAMEWORKS_DIR)' - -// Yep. -LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks' \ No newline at end of file diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..ebd1d10 --- /dev/null +++ b/Package.swift @@ -0,0 +1,14 @@ +// swift-tools-version:5.0 +import PackageDescription + + +let package = Package( + name: "QueryKit", + products: [ + .library(name: "QueryKit", targets: ["QueryKit"]), + ], + targets: [ + .target(name: "QueryKit", dependencies: []), + .testTarget(name: "QueryKitTests", dependencies: ["QueryKit"]), + ] +) diff --git a/QueryKit.png b/QueryKit.png new file mode 100644 index 0000000..a2ab70d Binary files /dev/null and b/QueryKit.png differ diff --git a/QueryKit.podspec b/QueryKit.podspec index 26a328c..03c48fd 100644 --- a/QueryKit.podspec +++ b/QueryKit.podspec @@ -1,73 +1,19 @@ Pod::Spec.new do |spec| spec.name = 'QueryKit' - spec.version = '0.9.1' - spec.summary = 'A simple CoreData query language for Swift.' - spec.homepage = 'http://querykit.org/' + spec.version = '0.14.1' + spec.summary = 'A simple type-safe Core Data query language.' + spec.homepage = 'https://github.com/QueryKit/QueryKit/' spec.license = { :type => 'BSD', :file => 'LICENSE' } - spec.author = { 'Kyle Fuller' => 'inbox@kylefuller.co.uk' } - spec.social_media_url = 'http://twitter.com/kylefuller' + spec.author = { 'Kyle Fuller' => 'kyle@fuller.li' } + spec.social_media_url = 'https://twitter.com/QueryKit' spec.source = { :git => 'https://github.com/QueryKit/QueryKit.git', :tag => "#{spec.version}" } - spec.source_files = 'QueryKit/QueryKit.h' spec.requires_arc = true - spec.ios.deployment_target = '5.0' - spec.osx.deployment_target = '10.7' - - spec.subspec 'ObjectiveC' do |objc_spec| - objc_spec.dependency 'QueryKit/Attribute/ObjectiveC' - objc_spec.dependency 'QueryKit/QuerySet/ObjectiveC' - end - - spec.subspec 'Swift' do |swift_spec| - swift_spec.dependency 'QueryKit/Attribute/Swift' - swift_spec.dependency 'QueryKit/QuerySet/Swift' - - swift_spec.ios.deployment_target = '8.0' - swift_spec.osx.deployment_target = '10.9' - end - - spec.subspec 'Attribute' do |attribute_spec| - attribute_spec.subspec 'ObjectiveC' do |objc_spec| - objc_spec.source_files = 'QueryKit/ObjectiveC/QKAttribute.{h,m}' - end - - attribute_spec.subspec 'Swift' do |swift_spec| - swift_spec.dependency 'QueryKit/QuerySet/Swift' - swift_spec.source_files = 'QueryKit/{Attribute,Expression,Predicate}.swift' - - swift_spec.ios.deployment_target = '8.0' - swift_spec.osx.deployment_target = '10.9' - end - - attribute_spec.subspec 'Bridge' do |bridge_spec| - bridge_spec.dependency 'QueryKit/Attribute/Swift' - bridge_spec.dependency 'QueryKit/Attribute/ObjectiveC' - bridge_spec.source_files = 'QueryKit/ObjectiveC/QKAttribute.swift' - - bridge_spec.ios.deployment_target = '8.0' - bridge_spec.osx.deployment_target = '10.9' - end - end - - spec.subspec 'QuerySet' do |queryset_spec| - queryset_spec.subspec 'ObjectiveC' do |objc_spec| - objc_spec.source_files = 'QueryKit/ObjectiveC/QKQuerySet.{h,m}' - end - - queryset_spec.subspec 'Swift' do |swift_spec| - swift_spec.source_files = 'QueryKit/QuerySet.swift' - - swift_spec.ios.deployment_target = '8.0' - swift_spec.osx.deployment_target = '10.9' - end - - queryset_spec.subspec 'Bridge' do |bridge_spec| - bridge_spec.dependency 'QueryKit/QuerySet/Swift' - bridge_spec.dependency 'QueryKit/QuerySet/ObjectiveC' - bridge_spec.source_files = 'QueryKit/ObjectiveC/QKQuerySet.swift' - - bridge_spec.ios.deployment_target = '8.0' - bridge_spec.osx.deployment_target = '10.9' - end - end + spec.swift_versions = ['5.0', '5.1'] + spec.ios.deployment_target = '8.0' + spec.osx.deployment_target = '10.9' + spec.watchos.deployment_target = '2.0' + spec.tvos.deployment_target = '9.0' + spec.frameworks = 'CoreData' + spec.source_files = 'Sources/QueryKit/*.swift' end diff --git a/QueryKit.xcodeproj/project.pbxproj b/QueryKit.xcodeproj/project.pbxproj deleted file mode 100644 index 73622be..0000000 --- a/QueryKit.xcodeproj/project.pbxproj +++ /dev/null @@ -1,612 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 77007D7D19A95CDE007DC2BC /* QKQuerySetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77007D7C19A95CDE007DC2BC /* QKQuerySetTests.swift */; }; - 77007D8119A95CE9007DC2BC /* QKQuerySet.h in Headers */ = {isa = PBXBuildFile; fileRef = 77007D7E19A95CE9007DC2BC /* QKQuerySet.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 77007D8219A95CE9007DC2BC /* QKQuerySet.m in Sources */ = {isa = PBXBuildFile; fileRef = 77007D7F19A95CE9007DC2BC /* QKQuerySet.m */; }; - 77007D8319A95CE9007DC2BC /* QKQuerySet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77007D8019A95CE9007DC2BC /* QKQuerySet.swift */; }; - 775E2A1B19B4981E007FE5BA /* QKQuerySetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 775E2A1A19B4981E007FE5BA /* QKQuerySetTests.m */; }; - 77A9B67F195374490016654E /* QueryKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 77A9B67E195374490016654E /* QueryKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 77A9B685195374490016654E /* QueryKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77A9B679195374490016654E /* QueryKit.framework */; }; - 77A9B68C195374490016654E /* QueryKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A9B68B195374490016654E /* QueryKitTests.swift */; }; - 77A9B698195374AA0016654E /* QueryKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A9B697195374AA0016654E /* QueryKit.swift */; }; - 77B17B8519A94C9100D6540D /* QKAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 77B17B8319A94C9100D6540D /* QKAttribute.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 77B17B8619A94C9100D6540D /* QKAttribute.m in Sources */ = {isa = PBXBuildFile; fileRef = 77B17B8419A94C9100D6540D /* QKAttribute.m */; }; - 77B17B8819A94D2C00D6540D /* QKAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B17B8719A94D2C00D6540D /* QKAttribute.swift */; }; - 77B17B8B19A94D4C00D6540D /* QKAttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B17B8A19A94D4C00D6540D /* QKAttributeTests.swift */; }; - 77E3A05D1969C019009372A8 /* QuerySet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E3A05C1969C019009372A8 /* QuerySet.swift */; }; - 77E3A05F1969C047009372A8 /* QuerySetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E3A05E1969C047009372A8 /* QuerySetTests.swift */; }; - 77E3A0611969DDF5009372A8 /* Expression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E3A0601969DDF5009372A8 /* Expression.swift */; }; - 77E3A0631969E003009372A8 /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E3A0621969E003009372A8 /* ExpressionTests.swift */; }; - 77E8728119539C0900A6F13F /* Attribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E8728019539C0900A6F13F /* Attribute.swift */; }; - 77E8728319539C2A00A6F13F /* AttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E8728219539C2A00A6F13F /* AttributeTests.swift */; }; - 77E8728519539FC000A6F13F /* PredicateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E8728419539FC000A6F13F /* PredicateTests.swift */; }; - 77E8728719539FD200A6F13F /* Predicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E8728619539FD200A6F13F /* Predicate.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 772A282A196C8D7800992BAB /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77A9B66E1953742F0016654E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77A9B678195374490016654E; - remoteInfo = QueryKit; - }; - 77A9B686195374490016654E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77A9B66E1953742F0016654E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77A9B678195374490016654E; - remoteInfo = QueryKit; - }; - 77A9B6931953744A0016654E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77A9B66E1953742F0016654E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77A9B678195374490016654E; - remoteInfo = QueryKit; - }; - 77A9B6951953744A0016654E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 77A9B66E1953742F0016654E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 77A9B678195374490016654E; - remoteInfo = QueryKit; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 279294E51A4B4C60009C52E1 /* UniversalFramework_Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Base.xcconfig; sourceTree = ""; }; - 279294E61A4B4C60009C52E1 /* UniversalFramework_Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Framework.xcconfig; sourceTree = ""; }; - 279294E71A4B4C60009C52E1 /* UniversalFramework_Test.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Test.xcconfig; sourceTree = ""; }; - 77007D7C19A95CDE007DC2BC /* QKQuerySetTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QKQuerySetTests.swift; sourceTree = ""; }; - 77007D7E19A95CE9007DC2BC /* QKQuerySet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QKQuerySet.h; sourceTree = ""; }; - 77007D7F19A95CE9007DC2BC /* QKQuerySet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QKQuerySet.m; sourceTree = ""; }; - 77007D8019A95CE9007DC2BC /* QKQuerySet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QKQuerySet.swift; sourceTree = ""; }; - 775E2A1919B4981D007FE5BA /* QueryKitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "QueryKitTests-Bridging-Header.h"; sourceTree = ""; }; - 775E2A1A19B4981E007FE5BA /* QKQuerySetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QKQuerySetTests.m; sourceTree = ""; }; - 77A9B679195374490016654E /* QueryKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = QueryKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 77A9B67D195374490016654E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 77A9B67E195374490016654E /* QueryKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QueryKit.h; sourceTree = ""; }; - 77A9B684195374490016654E /* QueryKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QueryKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 77A9B68A195374490016654E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 77A9B68B195374490016654E /* QueryKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryKitTests.swift; sourceTree = ""; }; - 77A9B697195374AA0016654E /* QueryKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryKit.swift; sourceTree = ""; }; - 77B17B8319A94C9100D6540D /* QKAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QKAttribute.h; sourceTree = ""; }; - 77B17B8419A94C9100D6540D /* QKAttribute.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QKAttribute.m; sourceTree = ""; }; - 77B17B8719A94D2C00D6540D /* QKAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QKAttribute.swift; sourceTree = ""; }; - 77B17B8A19A94D4C00D6540D /* QKAttributeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QKAttributeTests.swift; sourceTree = ""; }; - 77E3A05C1969C019009372A8 /* QuerySet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuerySet.swift; sourceTree = ""; }; - 77E3A05E1969C047009372A8 /* QuerySetTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuerySetTests.swift; sourceTree = ""; }; - 77E3A0601969DDF5009372A8 /* Expression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Expression.swift; sourceTree = ""; }; - 77E3A0621969E003009372A8 /* ExpressionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpressionTests.swift; sourceTree = ""; }; - 77E8728019539C0900A6F13F /* Attribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Attribute.swift; sourceTree = ""; }; - 77E8728219539C2A00A6F13F /* AttributeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributeTests.swift; sourceTree = ""; }; - 77E8728419539FC000A6F13F /* PredicateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateTests.swift; sourceTree = ""; }; - 77E8728619539FD200A6F13F /* Predicate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Predicate.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 77A9B675195374490016654E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77A9B681195374490016654E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 77A9B685195374490016654E /* QueryKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 279294E41A4B4C60009C52E1 /* Configurations */ = { - isa = PBXGroup; - children = ( - 279294E51A4B4C60009C52E1 /* UniversalFramework_Base.xcconfig */, - 279294E61A4B4C60009C52E1 /* UniversalFramework_Framework.xcconfig */, - 279294E71A4B4C60009C52E1 /* UniversalFramework_Test.xcconfig */, - ); - path = Configurations; - sourceTree = ""; - }; - 77A9B66D1953742F0016654E = { - isa = PBXGroup; - children = ( - 77A9B67B195374490016654E /* QueryKit */, - 77A9B688195374490016654E /* QueryKitTests */, - 279294E41A4B4C60009C52E1 /* Configurations */, - 77A9B67A195374490016654E /* Products */, - ); - sourceTree = ""; - }; - 77A9B67A195374490016654E /* Products */ = { - isa = PBXGroup; - children = ( - 77A9B679195374490016654E /* QueryKit.framework */, - 77A9B684195374490016654E /* QueryKitTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 77A9B67B195374490016654E /* QueryKit */ = { - isa = PBXGroup; - children = ( - 77A9B67E195374490016654E /* QueryKit.h */, - 77A9B697195374AA0016654E /* QueryKit.swift */, - 77E3A05C1969C019009372A8 /* QuerySet.swift */, - 77E8728019539C0900A6F13F /* Attribute.swift */, - 77E3A0601969DDF5009372A8 /* Expression.swift */, - 77E8728619539FD200A6F13F /* Predicate.swift */, - 77B17B8219A94C9100D6540D /* ObjectiveC */, - 77A9B67C195374490016654E /* Supporting Files */, - ); - path = QueryKit; - sourceTree = ""; - }; - 77A9B67C195374490016654E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 77A9B67D195374490016654E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 77A9B688195374490016654E /* QueryKitTests */ = { - isa = PBXGroup; - children = ( - 77A9B68B195374490016654E /* QueryKitTests.swift */, - 77E3A05E1969C047009372A8 /* QuerySetTests.swift */, - 77E3A0621969E003009372A8 /* ExpressionTests.swift */, - 77E8728219539C2A00A6F13F /* AttributeTests.swift */, - 77B17B8919A94D4C00D6540D /* ObjectiveC */, - 77A9B689195374490016654E /* Supporting Files */, - ); - path = QueryKitTests; - sourceTree = ""; - }; - 77A9B689195374490016654E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 77E8728419539FC000A6F13F /* PredicateTests.swift */, - 77A9B68A195374490016654E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 77B17B8219A94C9100D6540D /* ObjectiveC */ = { - isa = PBXGroup; - children = ( - 77B17B8319A94C9100D6540D /* QKAttribute.h */, - 77B17B8419A94C9100D6540D /* QKAttribute.m */, - 77B17B8719A94D2C00D6540D /* QKAttribute.swift */, - 77007D7E19A95CE9007DC2BC /* QKQuerySet.h */, - 77007D7F19A95CE9007DC2BC /* QKQuerySet.m */, - 77007D8019A95CE9007DC2BC /* QKQuerySet.swift */, - ); - path = ObjectiveC; - sourceTree = ""; - }; - 77B17B8919A94D4C00D6540D /* ObjectiveC */ = { - isa = PBXGroup; - children = ( - 775E2A1919B4981D007FE5BA /* QueryKitTests-Bridging-Header.h */, - 77B17B8A19A94D4C00D6540D /* QKAttributeTests.swift */, - 775E2A1A19B4981E007FE5BA /* QKQuerySetTests.m */, - 77007D7C19A95CDE007DC2BC /* QKQuerySetTests.swift */, - ); - path = ObjectiveC; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 77A9B676195374490016654E /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 77A9B67F195374490016654E /* QueryKit.h in Headers */, - 77B17B8519A94C9100D6540D /* QKAttribute.h in Headers */, - 77007D8119A95CE9007DC2BC /* QKQuerySet.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 77A9B678195374490016654E /* QueryKit */ = { - isa = PBXNativeTarget; - buildConfigurationList = 77A9B68D195374490016654E /* Build configuration list for PBXNativeTarget "QueryKit" */; - buildPhases = ( - 77A9B674195374490016654E /* Sources */, - 77A9B675195374490016654E /* Frameworks */, - 77A9B676195374490016654E /* Headers */, - 77A9B677195374490016654E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = QueryKit; - productName = QueryKit; - productReference = 77A9B679195374490016654E /* QueryKit.framework */; - productType = "com.apple.product-type.framework"; - }; - 77A9B683195374490016654E /* QueryKitTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 77A9B690195374490016654E /* Build configuration list for PBXNativeTarget "QueryKitTests" */; - buildPhases = ( - 77A9B680195374490016654E /* Sources */, - 77A9B681195374490016654E /* Frameworks */, - 77A9B682195374490016654E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 77A9B687195374490016654E /* PBXTargetDependency */, - 77A9B6941953744A0016654E /* PBXTargetDependency */, - 77A9B6961953744A0016654E /* PBXTargetDependency */, - 772A282B196C8D7800992BAB /* PBXTargetDependency */, - ); - name = QueryKitTests; - productName = QueryKitTests; - productReference = 77A9B684195374490016654E /* QueryKitTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 77A9B66E1953742F0016654E /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0600; - TargetAttributes = { - 77A9B678195374490016654E = { - CreatedOnToolsVersion = 6.0; - }; - 77A9B683195374490016654E = { - CreatedOnToolsVersion = 6.0; - TestTargetID = 77A9B678195374490016654E; - }; - }; - }; - buildConfigurationList = 77A9B6711953742F0016654E /* Build configuration list for PBXProject "QueryKit" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 77A9B66D1953742F0016654E; - productRefGroup = 77A9B67A195374490016654E /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 77A9B678195374490016654E /* QueryKit */, - 77A9B683195374490016654E /* QueryKitTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 77A9B677195374490016654E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77A9B682195374490016654E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 77A9B674195374490016654E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 77E8728119539C0900A6F13F /* Attribute.swift in Sources */, - 77E8728719539FD200A6F13F /* Predicate.swift in Sources */, - 77B17B8619A94C9100D6540D /* QKAttribute.m in Sources */, - 77B17B8819A94D2C00D6540D /* QKAttribute.swift in Sources */, - 77A9B698195374AA0016654E /* QueryKit.swift in Sources */, - 77007D8319A95CE9007DC2BC /* QKQuerySet.swift in Sources */, - 77007D8219A95CE9007DC2BC /* QKQuerySet.m in Sources */, - 77E3A05D1969C019009372A8 /* QuerySet.swift in Sources */, - 77E3A0611969DDF5009372A8 /* Expression.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 77A9B680195374490016654E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 77B17B8B19A94D4C00D6540D /* QKAttributeTests.swift in Sources */, - 77E8728319539C2A00A6F13F /* AttributeTests.swift in Sources */, - 77A9B68C195374490016654E /* QueryKitTests.swift in Sources */, - 77007D7D19A95CDE007DC2BC /* QKQuerySetTests.swift in Sources */, - 77E3A05F1969C047009372A8 /* QuerySetTests.swift in Sources */, - 775E2A1B19B4981E007FE5BA /* QKQuerySetTests.m in Sources */, - 77E8728519539FC000A6F13F /* PredicateTests.swift in Sources */, - 77E3A0631969E003009372A8 /* ExpressionTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 772A282B196C8D7800992BAB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77A9B678195374490016654E /* QueryKit */; - targetProxy = 772A282A196C8D7800992BAB /* PBXContainerItemProxy */; - }; - 77A9B687195374490016654E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77A9B678195374490016654E /* QueryKit */; - targetProxy = 77A9B686195374490016654E /* PBXContainerItemProxy */; - }; - 77A9B6941953744A0016654E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77A9B678195374490016654E /* QueryKit */; - targetProxy = 77A9B6931953744A0016654E /* PBXContainerItemProxy */; - }; - 77A9B6961953744A0016654E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 77A9B678195374490016654E /* QueryKit */; - targetProxy = 77A9B6951953744A0016654E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 77A9B6721953742F0016654E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.9; - }; - name = Debug; - }; - 77A9B6731953742F0016654E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - MACOSX_DEPLOYMENT_TARGET = 10.9; - }; - name = Release; - }; - 77A9B68E195374490016654E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 279294E71A4B4C60009C52E1 /* UniversalFramework_Test.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - FRAMEWORK_VERSION = A; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = QueryKit/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; - METAL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 77A9B68F195374490016654E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 279294E61A4B4C60009C52E1 /* UniversalFramework_Framework.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - FRAMEWORK_VERSION = A; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = QueryKit/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; - METAL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 77A9B691195374490016654E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 279294E71A4B4C60009C52E1 /* UniversalFramework_Test.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = QueryKitTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; - METAL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_OBJC_BRIDGING_HEADER = "QueryKitTests/ObjectiveC/QueryKitTests-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 77A9B692195374490016654E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 279294E61A4B4C60009C52E1 /* UniversalFramework_Framework.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = QueryKitTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; - METAL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_OBJC_BRIDGING_HEADER = "QueryKitTests/ObjectiveC/QueryKitTests-Bridging-Header.h"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 77A9B6711953742F0016654E /* Build configuration list for PBXProject "QueryKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77A9B6721953742F0016654E /* Debug */, - 77A9B6731953742F0016654E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 77A9B68D195374490016654E /* Build configuration list for PBXNativeTarget "QueryKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77A9B68E195374490016654E /* Debug */, - 77A9B68F195374490016654E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 77A9B690195374490016654E /* Build configuration list for PBXNativeTarget "QueryKitTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 77A9B691195374490016654E /* Debug */, - 77A9B692195374490016654E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 77A9B66E1953742F0016654E /* Project object */; -} diff --git a/QueryKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/QueryKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1639ef8..0000000 --- a/QueryKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/QueryKit.xcodeproj/xcshareddata/xcschemes/QueryKit.xcscheme b/QueryKit.xcodeproj/xcshareddata/xcschemes/QueryKit.xcscheme deleted file mode 100644 index 2fdfdb6..0000000 --- a/QueryKit.xcodeproj/xcshareddata/xcschemes/QueryKit.xcscheme +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/QueryKit/Attribute.swift b/QueryKit/Attribute.swift deleted file mode 100644 index 64eb6d8..0000000 --- a/QueryKit/Attribute.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// Attribute.swift -// QueryKit -// -// Created by Kyle Fuller on 19/06/2014. -// -// - -import Foundation - -public struct Attribute : Equatable { - public let name:String - - public init(_ name:String) { - self.name = name - } - - /// Builds a compound attribute with other key paths - public init(attributes:Array) { - self.init(".".join(attributes)) - } - - public var expression:NSExpression { - return NSExpression(forKeyPath: name) - } - - // MARK: Sorting - - public func ascending() -> NSSortDescriptor { - return NSSortDescriptor(key: name, ascending: true) - } - - public func descending() -> NSSortDescriptor { - return NSSortDescriptor(key: name, ascending: false) - } - - func expressionForValue(value:T) -> NSExpression { - // TODO: Find a cleaner implementation - if let value = value as? NSObject { - return NSExpression(forConstantValue: value as NSObject) - } - - if sizeof(value.dynamicType) == 8 { - let value = unsafeBitCast(value, Optional.self) - if let value = value { - return NSExpression(forConstantValue: value) - } - } - - let value = unsafeBitCast(value, Optional.self) - if let value = value { - return NSExpression(forConstantValue: value) - } - - return NSExpression(forConstantValue: NSNull()) - } -} - -public func == (lhs: Attribute, rhs: Attribute) -> Bool { - return lhs.name == rhs.name -} - -public func == (left: Attribute, right: T) -> NSPredicate { - return left.expression == left.expressionForValue(right) -} - -public func != (left: Attribute, right: T) -> NSPredicate { - return left.expression != left.expressionForValue(right) -} - -public func > (left: Attribute, right: T) -> NSPredicate { - return left.expression > left.expressionForValue(right) -} - -public func >= (left: Attribute, right: T) -> NSPredicate { - return left.expression >= left.expressionForValue(right) -} - -public func < (left: Attribute, right: T) -> NSPredicate { - return left.expression < left.expressionForValue(right) -} - -public func <= (left: Attribute, right: T) -> NSPredicate { - return left.expression <= left.expressionForValue(right) -} - -public func ~= (left: Attribute, right: T) -> NSPredicate { - return left.expression ~= left.expressionForValue(right) -} - -public func << (left: Attribute, right: [T]) -> NSPredicate { - let value = map(right) { value in return value as! NSObject } - return left.expression << NSExpression(forConstantValue: value) -} - -public func << (left: Attribute, right: Range) -> NSPredicate { - let value = [right.startIndex as! NSObject, right.endIndex as! NSObject] as NSArray - let rightExpression = NSExpression(forConstantValue: value) - - return NSComparisonPredicate(leftExpression: left.expression, rightExpression: rightExpression, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.BetweenPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -/// MARK: Bool Attributes - -prefix public func ! (left: Attribute) -> NSPredicate { - return left == false -} - -public extension QuerySet { - public func filter(attribute:Attribute) -> QuerySet { - return filter(attribute == true) - } - - public func exclude(attribute:Attribute) -> QuerySet { - return filter(attribute == false) - } -} - -// MARK: Collections - -public func count(attribute:Attribute) -> Attribute { - return Attribute(attributes: [attribute.name, "@count"]) -} - -public func count(attribute:Attribute) -> Attribute { - return Attribute(attributes: [attribute.name, "@count"]) -} diff --git a/QueryKit/Expression.swift b/QueryKit/Expression.swift deleted file mode 100644 index ddd3630..0000000 --- a/QueryKit/Expression.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// Expression.swift -// QueryKit -// -// Created by Kyle Fuller on 06/07/2014. -// -// - -import Foundation - -public func == (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.EqualToPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func != (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.NotEqualToPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func > (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.GreaterThanPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func >= (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.GreaterThanOrEqualToPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func < (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.LessThanPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func <= (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.LessThanOrEqualToPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func ~= (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.LikePredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} - -public func << (left: NSExpression, right: NSExpression) -> NSPredicate { - return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicateModifier.DirectPredicateModifier, type: NSPredicateOperatorType.InPredicateOperatorType, options: NSComparisonPredicateOptions(0)) -} diff --git a/QueryKit/Info.plist b/QueryKit/Info.plist deleted file mode 100644 index c8d5065..0000000 --- a/QueryKit/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - org.cocode.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - ${CURRENT_PROJECT_VERSION} - NSPrincipalClass - - - diff --git a/QueryKit/ObjectiveC/QKAttribute.h b/QueryKit/ObjectiveC/QKAttribute.h deleted file mode 100644 index efcd328..0000000 --- a/QueryKit/ObjectiveC/QKAttribute.h +++ /dev/null @@ -1,210 +0,0 @@ -// -// QKAttribute.h -// QueryKit -// -// Created by Kyle Fuller on 30/04/2013. -// -// - -#import - -/** A helper class to generate predicates and sort descriptors for attributes - on a managed object. - */ -@interface QKAttribute : NSObject - -@property (nonatomic, strong, readonly, nonnull) NSString *name; - -/// Initialized the attribute from multiple other attributes -- (nonnull instancetype)initWithAttributes:(nonnull QKAttribute *)attribute, ... NS_REQUIRES_NIL_TERMINATION; - -/// Initialized the attribute with the given name -- (nonnull instancetype)initWithName:(nonnull NSString *)name; - -/** Returns a Boolean value that indicates whether a given attribute is equal to the receiver - @param attribute The attribute to compare against the receiver - @return YES if attribute is equivalent to the receiver - */ -- (BOOL)isEqualToAttribute:(nonnull QKAttribute *)attribute; - -/** Returns an expression for the attributes key-value path */ -- (nonnull NSExpression *)expression; - -@end - -@interface QKAttribute (Predicate) - -/** Returns a predicate for an equality comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see equal: - */ -- (nonnull NSPredicate *)equal:(nullable id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for an equality comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see equal:options: - */ -- (nonnull NSPredicate *)equal:(nullable id)value; - -/** Returns a predicate for an unequal comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see notEqual: - */ -- (nonnull NSPredicate *)notEqual:(nullable id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for an unequal comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see notEqual:options: - */ -- (nonnull NSPredicate *)notEqual:(nullable id)value; - -/** Returns a predicate for a like comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see like: - */ -- (nonnull NSPredicate *)like:(nonnull id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for a like comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see like:options: - */ -- (nonnull NSPredicate *)like:(nonnull id)value; - -/** Returns a predicate for a matches comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see like: - */ -- (nonnull NSPredicate *)matches:(nonnull id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for a matches comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see like:options: - */ -- (nonnull NSPredicate *)matches:(nonnull id)value; - -/** Returns a predicate for a begins with comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see like: - */ -- (nonnull NSPredicate *)beginsWith:(nonnull id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for a begins with comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see like:options: - */ -- (nonnull NSPredicate *)beginsWith:(nonnull id)value; - -/** Returns a predicate for a ends with comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see like: - */ -- (nonnull NSPredicate *)endsWith:(nonnull id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for a ends with comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see like:options: - */ -- (nonnull NSPredicate *)endsWith:(nonnull id)value; - -/** Returns a predicate for greater than the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see greaterThanOrEqualTo: - */ -- (nonnull NSPredicate *)greaterThan:(nonnull id)value; - -/** Returns a predicate for greater than or equal to the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see greaterThan: - */ -- (nonnull NSPredicate *)greaterThanOrEqualTo:(nonnull id)value; - -/** Returns a predicate for less than the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see lessThanOrEqualTo: - */ -- (nonnull NSPredicate *)lessThan:(nonnull id)value; - -/** Returns a predicate for less than or equal to the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see lessThan: - */ -- (nonnull NSPredicate *)lessThanOrEqualTo:(nonnull id)value; - -/** Returns a predicate for attribute being between two values - @param minimumValue - @param maximumValue - @return The predicate for this comparison - */ -- (nonnull NSPredicate *)between:(nonnull id)minimumValue and:(nonnull id)maxiumValue; - -/** Returns an IN predicate for attribute - @param set An enumerable object containing a set ob objects - @return The predicate for this comparison - */ -- (nonnull NSPredicate *)in:(nonnull id)set; - -/** Returns a predicate for a contains with comparison against the supplied value - @param value To compare against the attribute - @param options NSComparisonPredicateOptions to apply to the comparison - @return The predicate for this comparison - @see contains: - */ -- (nonnull NSPredicate *)contains:(nonnull id)value options:(NSComparisonPredicateOptions)options; - -/** Returns a predicate for a contains with comparison against the supplied value - @param value To compare against the attribute - @return The predicate for this comparison - @see contains:options: - */ -- (nonnull NSPredicate *)contains:(nonnull id)value; - -/** Returns a predicate for if the attribute being equal to nil - @return The predicate for the attribute being nil. - */ -- (nonnull NSPredicate *)isNil; - -/** Returns a predicate for if the attribute being equal to YES - @return The predicate for the attribute being YES. - @see isNO - */ -- (nonnull NSPredicate *)isYes; - -/** Returns a predicate for if the attribute being equal to NO - @return The predicate for the attribute being NO. - @see isYes - */ -- (nonnull NSPredicate *)isNo; - -@end - -@interface QKAttribute (Sorting) - -/** Returns an ascending sort descriptor for this attribute */ -- (nonnull NSSortDescriptor *)ascending; - -/** Returns a descending sort descriptor for this attribute */ -- (nonnull NSSortDescriptor *)descending; - -@end diff --git a/QueryKit/ObjectiveC/QKAttribute.m b/QueryKit/ObjectiveC/QKAttribute.m deleted file mode 100644 index 2fce279..0000000 --- a/QueryKit/ObjectiveC/QKAttribute.m +++ /dev/null @@ -1,315 +0,0 @@ -// -// QKAttribute.m -// QueryKit -// -// Created by Kyle Fuller on 30/04/2013. -// -// - -#import "QKAttribute.h" - -@implementation QKAttribute - -#pragma mark - NSCoding - -+ (BOOL)supportsSecureCoding { - return YES; -} - -- (void)encodeWithCoder:(NSCoder *)encoder { - [encoder encodeObject:self.name forKey:@"name"]; -} - -- (instancetype)initWithCoder:(NSCoder *)decoder { - NSString *name = [decoder decodeObjectOfClass:[NSString class] forKey:@"name"]; - return [self initWithName:name]; -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - NSString *name = [self.name copyWithZone:zone]; - return [[[self class] alloc] initWithName:name]; -} - -#pragma mark - - -- (instancetype)initWithAttributes:(QKAttribute *)attribute, ... { - NSParameterAssert(attribute != nil); - - NSMutableArray *attributes = [NSMutableArray arrayWithObject:attribute.name]; - - va_list attributeList; - va_start(attributeList, attribute); - while ((attribute = va_arg(attributeList, id))) { - [attributes addObject:attribute.name]; - } - va_end(attributeList); - - NSString *name = [attributes componentsJoinedByString:@"."]; - - if (self = [super init]) { - _name = name; - } - - return self; -} - -- (instancetype)initWithName:(NSString *)name { - if (self = [super init]) { - _name = name; - } - - return self; -} - -- (NSExpression *)expression { - return [NSExpression expressionForKeyPath:self.name]; -} - -#pragma mark - Equality - -- (NSUInteger)hash { - return [self.name hash]; -} - -- (BOOL)isEqualToAttribute:(QKAttribute *)attribute { - return [self.name isEqualToString:attribute.name]; -} - -- (BOOL)isEqual:(id)object { - return object == self || ([object isKindOfClass:[QKAttribute class]] && [self isEqualToAttribute:object]); -} - -#pragma mark - - -// The following methods are implemented so that `[NSPredicate predicateWithFormat:@"%K", attribute]` will work - -- (NSString *)description { - return self.name; -} - -- (NSRange)rangeOfString:(NSString *)aString { - return [self.name rangeOfString:aString]; -} - -- (NSArray *)componentsSeparatedByString:(NSString *)separator { - return [self.name componentsSeparatedByString:separator]; -} - -- (NSUInteger)length { - return [self.name length]; -} - -#pragma mark - Comparison - -- (NSPredicate *)predicateWithRightExpression:(NSExpression *)expression - modifier:(NSComparisonPredicateModifier)modifier - type:(NSPredicateOperatorType)type - options:(NSComparisonPredicateOptions)options -{ - NSExpression *leftExpression = [self expression]; - - return [NSComparisonPredicate predicateWithLeftExpression:leftExpression - rightExpression:expression - modifier:modifier - type:type - options:options]; -} - -@end - -@implementation QKAttribute (Predicate) - -- (NSPredicate *)equal:(id)value options:(NSComparisonPredicateOptions)options { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSEqualToPredicateOperatorType - options:options]; -} - -- (NSPredicate *)equal:(id)value { - return [self equal:value options:0]; -} - -- (NSPredicate *)notEqual:(id)value options:(NSComparisonPredicateOptions)options { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSNotEqualToPredicateOperatorType - options:options]; -} - -- (NSPredicate *)notEqual:(id)value { - return [self notEqual:value options:0]; -} - -- (NSPredicate *)beginsWith:(id)value options:(NSComparisonPredicateOptions)options { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSBeginsWithPredicateOperatorType - options:options]; -} - -- (NSPredicate *)beginsWith:(id)value { - return [self beginsWith:value options:0]; -} - -- (NSPredicate *)endsWith:(id)value options:(NSComparisonPredicateOptions)options { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSEndsWithPredicateOperatorType - options:options]; -} - -- (NSPredicate *)endsWith:(id)value { - return [self endsWith:value options:0]; -} - -- (NSPredicate *)like:(id)value options:(NSComparisonPredicateOptions)options { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSLikePredicateOperatorType - options:options]; -} - -- (NSPredicate *)like:(id)value { - return [self like:value options:0]; -} - -- (NSPredicate *)matches:(id)value options:(NSComparisonPredicateOptions)options { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSMatchesPredicateOperatorType - options:options]; -} - -- (NSPredicate *)matches:(id)value { - return [self matches:value options:0]; -} - -- (NSPredicate *)greaterThan:(id)value { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSGreaterThanPredicateOperatorType - options:0]; -} - -- (NSPredicate *)greaterThanOrEqualTo:(id)value { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSGreaterThanOrEqualToPredicateOperatorType - options:0]; -} - -- (NSPredicate *)lessThan:(id)value { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSLessThanPredicateOperatorType - options:0]; -} - -- (NSPredicate *)lessThanOrEqualTo:(id)value { - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSLessThanOrEqualToPredicateOperatorType - options:0]; -} - -- (NSPredicate *)isNil { - NSExpression *expression = [NSExpression expressionForConstantValue:nil]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSEqualToPredicateOperatorType - options:0]; -} - -- (NSPredicate *)between:(id)minimumValue and:(id)maxiumValue { - NSParameterAssert(minimumValue != nil); - NSParameterAssert(maxiumValue != nil); - - NSExpression *expression = [NSExpression expressionForConstantValue:@[minimumValue, maxiumValue]]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSBetweenPredicateOperatorType - options:0]; -} - -- (NSPredicate *)in:(id)set { - NSParameterAssert(set != nil); - - NSExpression *expression = [NSExpression expressionForConstantValue:set]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSInPredicateOperatorType - options:0]; -} - -- (NSPredicate *)contains:(id)value options:(NSComparisonPredicateOptions)options { - NSParameterAssert(value != nil); - - NSExpression *expression = [NSExpression expressionForConstantValue:value]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSContainsPredicateOperatorType - options:options]; -} - -- (NSPredicate *)contains:(id)value { - return [self contains:value options:0]; -} - -- (NSPredicate *)isYes { - NSExpression *expression = [NSExpression expressionForConstantValue:@YES]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSEqualToPredicateOperatorType - options:0]; -} - -- (NSPredicate *)isNo { - NSExpression *expression = [NSExpression expressionForConstantValue:@NO]; - - return [self predicateWithRightExpression:expression - modifier:NSDirectPredicateModifier - type:NSEqualToPredicateOperatorType - options:0]; -} - -@end - -@implementation QKAttribute (Sorting) - -- (NSSortDescriptor *)ascending { - return [[NSSortDescriptor alloc] initWithKey:self.name ascending:YES]; -} - -- (NSSortDescriptor *)descending { - return [[NSSortDescriptor alloc] initWithKey:self.name ascending:NO]; -} - -@end diff --git a/QueryKit/ObjectiveC/QKAttribute.swift b/QueryKit/ObjectiveC/QKAttribute.swift deleted file mode 100644 index 148c615..0000000 --- a/QueryKit/ObjectiveC/QKAttribute.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation - -extension Attribute { - public func asQKAttribute() -> QKAttribute { - return QKAttribute(name: name) - } -} - -extension QKAttribute { - public func asAttribute() -> Attribute { - return Attribute(name) - } -} diff --git a/QueryKit/ObjectiveC/QKQuerySet.h b/QueryKit/ObjectiveC/QKQuerySet.h deleted file mode 100644 index 9848fdd..0000000 --- a/QueryKit/ObjectiveC/QKQuerySet.h +++ /dev/null @@ -1,156 +0,0 @@ -// -// QKQuerySet.h -// QueryKit -// -// Created by Kyle Fuller on 30/04/2013. -// -// - -#import -#import - -/** - Represents a lazy Core Data lookup for a set of objects. - - This object is immutable, any changes will normally be done to a copy. Such - as with the `-filter:`, `-exclude:`, `-orderBy:` and `-reverse` methods. - */ - -@interface QKQuerySet : NSObject - -/// The managed object context for the query set -@property (nonatomic, strong, readonly, nonnull) NSManagedObjectContext *managedObjectContext; - -/// The entity descriptor for the object -@property (nonatomic, strong, readonly, nonnull) NSEntityDescription *entityDescription; - -/** This is a read only property to hold any predicates set on this object. You can use the `filter:` and `exclude:` methods to effect this value on a child */ -@property (nonatomic, copy, readonly, nullable) NSPredicate *predicate; - -/** This is a read only property to hold any sort descriptors set on this object. You can use the `orderBy:` and `reverse` methods to effect this value on a child */ -@property (nonatomic, copy, readonly, nonnull) NSArray *sortDescriptors; - -/** This is a read only property to hold a range set. */ -@property (nonatomic, assign, readonly) NSRange range; - -#pragma mark - Creation - -- (nonnull instancetype)initWithManagedObjectContext:(NSManagedObjectContext * __nonnull)managedObjectContext entityDescription:(NSEntityDescription * __nonnull)entityDescription; -- (nonnull instancetype)initWithManagedObjectContext:(NSManagedObjectContext * __nonnull)managedObjectContext entityDescription:(NSEntityDescription * __nonnull)entityDescription predicate:(NSPredicate * __nullable)predicate sortDescriptors:(NSArray * __nullable)sortDescriptors range:(NSRange)range; -- (nonnull instancetype)initWithManagedObjectContext:(NSManagedObjectContext * __nonnull)managedObjectContext fetchRequest:(NSFetchRequest * __nonnull)fetchRequest; - -#pragma mark - Equality - -/** Returns a Boolean value that indicates whether a given queryset is equal to the receiver - @param queryset The queryset to compare against the receiver - @return YES if queryset is equivalent to the receiver - */ -- (BOOL)isEqualToQuerySet:(nonnull QKQuerySet *)queryset; - -#pragma mark - - -/** Returns a fetch request for the queryset */ -- (nonnull NSFetchRequest *)fetchRequest; - -/** Returns the amount of objects matching the set predicate - @param error If there is a problem fetching the count, upon return contains an instance of NSError that describes the problem. - @return The number of objects matching the set predicate - */ -- (NSUInteger)count:(NSError * __nullable* __nullable)error; - -/** Returns all objects matching the set predicate ordered by any set sort descriptors as an array - @param error If there is a problem fetching the objects, upon return contains an instance of NSError that describes the problem. - @return An array containing all matched objects - */ -- (nullable NSArray *)array:(NSError * __nullable* __nullable)error; - -/** Returns all objects matching the set predicate ordered by any set sort descriptors as an ordered set - @param error If there is a problem fetching the objects, upon return contains an instance of NSError that describes the problem. - @return An ordered set containing all matched objects - */ -- (nullable NSSet *)set:(NSError * __nullable* __nullable)error; - -/** Returns all objects matching the set predicate ordered by any set sort descriptors as a set - @param error If there is a problem fetching the objects, upon return contains an instance of NSError that describes the problem. - @return A set containing all matched objects - */ -- (nullable NSOrderedSet *)orderedSet:(NSError * __nullable* __nullable)error; - -#pragma mark - Enumeration - -/** Enumerate all objects matching the set predicate ordered by any set sort descriptors - @param block The block to apply to elements in the array - @param error If there is a problem fetching the objects, upon return contains an instance of NSError that describes the problem. - @return YES if the operation succeeded. - */ -- (BOOL)enumerateObjects:(nonnull void (^)(NSManagedObject * - __nonnull object, NSUInteger index, BOOL * __nonnull stop))block error:(NSError * __nullable* __nullable)error; - -/** Enumerate all objects matching the set predicate ordered by any set sort descriptors - @param block The block to apply to all objects - @param error If there is a problem fetching the objects, upon return contains an instance of NSError that describes the problem. - @return YES if the operation succeeded. - */ -- (BOOL)each:(nonnull void (^)(NSManagedObject * __nonnull managedObject))block error:(NSError * __nullable* __nullable)error; - -#pragma mark - Deletion - -/** Delete all objects matching the set predicate - @param error If there is a problem deleting the objects, upon return contains an instance of NSError that describes the problem. - @return Returns the amount of objects that were deleted - */ -- (NSUInteger)deleteObjects:(NSError * __nullable* __nullable)error; - -@end - -/// Methods to sort an query set -@interface QKQuerySet (Sorting) - -/** Returns a copy and the sort descriptors */ -- (nonnull instancetype)orderBy:(nonnull NSArray * )sortDescriptors; - -/** Returns a copy and reverses any sort descriptors */ -- (nonnull instancetype)reverse; - -@end - -/// Filtering related methods of QKQuerySet -@interface QKQuerySet (Filtering) - -/** Returns a copy filtered by a predicate */ -- (nonnull instancetype)filter:(nonnull NSPredicate *)predicate; - -/** Returns a copy excluding a predicate */ -- (nonnull instancetype)exclude:(nonnull NSPredicate *)predicate; - -@end - -/// Fetching single objects in QKQuerySet -@interface QKQuerySet (SingleObject) - -/** Returns a single object matching the filters, if there is more than one. An error will instead be returned. - @param error If there is a problem fetching the object or there is more than one object, upon return contains an instance of NSError that describes the problem. - @return Returns the object matching the set predicate, or nil. - */ -- (nullable NSManagedObject *)object:(NSError * __nullable* __nullable)error; - -/** Returns the first object matching the filters ordered by the set sort descriptors. - @param error If there is a problem fetching the object, upon return contains an instance of NSError that describes the problem. - @return Returns the first object matching the set predicate, or nil. - */ -- (nullable NSManagedObject *)firstObject:(NSError * __nullable* __nullable)error; - -/** Returns the last object matching the filters ordered by the set sort descriptors. - @param error If there is a problem fetching the object, upon return contains an instance of NSError that describes the problem. - @return Returns the last object matching the set predicate, or nil. - */ -- (nullable NSManagedObject *)lastObject:(NSError * __nullable* __nullable)error; - -@end - - -@interface NSManagedObject (QKQuerySet) - -+ (nonnull QKQuerySet *)querySetWithManagedObjectContext:(nonnull NSManagedObjectContext *)context; - -@end diff --git a/QueryKit/ObjectiveC/QKQuerySet.m b/QueryKit/ObjectiveC/QKQuerySet.m deleted file mode 100644 index 252db94..0000000 --- a/QueryKit/ObjectiveC/QKQuerySet.m +++ /dev/null @@ -1,314 +0,0 @@ -// -// QKQuerySet.m -// QueryKit -// -// Created by Kyle Fuller on 30/04/2013. -// -// - -#import "QKQuerySet.h" - -NSString * const QKQuerySetErrorDomain = @"QKQuerySetErrorDomain"; - -@interface QKQuerySet () - -@property (nonatomic, strong) NSArray *resultsCache; - -@end - -@implementation QKQuerySet - -#pragma mark - Creation - -- (instancetype)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext entityDescription:(NSEntityDescription *)entityDescription { - return [self initWithManagedObjectContext:managedObjectContext entityDescription:entityDescription predicate:nil sortDescriptors:nil range:NSMakeRange(NSNotFound, NSNotFound)]; -} - -- (instancetype)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext fetchRequest:(NSFetchRequest *)fetchRequest { - NSParameterAssert(fetchRequest != nil); - - NSEntityDescription *entityDescription = [fetchRequest entity]; - NSPredicate *predicate = [fetchRequest predicate]; - NSArray *sortDescriptors = [fetchRequest sortDescriptors]; - - return [self initWithManagedObjectContext:managedObjectContext entityDescription:entityDescription predicate:predicate sortDescriptors:sortDescriptors range:NSMakeRange(NSNotFound, NSNotFound)]; -} - -- (instancetype)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext entityDescription:(NSEntityDescription *)entityDescription predicate:(NSPredicate *)predicate sortDescriptors:(NSArray *)sortDescriptors range:(NSRange)range { - NSParameterAssert(managedObjectContext != nil); - NSParameterAssert(entityDescription != nil); - - if (self = [super init]) { - _managedObjectContext = managedObjectContext; - _entityDescription = entityDescription; - _predicate = [predicate copy]; - _sortDescriptors = sortDescriptors? [sortDescriptors copy] : @[]; - _range = range; - } - - return self; -} - -- (instancetype)init { - NSString *reason = [NSString stringWithFormat:@"%@ Failed to call designated initializer.", NSStringFromClass([self class])]; - @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:nil]; -} - -#pragma mark - Equality - -- (NSUInteger)hash { - return [self.managedObjectContext hash]; -} - -- (BOOL)isEqual:(id)object { - if (self == object) { - return YES; - } - - if ([object isKindOfClass:[QKQuerySet class]] == NO) { - return NO; - } - - return [self isEqualToQuerySet:object]; -} - -- (BOOL)isEqualToQuerySet:(QKQuerySet *)queryset { - return ( - [self.managedObjectContext isEqual:[queryset managedObjectContext]] && - [self.entityDescription isEqual:[queryset entityDescription]] && - [self.predicate isEqual:[queryset predicate]] && - [self.sortDescriptors isEqual:[queryset sortDescriptors]] && - NSEqualRanges(self.range, queryset.range) - ); -} - -#pragma mark - NSCopying - -- (instancetype)copyWithZone:(NSZone *)zone { - return [[[self class] allocWithZone:zone] initWithManagedObjectContext:self.managedObjectContext entityDescription:self.entityDescription predicate:self.predicate sortDescriptors:self.sortDescriptors range:self.range]; -} - -#pragma mark - NSFastEnumeration - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { - if (_resultsCache == nil) { - [self array:nil]; - } - - return [_resultsCache countByEnumeratingWithState:state objects:buffer count:len]; -} - -#pragma mark - Fetching - -- (NSFetchRequest *)fetchRequest { - NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; - [fetchRequest setEntity:_entityDescription]; - [fetchRequest setPredicate:self.predicate]; - [fetchRequest setSortDescriptors:self.sortDescriptors]; - - if (self.range.location != NSNotFound) { - fetchRequest.fetchOffset = self.range.location; - fetchRequest.fetchLimit = self.range.length; - } - - return fetchRequest; -} - -- (NSUInteger)count:(NSError **)error { - NSUInteger count = 0; - - if (_resultsCache) { - count = [_resultsCache count]; - } else { - NSFetchRequest *fetchRequest = [self fetchRequest]; - count = [self.managedObjectContext countForFetchRequest:fetchRequest error:error]; - } - - return count; -} - -- (NSArray *)array:(NSError **)error { - if (_resultsCache == nil) { - _resultsCache = [self.managedObjectContext executeFetchRequest:[self fetchRequest] error:error]; - } - - return _resultsCache; -} - -- (NSSet *)set:(NSError **)error { - NSArray *array = [self array:error]; - NSSet *set; - - if (array != nil) { - set = [NSSet setWithArray:array]; - } - - return set; -} - -- (NSOrderedSet *)orderedSet:(NSError **)error { - NSArray *array = [self array:error]; - NSOrderedSet *orderedSet; - - if (array != nil) { - orderedSet = [NSOrderedSet orderedSetWithArray:array]; - } - - return orderedSet; -} - -- (BOOL)enumerateObjects:(void (^)(NSManagedObject *object, NSUInteger index, BOOL *stop))block error:(NSError **)error { - NSArray *array = [self array:error]; - - if (array != nil) { - [array enumerateObjectsUsingBlock:block]; - } - - return array != nil; -} - -- (BOOL)each:(void (^)(NSManagedObject *managedObject))block error:(NSError **)error { - NSArray *array = [self array:error]; - - if (array != nil) { - for (NSManagedObject *managedObject in array) { - block(managedObject); - } - } - - return array != nil; -} - -#pragma mark - Deletion - -- (NSUInteger)deleteObjects:(NSError **)error { - NSArray *array = [self array:error]; - - NSUInteger count = 0; - - if (array != nil) { - NSManagedObjectContext *managedObjectContext = self.managedObjectContext; - - for (NSManagedObject *managedObject in array) { - [managedObjectContext deleteObject:managedObject]; - ++count; - } - } - - return count; -} - -@end - -@implementation QKQuerySet (Sorting) - -- (instancetype)orderBy:(NSArray *)sortDescriptors { - return [[QKQuerySet alloc] initWithManagedObjectContext:_managedObjectContext entityDescription:_entityDescription predicate:_predicate sortDescriptors:sortDescriptors range:self.range]; -} - -- (instancetype)reverse { - NSMutableArray *sortDescriptors = [[NSMutableArray alloc] initWithCapacity:[_sortDescriptors count]]; - - for (NSSortDescriptor *sortDescriptor in _sortDescriptors) { - [sortDescriptors addObject:[sortDescriptor reversedSortDescriptor]]; - } - - return [[QKQuerySet alloc] initWithManagedObjectContext:_managedObjectContext entityDescription:_entityDescription predicate:_predicate sortDescriptors:sortDescriptors range:self.range]; -} - -@end - -@implementation QKQuerySet (Filtering) - -- (instancetype)exclude:(NSPredicate *)predicate { - predicate = [[NSCompoundPredicate alloc] initWithType:NSNotPredicateType subpredicates:@[predicate]]; - - if (_predicate) { - predicate = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[_predicate, predicate]]; - } - - return [[QKQuerySet alloc] initWithManagedObjectContext:_managedObjectContext entityDescription:_entityDescription predicate:predicate sortDescriptors:_sortDescriptors range:self.range]; -} - -- (instancetype)filter:(NSPredicate *)predicate { - if (_predicate) { - predicate = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[_predicate, predicate]]; - } - - return [[QKQuerySet alloc] initWithManagedObjectContext:_managedObjectContext entityDescription:_entityDescription predicate:predicate sortDescriptors:_sortDescriptors range:self.range]; -} - -@end - -@implementation QKQuerySet (SingleObject) - -- (NSManagedObject *)object:(NSError **)error { - NSManagedObject *managedObject; - NSArray *array; - - if (_resultsCache) { - array = _resultsCache; - } else { - NSFetchRequest *fetchRequest = [self fetchRequest]; - fetchRequest.fetchBatchSize = 1; // Only request one - - array = [self.managedObjectContext executeFetchRequest:fetchRequest error:error]; - } - - NSUInteger count = [array count]; - - if (count == 1) { - managedObject = [array firstObject]; - } else if ((count > 1) && error != nil) { - *error = [NSError errorWithDomain:QKQuerySetErrorDomain code:0 userInfo:@{ - NSLocalizedDescriptionKey: @"Find object in fetch request failed, should only result in a single result.", - }]; - } - - return managedObject; -} - -- (NSManagedObject *)firstObject:(NSError **)error { - NSManagedObject *managedObject; - - if (_resultsCache) { - managedObject = [_resultsCache firstObject]; - } else { - NSFetchRequest *fetchRequest = [self fetchRequest]; - [fetchRequest setFetchLimit:1]; - - NSArray *array = [self.managedObjectContext executeFetchRequest:fetchRequest error:error]; - managedObject = [array firstObject]; - } - - return managedObject; -} - -- (NSManagedObject *)lastObject:(NSError **)error { - NSManagedObject *managedObject; - - if (_resultsCache) { - managedObject = [_resultsCache lastObject]; - } else { - NSFetchRequest *fetchRequest = [self fetchRequest]; - NSArray *array = [self.managedObjectContext executeFetchRequest:fetchRequest error:error]; - managedObject = [array lastObject]; - } - - return managedObject; -} - -@end - -@implementation NSManagedObject (QKQuerySet) - -+ (NSString *)entityName { - return NSStringFromClass([self class]); -} - -+ (QKQuerySet *)querySetWithManagedObjectContext:(NSManagedObjectContext *)context { - NSEntityDescription *entityDescription = [NSEntityDescription entityForName:[self entityName] inManagedObjectContext:context]; - return [[QKQuerySet alloc] initWithManagedObjectContext:context entityDescription:entityDescription]; -} - -@end diff --git a/QueryKit/ObjectiveC/QKQuerySet.swift b/QueryKit/ObjectiveC/QKQuerySet.swift deleted file mode 100644 index 2cb10d4..0000000 --- a/QueryKit/ObjectiveC/QKQuerySet.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Foundation - -extension QuerySet { - public func asQKQuerySet() -> QKQuerySet { - let entityDescription = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)! - - var nsrange:NSRange = NSMakeRange(NSNotFound, NSNotFound) - if let range = self.range { - nsrange = NSMakeRange(range.startIndex, range.endIndex - range.startIndex) - } - - return QKQuerySet(managedObjectContext: context, entityDescription: entityDescription, predicate: predicate, sortDescriptors: sortDescriptors, range:nsrange) - } -} - -extension QKQuerySet { - public func asQuerySet() -> QuerySet { - var queryset = QuerySet(managedObjectContext, entityDescription.name!) - - if let sortDescriptors = sortDescriptors as? [NSSortDescriptor] { - queryset = queryset.orderBy(sortDescriptors) - } - - if let predicate = predicate { - queryset = queryset.filter(predicate) - } - - if range.location != NSNotFound { - queryset = queryset[range.location..<(range.location + range.length)] - } - - return queryset - } -} diff --git a/QueryKit/Predicate.swift b/QueryKit/Predicate.swift deleted file mode 100644 index b3ad728..0000000 --- a/QueryKit/Predicate.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Predicate.swift -// QueryKit -// -// Created by Kyle Fuller on 19/06/2014. -// -// - -import Foundation - -public func && (left: NSPredicate, right: NSPredicate) -> NSPredicate { - return NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [left, right]) -} - -public func || (left: NSPredicate, right: NSPredicate) -> NSPredicate { - return NSCompoundPredicate(type: NSCompoundPredicateType.OrPredicateType, subpredicates: [left, right]) -} - -prefix public func ! (left: NSPredicate) -> NSPredicate { - return NSCompoundPredicate(type: NSCompoundPredicateType.NotPredicateType, subpredicates: [left]) -} diff --git a/QueryKit/QueryKit.h b/QueryKit/QueryKit.h deleted file mode 100644 index 6423d79..0000000 --- a/QueryKit/QueryKit.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// QueryKit.h -// QueryKit -// -// Created by Kyle Fuller on 19/06/2014. -// -// - -#import - -//! Project version number for QueryKit. -FOUNDATION_EXPORT double QueryKitVersionNumber; - -//! Project version string for QueryKit. -FOUNDATION_EXPORT const unsigned char QueryKitVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import -#import -#import diff --git a/QueryKit/QueryKit.swift b/QueryKit/QueryKit.swift deleted file mode 100644 index ba12018..0000000 --- a/QueryKit/QueryKit.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// QueryKit.swift -// QueryKit -// -// Created by Kyle Fuller on 19/06/2014. -// -// - diff --git a/QueryKit/QuerySet.swift b/QueryKit/QuerySet.swift deleted file mode 100644 index dab6626..0000000 --- a/QueryKit/QuerySet.swift +++ /dev/null @@ -1,236 +0,0 @@ -// -// QuerySet.swift -// QueryKit -// -// Created by Kyle Fuller on 06/07/2014. -// -// - -import Foundation -import CoreData - - -/// Represents a lazy database lookup for a set of objects. -public class QuerySet : SequenceType, Equatable { - /// Returns the managed object context that will be used to execute any requests. - public let context:NSManagedObjectContext - - /// Returns the name of the entity the request is configured to fetch. - public let entityName:String - - /// Returns the sort descriptors of the receiver. - public let sortDescriptors:[NSSortDescriptor] - - /// Returns the predicate of the receiver. - public let predicate:NSPredicate? - - public let range:Range? - - // MARK: Initialization - - public init(_ context:NSManagedObjectContext, _ entityName:String) { - self.context = context - self.entityName = entityName - self.sortDescriptors = [] - self.predicate = nil - self.range = nil - } - - public init(queryset:QuerySet, sortDescriptors:[NSSortDescriptor]?, predicate:NSPredicate?, range:Range?) { - self.context = queryset.context - self.entityName = queryset.entityName - self.sortDescriptors = sortDescriptors ?? [] - self.predicate = predicate - self.range = range - } - - // MARK: Sorting - - /// Returns a new QuerySet containing objects ordered by the given sort descriptor. - public func orderBy(sortDescriptor:NSSortDescriptor) -> QuerySet { - return orderBy([sortDescriptor]) - } - - /// Returns a new QuerySet containing objects ordered by the given sort descriptors. - public func orderBy(sortDescriptors:[NSSortDescriptor]) -> QuerySet { - return QuerySet(queryset:self, sortDescriptors:sortDescriptors, predicate:predicate, range:range) - } - - /// Reverses the ordering of the QuerySet - public func reverse() -> QuerySet { - return QuerySet(queryset:self, sortDescriptors:sortDescriptors.reverse(), predicate:predicate, range:range) - } - - // MARK: Filtering - - /// Returns a new QuerySet containing objects that match the given predicate. - public func filter(predicate:NSPredicate) -> QuerySet { - var futurePredicate = predicate - - if let existingPredicate = self.predicate { - futurePredicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [existingPredicate, predicate]) - } - - return QuerySet(queryset:self, sortDescriptors:sortDescriptors, predicate:futurePredicate, range:range) - } - - /// Returns a new QuerySet containing objects that match the given predicates. - public func filter(predicates:[NSPredicate]) -> QuerySet { - let newPredicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: predicates) - return filter(newPredicate) - } - - /// Returns a new QuerySet containing objects that exclude the given predicate. - public func exclude(predicate:NSPredicate) -> QuerySet { - let excludePredicate = NSCompoundPredicate(type: NSCompoundPredicateType.NotPredicateType, subpredicates: [predicate]) - return filter(excludePredicate) - } - - /// Returns a new QuerySet containing objects that exclude the given predicates. - public func exclude(predicates:[NSPredicate]) -> QuerySet { - let excludePredicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: predicates) - return exclude(excludePredicate) - } - - // MARK: Subscripting - - public subscript(index: Int) -> (object:T?, error:NSError?) { - get { - var request = fetchRequest - request.fetchOffset = index - request.fetchLimit = 1 - - var error:NSError? - if let items = context.executeFetchRequest(request, error:&error) { - return (object:items.first as? T, error:error) - } else { - return (object: nil, error: error) - } - } - } - - /// Returns the object at the specified index. - public subscript(index: Int) -> T? { - get { - return self[index].object - } - } - - public subscript(range:Range) -> QuerySet { - get { - var fullRange = range - - if let currentRange = self.range { - fullRange = Range(start: currentRange.startIndex + range.startIndex, end: range.endIndex) - } - - return QuerySet(queryset:self, sortDescriptors:sortDescriptors, predicate:predicate, range:fullRange) - } - } - - // Mark: Getters - public var first: T? { - get { - return self[0].object - } - } - - - // MARK: Conversion - - public var fetchRequest:NSFetchRequest { - var request = NSFetchRequest(entityName:entityName) - request.predicate = predicate - request.sortDescriptors = sortDescriptors - - if let range = range { - request.fetchOffset = range.startIndex - request.fetchLimit = range.endIndex - range.startIndex - } - - return request - } - - public func array() -> (objects:([T]?), error:NSError?) { - var error:NSError? - var objects = context.executeFetchRequest(fetchRequest, error:&error) as? [T] - return (objects:objects, error:error) - } - - public func array() -> [T]? { - return array().objects - } - - // MARK: Count - - public func count() -> (count:Int?, error:NSError?) { - var error:NSError? - var count:Int? = context.countForFetchRequest(fetchRequest, error: &error) - - if count! == NSNotFound { - count = nil - } - - return (count:count, error:error) - } - - /// Returns the count of objects matching the QuerySet. - public func count() -> Int? { - return count().count - } - - // MARK: Exists - - /** Returns true if the QuerySet contains any results, and false if not. - :note: Returns nil if the operation could not be completed. - */ - public func exists() -> Bool? { - let result:Int? = count() - - if let result = result { - return result > 0 - } - - return nil - } - - // MARK: Deletion - - /// Deletes all the objects matching the QuerySet. - public func delete() -> (count:Int, error:NSError?) { - var result = array() as (objects:([T]?), error:NSError?) - var deletedCount = 0 - - if let objects = result.objects { - for object in objects { - context.deleteObject(object) - } - - deletedCount = objects.count - } - - return (count:deletedCount, error:result.error) - } - - // MARK: Sequence - - public func generate() -> IndexingGenerator> { - var result = self.array() as (objects:([T]?), error:NSError?) - - if let objects = result.objects { - return objects.generate() - } - - return [].generate() - } -} - -public func == (lhs: QuerySet, rhs: QuerySet) -> Bool { - let context = lhs.context == rhs.context - let entityName = lhs.entityName == rhs.entityName - let sortDescriptors = lhs.sortDescriptors == rhs.sortDescriptors - let predicate = lhs.predicate == rhs.predicate - let startIndex = lhs.range?.startIndex == rhs.range?.startIndex - let endIndex = lhs.range?.endIndex == rhs.range?.endIndex - return context && entityName && sortDescriptors && predicate && startIndex && endIndex -} diff --git a/QueryKitTests/AttributeTests.swift b/QueryKitTests/AttributeTests.swift deleted file mode 100644 index 1d91fae..0000000 --- a/QueryKitTests/AttributeTests.swift +++ /dev/null @@ -1,135 +0,0 @@ -// -// AttributeTests.swift -// QueryKit -// -// Created by Kyle Fuller on 19/06/2014. -// -// - -import XCTest -import QueryKit - -class AttributeTests: XCTestCase { - var attribute:Attribute! - - override func setUp() { - super.setUp() - - attribute = Attribute("age") - } - - func testAttributeHasName() { - XCTAssertEqual(attribute.name, "age") - } - - func testAttributeExpression() { - XCTAssertEqual(attribute.expression.keyPath, "age") - } - - func testEqualAttributesAreEquatable() { - XCTAssertEqual(attribute, Attribute("age")) - } - - func testCompoundAttributeCreation() { - let personCompanyNameAttribute = Attribute(attributes:["company", "name"]) - - XCTAssertEqual(personCompanyNameAttribute.name, "company.name") - XCTAssertEqual(personCompanyNameAttribute.expression.keyPath, "company.name") - } - - // Sorting - - func testAscendingSortDescriptor() { - XCTAssertEqual(attribute.ascending(), NSSortDescriptor(key: "age", ascending: true)) - } - - func testDescendingSortDescriptor() { - XCTAssertEqual(attribute.descending(), NSSortDescriptor(key: "age", ascending: false)) - } - - // Operators - - func testEqualityOperator() { - var predicate:NSPredicate = (attribute == 10) - XCTAssertEqual(predicate, NSPredicate(format: "age == 10")) - } - - func testInequalityOperator() { - var predicate:NSPredicate = (attribute != 10) - XCTAssertEqual(predicate, NSPredicate(format: "age != 10")) - } - - func testGreaterThanOperator() { - var predicate:NSPredicate = (attribute > 10) - XCTAssertEqual(predicate, NSPredicate(format: "age > 10")) - } - - func testGreaterOrEqualThanOperator() { - var predicate:NSPredicate = (attribute >= 10) - XCTAssertEqual(predicate, NSPredicate(format: "age >= 10")) - } - - func testLessThanOperator() { - var predicate:NSPredicate = (attribute < 10) - XCTAssertEqual(predicate, NSPredicate(format: "age < 10")) - } - - func testLessOrEqualThanOperator() { - var predicate:NSPredicate = (attribute <= 10) - XCTAssertEqual(predicate, NSPredicate(format: "age <= 10")) - } - - func testLikeOperator() { - var predicate:NSPredicate = (attribute ~= 10) - XCTAssertEqual(predicate, NSPredicate(format: "age LIKE 10")) - } - - func testInOperator() { - var predicate:NSPredicate = (attribute << [5, 10]) - XCTAssertEqual(predicate, NSPredicate(format: "age IN %@", [5, 10])) - } - - func testBetweenRangeOperator() { - var predicate:NSPredicate = attribute << (5..<10) - XCTAssertEqual(predicate, NSPredicate(format: "age BETWEEN %@", [5, 10])) - } - - func testOptionalEqualityOperator() { - let attribute = Attribute("name") - var predicate:NSPredicate = (attribute == "kyle") - XCTAssertEqual(predicate, NSPredicate(format: "name == 'kyle'")) - } - - func testOptionalNSObjectEqualityOperator() { - let attribute = Attribute("name") - var predicate:NSPredicate = (attribute == "kyle") - XCTAssertEqual(predicate, NSPredicate(format: "name == 'kyle'")) - } -} - -class CollectionAttributeTests: XCTestCase { - func testCountOfSet() { - let setAttribute = Attribute("names") - let countAttribute = count(setAttribute) - XCTAssertEqual(countAttribute, Attribute("names.@count")) - } - - func testCountOfOrderedSet() { - let setAttribute = Attribute("names") - let countAttribute = count(setAttribute) - XCTAssertEqual(countAttribute, Attribute("names.@count")) - } -} - -class BoolAttributeTests: XCTestCase { - var attribute:Attribute! - - override func setUp() { - super.setUp() - attribute = Attribute("hasName") - } - - func testNotAttributeReturnsPredicate() { - XCTAssertEqual(!attribute, NSPredicate(format: "hasName == NO")) - } -} diff --git a/QueryKitTests/ExpressionTests.swift b/QueryKitTests/ExpressionTests.swift deleted file mode 100644 index 5a780ef..0000000 --- a/QueryKitTests/ExpressionTests.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ExpressionTests.swift -// QueryKit -// -// Created by Kyle Fuller on 06/07/2014. -// -// - -import XCTest -import QueryKit - -class ExpressionTests: XCTestCase { - var leftHandSide:NSExpression! - var rightHandSide:NSExpression! - - override func setUp() { - super.setUp() - - leftHandSide = NSExpression(forKeyPath: "age") - rightHandSide = NSExpression(forConstantValue: 10) - } - - func testEqualityOperator() { - XCTAssertEqual(leftHandSide == rightHandSide, NSPredicate(format: "age == 10")) - } - - func testInequalityOperator() { - XCTAssertEqual(leftHandSide != rightHandSide, NSPredicate(format: "age != 10")) - } - - func testGreaterThanOperator() { - let predicate:NSPredicate = leftHandSide > rightHandSide - XCTAssertEqual(predicate, NSPredicate(format: "age > 10")) - } - - func testGreaterOrEqualThanOperator() { - XCTAssertEqual(leftHandSide >= rightHandSide, NSPredicate(format: "age >= 10")) - } - - func testLessThanOperator() { - XCTAssertEqual(leftHandSide < rightHandSide, NSPredicate(format: "age < 10")) - } - - func testLessOrEqualThanOperator() { - XCTAssertEqual(leftHandSide <= rightHandSide, NSPredicate(format: "age <= 10")) - } - - func testLikeOperator() { - XCTAssertEqual(leftHandSide ~= rightHandSide, NSPredicate(format: "age LIKE 10")) - } - - func testInOperator() { - XCTAssertEqual(leftHandSide << rightHandSide, NSPredicate(format: "age IN 10")) - } -} diff --git a/QueryKitTests/Info.plist b/QueryKitTests/Info.plist deleted file mode 100644 index 205ebc1..0000000 --- a/QueryKitTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - org.cocode.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/QueryKitTests/ObjectiveC/QKAttributeTests.swift b/QueryKitTests/ObjectiveC/QKAttributeTests.swift deleted file mode 100644 index a417bf0..0000000 --- a/QueryKitTests/ObjectiveC/QKAttributeTests.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// QKAttributeTests.swift -// QueryKit -// -// Created by Kyle Fuller on 23/08/2014. -// -// - -import XCTest -import QueryKit - -class QKAttributeTests: XCTestCase { - var attribute:QKAttribute! - - override func setUp() { - super.setUp() - - attribute = QKAttribute(name: "age") - } - - func testAttributeHasName() { - XCTAssertEqual(attribute.name, "age") - } - - func testAttributeExpression() { - XCTAssertEqual(attribute.expression().keyPath, "age") - } - - func testEqualAttributesAreEquatable() { - XCTAssertEqual(attribute, QKAttribute(name: "age")) - } - - // MARK: Conversion - - func testConvertingAttributeToQKAttribute() { - let qkAttribute = Attribute("age").asQKAttribute() - XCTAssertEqual(qkAttribute, attribute) - } - - func testConvertingQKAttributeToAttribute() { - XCTAssertEqual(attribute.asAttribute(), Attribute("age")) - } - - // MARK: Ordering - - func testAscendingSortDescriptor() { - XCTAssertEqual(attribute.ascending(), NSSortDescriptor(key: "age", ascending: true)) - } - - func testDescendingSortDescriptor() { - XCTAssertEqual(attribute.descending(), NSSortDescriptor(key: "age", ascending: false)) - } - - // MARK: Operators - - func testEqualityOperator() { - let predicate = attribute.equal(10) - XCTAssertEqual(predicate, NSPredicate(format: "age == 10")) - } - - func testInequalityOperator() { - let predicate = attribute.notEqual(10) - XCTAssertEqual(predicate, NSPredicate(format: "age != 10")) - } - - func testGreaterThanOperator() { - let predicate = attribute.greaterThan(10) - XCTAssertEqual(predicate, NSPredicate(format: "age > 10")) - } - - func testGreaterOrEqualThanOperator() { - let predicate = attribute.greaterThanOrEqualTo(10) - XCTAssertEqual(predicate, NSPredicate(format: "age >= 10")) - } - - func testLessThanOperator() { - let predicate = attribute.lessThan(10) - XCTAssertEqual(predicate, NSPredicate(format: "age < 10")) - } - - func testLessOrEqualThanOperator() { - let predicate = attribute.lessThanOrEqualTo(10) - XCTAssertEqual(predicate, NSPredicate(format: "age <= 10")) - } - -} diff --git a/QueryKitTests/ObjectiveC/QKQuerySetTests.m b/QueryKitTests/ObjectiveC/QKQuerySetTests.m deleted file mode 100644 index b45eeac..0000000 --- a/QueryKitTests/ObjectiveC/QKQuerySetTests.m +++ /dev/null @@ -1,141 +0,0 @@ -// -// KFObjectManagerTests -// KFDataTests -// -// Created by Kyle Fuller on 14/06/2013. -// -// - -#import -#import - - -@interface QKQuerySetTests : XCTestCase - -@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; -@property (nonatomic, strong) NSEntityDescription *entityDescription; -@property (nonatomic, strong) QKQuerySet *queryset; - -@end - -@implementation QKQuerySetTests - -- (void)setUp { - self.managedObjectContext = [[NSManagedObjectContext alloc] init]; - self.entityDescription = [[NSEntityDescription alloc] init]; - - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Kyle'"]; - NSArray *sortDescriptors = @[ - [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES], - [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO] - ]; - - self.queryset = [[QKQuerySet alloc] initWithManagedObjectContext:self.managedObjectContext entityDescription:self.entityDescription predicate:predicate sortDescriptors:sortDescriptors range:NSMakeRange(1, 3)]; -} - -- (void)testInitializationWithContextAndEntityDescription { - QKQuerySet *queryset = [[QKQuerySet alloc] initWithManagedObjectContext:self.managedObjectContext entityDescription:self.entityDescription]; - - XCTAssertEqualObjects(queryset.managedObjectContext, self.managedObjectContext); - XCTAssertEqualObjects(queryset.entityDescription, self.entityDescription); - XCTAssertNil(queryset.predicate); - XCTAssertEqualObjects(queryset.sortDescriptors, @[]); -} - -- (void)testInitializationWithPredicateAndSortDescriptors { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Kyle'"]; - NSArray *sortDescriptors = @[ - [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES], - [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO] - ]; - - XCTAssertEqualObjects(self.queryset.managedObjectContext, self.managedObjectContext); - XCTAssertEqualObjects(self.queryset.entityDescription, self.entityDescription); - XCTAssertEqualObjects(self.queryset.predicate, predicate); - XCTAssertEqualObjects(self.queryset.sortDescriptors, sortDescriptors); -} - -- (void)testCreationFromFetchRequest { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Kyle'"]; - NSArray *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; - - NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; - [fetchRequest setEntity:self.entityDescription]; - [fetchRequest setPredicate:predicate]; - [fetchRequest setSortDescriptors:sortDescriptors]; - - QKQuerySet *queryset = [[QKQuerySet alloc] initWithManagedObjectContext:self.managedObjectContext fetchRequest:fetchRequest]; - - XCTAssertEqualObjects(queryset.managedObjectContext, self.managedObjectContext); - XCTAssertEqualObjects(queryset.entityDescription, self.entityDescription); - XCTAssertEqualObjects(queryset.predicate, predicate); - XCTAssertEqualObjects(queryset.sortDescriptors, sortDescriptors); -} - -- (void)testIsEqual { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Kyle'"]; - NSArray *sortDescriptors = @[ - [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES], - [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO] - ]; - - QKQuerySet *queryset = [[QKQuerySet alloc] initWithManagedObjectContext:self.managedObjectContext entityDescription:self.entityDescription predicate:predicate sortDescriptors:sortDescriptors range:NSMakeRange(1, 3)]; - - XCTAssertEqualObjects(self.queryset, queryset); - XCTAssertEqual([self.queryset hash], [queryset hash]); -} - -- (void)testCopying { - QKQuerySet *queryset = [self.queryset copy]; - XCTAssertEqualObjects(self.queryset, queryset); -} - -- (void)testFilterAddsPredicate { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age == 28"]; - QKQuerySet *filteredQuerySet = [self.queryset filter:predicate]; - - XCTAssertEqualObjects(filteredQuerySet.predicate, [NSPredicate predicateWithFormat:@"name == 'Kyle' AND age == 28"]); -} - -- (void)testExcludeAddsPredicate { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age == 28"]; - QKQuerySet *queryset = [self.queryset exclude:predicate]; - - XCTAssertEqualObjects(queryset.predicate, [NSPredicate predicateWithFormat:@"name == 'Kyle' AND (NOT age == 28)"]); -} - -- (void)testOrderBy { - NSArray *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; - QKQuerySet *queryset = [self.queryset orderBy:sortDescriptors]; - - XCTAssertEqualObjects(queryset.sortDescriptors, sortDescriptors); -} - -- (void)testReverse { - QKQuerySet *reversedQueryset = [self.queryset reverse]; - - NSArray *reversedSortDescriptors = @[ - [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:NO], - [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES] - ]; - - XCTAssertEqualObjects(reversedQueryset.sortDescriptors, reversedSortDescriptors); -} - -- (void)testFetchRequest { - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Kyle'"]; - NSArray *sortDescriptors = @[ - [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES], - [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO] - ]; - - NSFetchRequest *fetchRequest = [self.queryset fetchRequest]; - - XCTAssertEqualObjects(fetchRequest.entityName, self.entityDescription.name); - XCTAssertEqualObjects(fetchRequest.predicate, predicate); - XCTAssertEqualObjects(fetchRequest.sortDescriptors, sortDescriptors); - XCTAssertEqual(fetchRequest.fetchOffset, 1); - XCTAssertEqual(fetchRequest.fetchLimit, 3); -} - -@end diff --git a/QueryKitTests/ObjectiveC/QKQuerySetTests.swift b/QueryKitTests/ObjectiveC/QKQuerySetTests.swift deleted file mode 100644 index 7b99d91..0000000 --- a/QueryKitTests/ObjectiveC/QKQuerySetTests.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// QuerySetTests.swift -// QueryKit -// -// Created by Kyle Fuller on 06/07/2014. -// -// - -import XCTest -import QueryKit - -class QKQuerySetConversionTests: XCTestCase { - var qkQueryset:QKQuerySet! - var queryset:QuerySet! - - override func setUp() { - super.setUp() - - let context = NSManagedObjectContext() - context.persistentStoreCoordinator = persistentStoreCoordinator() - let entityDescription = NSEntityDescription.entityForName("Person", inManagedObjectContext:context)! - let predicate = NSPredicate(format: "name == 'Kyle'") - let sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] - - qkQueryset = QKQuerySet(managedObjectContext: context, entityDescription: entityDescription, predicate: predicate, sortDescriptors: sortDescriptors, range:NSMakeRange(1, 4)) - queryset = QuerySet(context, "Person") - queryset = queryset.filter(predicate).orderBy(sortDescriptors)[1..<5] - } - - func testConvertingQuerySetToQKQuerySet() { - XCTAssertEqual(queryset.asQKQuerySet(), qkQueryset) - } - - func testConvertingQKQuerySetToQuerySet() { - XCTAssertEqual(qkQueryset.asQuerySet(), queryset) - } -} diff --git a/QueryKitTests/ObjectiveC/QueryKitTests-Bridging-Header.h b/QueryKitTests/ObjectiveC/QueryKitTests-Bridging-Header.h deleted file mode 100644 index 1b2cb5d..0000000 --- a/QueryKitTests/ObjectiveC/QueryKitTests-Bridging-Header.h +++ /dev/null @@ -1,4 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - diff --git a/QueryKitTests/Person.h b/QueryKitTests/Person.h deleted file mode 100644 index 884af31..0000000 --- a/QueryKitTests/Person.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Person.h -// QueryKit -// -// Created by Kyle Fuller on 06/07/2014. -// -// - -#import - -@interface Person : NSManagedObject - -@property (nonatomic, retain) NSString *name; - -@end diff --git a/QueryKitTests/PredicateTests.swift b/QueryKitTests/PredicateTests.swift deleted file mode 100644 index 200c285..0000000 --- a/QueryKitTests/PredicateTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// PredicateTests.swift -// QueryKit -// -// Created by Kyle Fuller on 19/06/2014. -// -// - -import XCTest -import QueryKit - -class PredicateTests: XCTestCase { - var namePredicate = NSPredicate(format: "name == Kyle") - var agePredicate = NSPredicate(format: "age >= 21") - - func testAndPredicate() { - var predicate = namePredicate && agePredicate - XCTAssertEqual(predicate, NSPredicate(format: "name == Kyle AND age >= 21")) - } - - func testOrPredicate() { - var predicate = namePredicate || agePredicate - XCTAssertEqual(predicate, NSPredicate(format: "name == Kyle OR age >= 21")) - } - - func testNotPredicate() { - var predicate = !namePredicate - XCTAssertEqual(predicate, NSPredicate(format: "NOT name == Kyle")) - } -} diff --git a/QueryKitTests/QueryKitTests.swift b/QueryKitTests/QueryKitTests.swift deleted file mode 100644 index 680e04d..0000000 --- a/QueryKitTests/QueryKitTests.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// QueryKitTests.swift -// QueryKitTests -// -// Created by Kyle Fuller on 19/06/2014. -// -// - -import XCTest -import QueryKit -import CoreData - -@objc(Person) class Person : NSManagedObject { - @NSManaged var name:String - - class var entityName:String { - return "Person" - } -} - -extension Person { - class func create(context:NSManagedObjectContext) -> Person { - return NSEntityDescription.insertNewObjectForEntityForName(Person.entityName, inManagedObjectContext: context) as! Person - } -} - -func managedObjectModel() -> NSManagedObjectModel { - let personEntity = NSEntityDescription() - personEntity.name = Person.entityName - personEntity.managedObjectClassName = "Person" - - let personNameAttribute = NSAttributeDescription() - personNameAttribute.name = "name" - personNameAttribute.attributeType = NSAttributeType.StringAttributeType - personNameAttribute.optional = false - personEntity.properties = [personNameAttribute] - - let model = NSManagedObjectModel() - model.entities = [personEntity] - - return model -} - -func persistentStoreCoordinator() -> NSPersistentStoreCoordinator { - let model = managedObjectModel() - let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) - persistentStoreCoordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil, error: nil) - return persistentStoreCoordinator -} diff --git a/QueryKitTests/QuerySetTests.swift b/QueryKitTests/QuerySetTests.swift deleted file mode 100644 index 1f2062d..0000000 --- a/QueryKitTests/QuerySetTests.swift +++ /dev/null @@ -1,225 +0,0 @@ -// -// QuerySetTests.swift -// QueryKit -// -// Created by Kyle Fuller on 06/07/2014. -// -// - -import XCTest -import CoreData -import QueryKit - -class QuerySetTests: XCTestCase { - var context:NSManagedObjectContext! - var queryset:QuerySet! - - override func setUp() { - super.setUp() - - context = NSManagedObjectContext() - context.persistentStoreCoordinator = persistentStoreCoordinator() - - for name in ["Kyle", "Orta", "Ayaka", "Mark", "Scott"] { - let person = Person.create(context) - person.name = name - } - - context.save(nil) - - queryset = QuerySet(context, "Person") - } - - func testEqualQuerySetIsEquatable() { - XCTAssertEqual(queryset, QuerySet(context, "Person")) - } - - // MARK: Sorting - - func testOrderBySortDescriptor() { - let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) - var qs = queryset.orderBy(sortDescriptor) - XCTAssertTrue(qs.sortDescriptors == [sortDescriptor]) - } - - func testOrderBySortDescriptors() { - let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) - var qs = queryset.orderBy([sortDescriptor]) - XCTAssertTrue(qs.sortDescriptors == [sortDescriptor]) - } - - func testReverseOrdering() { - let nameSortDescriptor = NSSortDescriptor(key: "name", ascending: true) - let ageSortDescriptor = NSSortDescriptor(key: "age", ascending: true) - let qs = queryset.orderBy([nameSortDescriptor, ageSortDescriptor]).reverse() - - XCTAssertEqual(qs.sortDescriptors, [ageSortDescriptor, nameSortDescriptor]) - } - - // MARK: Filtering - - func testFilterPredicate() { - let predicate = NSPredicate(format: "name == Kyle") - var qs = queryset.filter(predicate) - XCTAssertEqual(qs.predicate!, predicate) - } - - func testFilterPredicates() { - let predicateName = NSPredicate(format: "name == Kyle") - let predicateAge = NSPredicate(format: "age > 27") - - var qs = queryset.filter([predicateName, predicateAge]) - XCTAssertEqual(qs.predicate!, NSPredicate(format: "name == Kyle AND age > 27")) - } - - func testFilterBooleanAttribute() { - let qs = queryset.filter(Attribute("isEmployed")) - XCTAssertEqual(qs.predicate!, NSPredicate(format: "isEmployed == YES")) - } - - // MARK: Exclusion - - func testExcludePredicate() { - let predicate = NSPredicate(format: "name == Kyle") - var qs = queryset.exclude(predicate) - XCTAssertEqual(qs.predicate!, NSPredicate(format: "NOT name == Kyle")) - } - - func testExcludePredicates() { - let predicateName = NSPredicate(format: "name == Kyle") - let predicateAge = NSPredicate(format: "age > 27") - - var qs = queryset.exclude([predicateName, predicateAge]) - XCTAssertEqual(qs.predicate!, NSPredicate(format: "NOT (name == Kyle AND age > 27)")) - } - - func testExcludeBooleanAttribute() { - let qs = queryset.exclude(Attribute("isEmployed")) - XCTAssertEqual(qs.predicate!, NSPredicate(format: "isEmployed == NO")) - } - - // Fetch Request - - func testConversionToFetchRequest() { - let predicate = NSPredicate(format: "name == Kyle") - let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) - let qs = queryset.filter(predicate).orderBy(sortDescriptor)[2..<4] - - let fetchRequest = qs.fetchRequest - - XCTAssertEqual(fetchRequest.entityName!, "Person") - XCTAssertEqual(fetchRequest.predicate!, predicate) -// XCTAssertEqual(fetchRequest.sortDescriptors!, [sortDescriptor]) - XCTAssertEqual(fetchRequest.fetchOffset, 2) - XCTAssertEqual(fetchRequest.fetchLimit, 2) - } - - // MARK: Subscripting - - func testSubscriptingAtIndex() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) - - var ayaka = qs[0].object - var kyle = qs[1].object - var mark = qs[2].object - var orta:Person? = qs[3].object - var scott:Person? = qs[4] - - XCTAssertEqual(ayaka!.name, "Ayaka") - XCTAssertEqual(kyle!.name, "Kyle") - XCTAssertEqual(mark!.name, "Mark") - XCTAssertEqual(orta!.name, "Orta") - XCTAssertEqual(scott!.name, "Scott") - } - - func testSubscriptingRange() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...2] - - XCTAssertEqual(qs.range!.startIndex, 0) - XCTAssertEqual(qs.range!.endIndex, 3) - } - - func testSubscriptingRangeSubscriptsCurrentRange() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) - qs = qs[2...5] - qs = qs[2...4] - - XCTAssertEqual(qs.range!.startIndex, 4) - XCTAssertEqual(qs.range!.endIndex, 5) - } - - // MARK: Getters - func testFirst() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) - XCTAssertEqual(qs.first!.name, "Ayaka") - } - - // MARK: Conversion - - func testConversionToArray() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...1] - var people = qs.array().objects - - XCTAssertEqual(people!.count, 2) - } - - func testConversionToArrayWithoutError() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...1] - var people = qs.array() as [Person]? - - XCTAssertEqual(people!.count, 2) - } - - // MARK: Count - - func testCount() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...1] - var count = qs.count().count - - XCTAssertEqual(count!, 2) - } - - func testCountWithoutError() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...1] - var count = qs.count() as Int? - - XCTAssertEqual(count!, 2) - } - - // MARK: Exists - - func testExistsReturnsTrueWithMatchingObjects() { - let qs = queryset.filter(NSPredicate(format: "name == %@", "Kyle")) - XCTAssertTrue(qs.exists()!) - } - - func testExistsReturnsFalseWithNoMatchingObjects() { - let qs = queryset.filter(NSPredicate(format: "name == %@", "None")) - XCTAssertFalse(qs.exists()!) - } - - // MARK: Deletion - - func testDelete() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) - - var deletedCount = qs[0...1].delete().count - var count = qs.count() as Int? - - XCTAssertEqual(deletedCount, 2) - XCTAssertEqual(count!, 3) - } - - // MARK: Sequence - - func testSequence() { - var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) - var objects = [Person]() - - for object in qs { - objects.append(object) - } - - XCTAssertEqual(objects.count, 5) - } -} diff --git a/README.md b/README.md index 99fcd35..2cc9993 100644 --- a/README.md +++ b/README.md @@ -1,121 +1,163 @@ -QueryKit -======== +QueryKit Logo -[![Build Status](http://img.shields.io/travis/QueryKit/QueryKit/master.svg?style=flat)](https://travis-ci.org/QueryKit/QueryKit) +# QueryKit -QueryKit, a simple CoreData query language. +QueryKit, a simple type-safe Core Data query language. ## Usage ```swift -Person.queryset(context).filter(Person.name == "Kyle").delete() +QuerySet(context, "Person") + .orderedBy(.name, ascending: true) + .filter(\.age >= 18) ``` -### Querying +### QuerySet -To retrieve objects from CoreData with QueryKit, you can construct a QuerySet -for your model in a managed object context. +A QuerySet represents a collection of objects from your Core Data Store. +It may have zero, one or many filters. Filters narrow down the query +results based on the given parameters. -A queryset is an immutable object, any operation will return a new queryset -instead of modifying the queryset. +#### Retrieving all objects ```swift -var queryset = Person.queryset(context) +let queryset = QuerySet(context, "Person") ``` -#### Filtering +#### Retrieving specific objects with filters -You can filter a queryset using the `filter` and `exclude` methods, which -accept a predicate and return a new queryset. +You may filter a QuerySet using the `filter` and `exclude` methods, which +accept a predicate which can be constructed using KeyPath extensions. + +The `filter` and `exclude` methods return new QuerySet's including your filter. + +```swift +queryset.filter(\.name == "Kyle") +queryset.exclude(\.age > 25) +``` + +You may also use standard `NSPredicate` if you want to construct complicated +queries or do not wish to use the type-safe properties. + +```swift +queryset.filter(NSPredicate(format: "name == '%@'", "Kyle")) +queryset.exclude(NSPredicate(format: "age > 25")) +``` + +##### Chaining filters + +The result of refining a QuerySet is itself a QuerySet, so it’s possible +to chain refinements together. For example: ```swift -queryset.filter(NSPredicate(format:"name == %@", "Kyle")) -queryset.filter(Person.name == "Kyle") -queryset.exclude(Person.age < 21) -queryset.exclude(Person.isEmployed) +queryset.filter(\.name == "Kyle") + .exclude(\.age < 25) ``` +Each time you refine a QuerySet, you get a new QuerySet instance that is in no +way bound to the previous QuerySet. Each refinement creates a separate and +distinct QuerySet that may be stored, used and reused. + +#### QuerySets are lazy + +A QuerySet is lazy, creating a QuerySet doesn’t involve querying +Core Data. QueryKit won’t actually execute the query until the +QuerySet is *evaluated*. + #### Ordering -You can order a queryset's results by using the `orderBy` method which accepts -a collection of sort descriptors: +You may order a QuerySet's results by using the `orderBy` function which +accepts a KeyPath. + +```swift +queryset.orderBy(\.name, ascending: true) +``` + +You may also pass in an `NSSortDescriptor` if you would rather. ```swift queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) -queryset.orderBy(Person.name.ascending) -queryset.orderBy([Person.name.ascending, Person.age.descending]) ``` #### Slicing -You can use slicing to limit a queryset to a range. For example, to get the -first 5 items: +Using slicing, a QuerySet's results may be limited to a specified range. For +example, to get the first 5 items in our QuerySet: ```swift -queryset[0..5] +queryset[0...5] ``` -### Fetching +**NOTE**: *Remember, QuerySets are lazily evaluated. Slicing doesn’t evaluate the query.* + +#### Fetching -#### Single object +##### Multiple objects + +You may convert a QuerySet to an array using the `array()` function. For example: ```swift -var kyle = queryset.filter(Person.name == "Kyle").get() +for person in try! queryset.array() { + println("Hello \(person.name).") +} ``` -#### Object at index +##### First object ```swift -var orta = queryset[3] +let kyle = try? queryset.first() ``` -#### Count +##### Last object ```swift -queryset.count() +let kyle = try? queryset.last() ``` -#### Iteration +##### Object at index ```swift -for person in queryset { - println(person.name) -} +let katie = try? queryset.object(3) ``` -#### Conversion to an array +##### Count ```swift -queryset.array() +let numberOfPeople = try? queryset.count() ``` -### Deleting +##### Deleting This method immediately deletes the objects in your queryset and returns a -count and an error if the operation failed. +count or an error if the operation failed. ```swift -queryset.delete() +let deleted = try? queryset.delete() ``` -### Attributes +##### Operators -The `Attribute` is a generic structure for creating predicates providing -type-safety. +QueryKit provides KeyPath extensions providing operator functions allowing you +to create predicates. ```swift -let name = Attribute("name") -let age = Attribute("age") +// Name is equal to Kyle +\Person.name == "Kyle" -name == "Kyle" -name << ["Kyle", "Katie"] +// Name is either equal to Kyle or Katie +\.Person.name << ["Kyle", "Katie"] -age == 27 -age >= 25 -age << (22...30) +// Age is equal to 27 +\.Person.age == 27 + +// Age is more than or equal to 25 +\Person.age >= 25 + +// Age is within the range 22 to 30. +\Person.age << (22...30) ``` -#### Operators +The following types of comparisons are supported using Attribute: | Comparison | Meaning | | ------- |:--------:| @@ -130,19 +172,28 @@ age << (22...30) | << | x IN y, where y is an array | | << | x BETWEEN y, where y is a range | -## Predicate extensions +##### Predicate extensions -We've extended NSPredicate to add support for the `!`, `&&` and `||` operators -for joining predicates together. +QueryKit provides the `!`, `&&` and `||` operators for joining multiple predicates together. ```swift -NSPredicate(format:"name == Kyle") || NSPredicate(format:"name == Katie") -NSPredicate(format:"age >= 21") && !NSPredicate(format:"name == Kyle") +// Persons name is Kyle or Katie +\Person.name == "Kyle" || \Person.name == "Katie" + +// Persons age is more than 25 and their name is Kyle +\Person.age >= 25 && \Person.name == "Kyle" + +// Persons name is not Kyle +!(\Person.name == "Kyle") ``` -```swift -Person.name == "Kyle" || Person.name == "Katie" -Person.age >= 21 || Person.name != "Kyle" +## Installation + +[CocoaPods](http://cocoapods.org) is the recommended way to add QueryKit to +your project, you may also use Carthage. + +```ruby +pod 'QueryKit' ``` ## License diff --git a/Sources/QueryKit/Attribute.swift b/Sources/QueryKit/Attribute.swift new file mode 100644 index 0000000..a917dec --- /dev/null +++ b/Sources/QueryKit/Attribute.swift @@ -0,0 +1,128 @@ +import Foundation + +/// An attribute, representing an attribute on a model +public struct Attribute : Equatable { + public let key:String + + public init(_ key:String) { + self.key = key + } + + /// Builds a compound attribute with other key paths + public init(attributes:[String]) { + self.init(attributes.joined(separator: ".")) + } + + /// Returns an expression for the attribute + public var expression:NSExpression { + return NSExpression(forKeyPath: key) + } + + // MARK: Sorting + + /// Returns an ascending sort descriptor for the attribute + public func ascending() -> NSSortDescriptor { + return NSSortDescriptor(key: key, ascending: true) + } + + /// Returns a descending sort descriptor for the attribute + public func descending() -> NSSortDescriptor { + return NSSortDescriptor(key: key, ascending: false) + } + + func expressionForValue(_ value:AttributeType?) -> NSExpression { + if let value = value { + if let value = value as? NSObject { + return NSExpression(forConstantValue: value as NSObject) + } + + if MemoryLayout.size == MemoryLayout.size { + let value = unsafeBitCast(value, to: Optional.self) + if let value = value { + return NSExpression(forConstantValue: value) + } + } + + return NSExpression(forConstantValue: value) + } + + return NSExpression(forConstantValue: NSNull()) + } + + /// Builds a compound attribute by the current attribute with the given attribute + public func attribute(_ attribute:Attribute) -> Attribute { + return Attribute(attributes: [key, attribute.key]) + } +} + + +/// Returns true if two attributes have the same name +public func == (lhs: Attribute, rhs: Attribute) -> Bool { + return lhs.key == rhs.key +} + +public func == (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression == left.expressionForValue(right) +} + +public func != (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression != left.expressionForValue(right) +} + +public func > (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression > left.expressionForValue(right) +} + +public func >= (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression >= left.expressionForValue(right) +} + +public func < (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression < left.expressionForValue(right) +} + +public func <= (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression <= left.expressionForValue(right) +} + +public func ~= (left: Attribute, right: AttributeType?) -> NSPredicate { + return left.expression ~= left.expressionForValue(right) +} + +public func << (left: Attribute, right: [AttributeType]) -> NSPredicate { + let value = right.map { value in return value as! NSObject } + return left.expression << NSExpression(forConstantValue: value) +} + +public func << (left: Attribute, right: Range) -> NSPredicate { + let value = [right.lowerBound as! NSObject, right.upperBound as! NSObject] as NSArray + let rightExpression = NSExpression(forConstantValue: value) + + return NSComparisonPredicate(leftExpression: left.expression, rightExpression: rightExpression, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.between, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +/// MARK: Bool Attributes + +prefix public func ! (left: Attribute) -> NSPredicate { + return left == false +} + +public extension QuerySet { + func filter(_ attribute:Attribute) -> QuerySet { + return filter((attribute == true) as NSPredicate) + } + + func exclude(_ attribute:Attribute) -> QuerySet { + return filter((attribute == false) as NSPredicate) + } +} + +// MARK: Collections + +public func count(_ attribute:Attribute) -> Attribute { + return Attribute(attributes: [attribute.key, "@count"]) +} + +public func count(_ attribute:Attribute) -> Attribute { + return Attribute(attributes: [attribute.key, "@count"]) +} diff --git a/Sources/QueryKit/Expression.swift b/Sources/QueryKit/Expression.swift new file mode 100644 index 0000000..546756a --- /dev/null +++ b/Sources/QueryKit/Expression.swift @@ -0,0 +1,35 @@ +import Foundation + +/// Returns an equality predicate for the two given expressions +public func == (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.equalTo, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +/// Returns an inequality predicate for the two given expressions +public func != (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.notEqualTo, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +public func > (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.greaterThan, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +public func >= (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.greaterThanOrEqualTo, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +public func < (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.lessThan, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +public func <= (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.lessThanOrEqualTo, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +public func ~= (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.like, options: NSComparisonPredicate.Options(rawValue: 0)) +} + +public func << (left: NSExpression, right: NSExpression) -> NSPredicate { + return NSComparisonPredicate(leftExpression: left, rightExpression: right, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.in, options: NSComparisonPredicate.Options(rawValue: 0)) +} diff --git a/Sources/QueryKit/KeyPath.swift b/Sources/QueryKit/KeyPath.swift new file mode 100644 index 0000000..8991eaa --- /dev/null +++ b/Sources/QueryKit/KeyPath.swift @@ -0,0 +1,81 @@ +import CoreData + +func expression(for keyPath: KeyPath) -> NSExpression { + return NSExpression(forKeyPath: (keyPath as AnyKeyPath)._kvcKeyPathString!) +} + +// MARK: Predicate + +public func == (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs == rhs) +} + +public func != (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs != rhs) +} + +public func > (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs > rhs) +} + +public func >= (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs >= rhs) +} + +public func < (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs < rhs) +} + +public func <= (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs <= rhs) +} + +public func ~= (lhs: KeyPath, rhs: V) -> Predicate { + return Predicate(predicate: lhs ~= rhs) +} + +public func << (lhs: KeyPath, rhs: [V]) -> Predicate { + return Predicate(predicate: lhs << rhs) +} + +public func << (lhs: KeyPath, rhs: Range) -> Predicate { + return Predicate(predicate: lhs << rhs) +} + +// MARK: - NSPredicate + +public func == (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) == NSExpression(forConstantValue: rhs) +} + +public func != (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) != NSExpression(forConstantValue: rhs) +} + +public func > (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) > NSExpression(forConstantValue: rhs) +} + +public func >= (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) >= NSExpression(forConstantValue: rhs) +} + +public func < (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) < NSExpression(forConstantValue: rhs) +} + +public func <= (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) <= NSExpression(forConstantValue: rhs) +} + +public func ~= (lhs: KeyPath, rhs: V) -> NSPredicate { + return expression(for: lhs) ~= NSExpression(forConstantValue: rhs) +} + +public func << (lhs: KeyPath, rhs: [V]) -> NSPredicate { + return expression(for: lhs) << NSExpression(forConstantValue: rhs) +} + +public func << (lhs: KeyPath, rhs: Range) -> NSPredicate { + return expression(for: lhs) << NSExpression(forConstantValue: rhs) +} diff --git a/Sources/QueryKit/Predicate.swift b/Sources/QueryKit/Predicate.swift new file mode 100644 index 0000000..a13b9cf --- /dev/null +++ b/Sources/QueryKit/Predicate.swift @@ -0,0 +1,79 @@ +import Foundation +import CoreData + +/// Returns an and predicate from the given predicates +public func && (left: NSPredicate, right: NSPredicate) -> NSPredicate { + return NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: [left, right]) +} + +/// Returns an or predicate from the given predicates +public func || (left: NSPredicate, right: NSPredicate) -> NSPredicate { + return NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [left, right]) +} + +/// Returns a predicate reversing the given predicate +prefix public func ! (left: NSPredicate) -> NSPredicate { + return NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.not, subpredicates: [left]) +} + +// MARK: Predicate Type + +/// Represents a predicate for a specific model +public struct Predicate { + let predicate:NSPredicate + + init(predicate:NSPredicate) { + self.predicate = predicate + } +} + +public func == (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left == right) +} + +public func != (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left != right) +} + +public func > (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left > right) +} + +public func >= (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left >= right) +} + +public func < (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left < right) +} + +public func <= (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left <= right) +} + +public func ~= (left: Attribute, right: AttributeType?) -> Predicate { + return Predicate(predicate: left ~= right) +} + +public func << (left: Attribute, right: [AttributeType]) -> Predicate { + return Predicate(predicate: left << right) +} + +public func << (left: Attribute, right: Range) -> Predicate { + return Predicate(predicate: left << right) +} + +/// Returns an and predicate from the given predicates +public func && (left: Predicate, right: Predicate) -> Predicate { + return Predicate(predicate: left.predicate && right.predicate) +} + +/// Returns an or predicate from the given predicates +public func || (left: Predicate, right: Predicate) -> Predicate { + return Predicate(predicate: left.predicate || right.predicate) +} + +/// Returns a predicate reversing the given predicate +prefix public func ! (predicate: Predicate) -> Predicate { + return Predicate(predicate: !predicate.predicate) +} diff --git a/Sources/QueryKit/QueryKit.h b/Sources/QueryKit/QueryKit.h new file mode 100644 index 0000000..80cf39f --- /dev/null +++ b/Sources/QueryKit/QueryKit.h @@ -0,0 +1,7 @@ +#import + +//! Project version number for QueryKit. +FOUNDATION_EXPORT double QueryKitVersionNumber; + +//! Project version string for QueryKit. +FOUNDATION_EXPORT const unsigned char QueryKitVersionString[]; \ No newline at end of file diff --git a/Sources/QueryKit/QuerySet.swift b/Sources/QueryKit/QuerySet.swift new file mode 100644 index 0000000..c63d7a8 --- /dev/null +++ b/Sources/QueryKit/QuerySet.swift @@ -0,0 +1,257 @@ +import Foundation +import CoreData + +/// Represents a lazy database lookup for a set of objects. +open class QuerySet : Equatable { + /// Returns the managed object context that will be used to execute any requests. + public let context: NSManagedObjectContext + + /// Returns the name of the entity the request is configured to fetch. + public let entityName: String + + /// Returns the sort descriptors of the receiver. + public let sortDescriptors: [NSSortDescriptor] + + /// Returns the predicate of the receiver. + public let predicate: NSPredicate? + + /// The range of the query, allows you to offset and limit a query + public let range: Range? + + // MARK: Initialization + + public init(_ context:NSManagedObjectContext, _ entityName:String) { + self.context = context + self.entityName = entityName + self.sortDescriptors = [] + self.predicate = nil + self.range = nil + } + + /// Create a queryset from another queryset with a different sortdescriptor predicate and range + public init(queryset:QuerySet, sortDescriptors:[NSSortDescriptor]?, predicate:NSPredicate?, range: Range?) { + self.context = queryset.context + self.entityName = queryset.entityName + self.sortDescriptors = sortDescriptors ?? [] + self.predicate = predicate + self.range = range + } +} + +/// Methods which return a new queryset +extension QuerySet { + // MARK: Sorting + + /// Returns a new QuerySet containing objects ordered by the given sort descriptor. + public func orderBy(_ sortDescriptor:NSSortDescriptor) -> QuerySet { + return orderBy([sortDescriptor]) + } + + /// Returns a new QuerySet containing objects ordered by the given sort descriptors. + public func orderBy(_ sortDescriptors:[NSSortDescriptor]) -> QuerySet { + return QuerySet(queryset:self, sortDescriptors:sortDescriptors, predicate:predicate, range:range) + } + + /// Reverses the ordering of the QuerySet + public func reverse() -> QuerySet { + func reverseSortDescriptor(_ sortDescriptor:NSSortDescriptor) -> NSSortDescriptor { + return NSSortDescriptor(key: sortDescriptor.key!, ascending: !sortDescriptor.ascending) + } + + return QuerySet(queryset:self, sortDescriptors:sortDescriptors.map(reverseSortDescriptor), predicate:predicate, range:range) + } + + // MARK: Type-safe Sorting + + /// Returns a new QuerySet containing objects ordered by the given key path. + public func orderBy(_ keyPath: KeyPath, ascending: Bool) -> QuerySet { + return orderBy(NSSortDescriptor(key: (keyPath as AnyKeyPath)._kvcKeyPathString!, ascending: ascending)) + } + + /// Returns a new QuerySet containing objects ordered by the given sort descriptor. + public func orderBy(_ closure:((ModelType.Type) -> (SortDescriptor))) -> QuerySet { + return orderBy(closure(ModelType.self).sortDescriptor) + } + + /// Returns a new QuerySet containing objects ordered by the given sort descriptors. + public func orderBy(_ closure:((ModelType.Type) -> ([SortDescriptor]))) -> QuerySet { + return orderBy(closure(ModelType.self).map { $0.sortDescriptor }) + } + + // MARK: Filtering + + /// Returns a new QuerySet containing objects that match the given predicate. + public func filter(_ predicate: Predicate) -> QuerySet { + return filter(predicate.predicate) + } + + /// Returns a new QuerySet containing objects that match the given predicate. + public func filter(_ predicate:NSPredicate) -> QuerySet { + var futurePredicate = predicate + + if let existingPredicate = self.predicate { + futurePredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: [existingPredicate, predicate]) + } + + return QuerySet(queryset:self, sortDescriptors:sortDescriptors, predicate:futurePredicate, range:range) + } + + /// Returns a new QuerySet containing objects that match the given predicates. + public func filter(_ predicates:[NSPredicate]) -> QuerySet { + let newPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: predicates) + return filter(newPredicate) + } + + /// Returns a new QuerySet containing objects that exclude the given predicate. + public func exclude(_ predicate: Predicate) -> QuerySet { + return exclude(predicate.predicate) + } + + /// Returns a new QuerySet containing objects that exclude the given predicate. + public func exclude(_ predicate:NSPredicate) -> QuerySet { + let excludePredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.not, subpredicates: [predicate]) + return filter(excludePredicate) + } + + /// Returns a new QuerySet containing objects that exclude the given predicates. + public func exclude(_ predicates:[NSPredicate]) -> QuerySet { + let excludePredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.and, subpredicates: predicates) + return exclude(excludePredicate) + } + + // MARK: Type-safe filtering + + /// Returns a new QuerySet containing objects that match the given predicate. + @available(*, deprecated, renamed: "filter(_:)", message: "Replaced by KeyPath filtering https://git.io/Jv2v3") + public func filter(_ closure:((ModelType.Type) -> (Predicate))) -> QuerySet { + return filter(closure(ModelType.self).predicate) + } + + /// Returns a new QuerySet containing objects that exclude the given predicate. + @available(*, deprecated, renamed: "exclude(_:)", message: "Replaced by KeyPath filtering https://git.io/Jv2v3") + public func exclude(_ closure:((ModelType.Type) -> (Predicate))) -> QuerySet { + return exclude(closure(ModelType.self).predicate) + } + + /// Returns a new QuerySet containing objects that match the given predicatess. + @available(*, deprecated, renamed: "filter(_:)", message: "Replaced by KeyPath filtering https://git.io/Jv2v3") + public func filter(_ closures:[((ModelType.Type) -> (Predicate))]) -> QuerySet { + return filter(closures.map { $0(ModelType.self).predicate }) + } + + /// Returns a new QuerySet containing objects that exclude the given predicates. + @available(*, deprecated, renamed: "exclude(_:)", message: "Replaced by KeyPath filtering https://git.io/Jv2v3") + public func exclude(_ closures:[((ModelType.Type) -> (Predicate))]) -> QuerySet { + return exclude(closures.map { $0(ModelType.self).predicate }) + } +} + +/// Functions for evauluating a QuerySet +extension QuerySet { + // MARK: Subscripting + + /// Returns the object at the specified index. + public func object(_ index: Int) throws -> ModelType? { + let request = fetchRequest + request.fetchOffset = index + request.fetchLimit = 1 + let items = try context.fetch(request) + return items.first + } + + public subscript(range: ClosedRange) -> QuerySet { + get { + return self[Range(range)] + } + } + + public subscript(range: Range) -> QuerySet { + get { + var fullRange = range + + if let currentRange = self.range { + fullRange = ((currentRange.lowerBound + range.lowerBound) ..< range.upperBound) + } + + return QuerySet(queryset:self, sortDescriptors:sortDescriptors, predicate:predicate, range:fullRange) + } + } + + // Mark: Getters + + /// Returns the first object in the QuerySet + public func first() throws -> ModelType? { + return try self.object(0) + } + + /// Returns the last object in the QuerySet + public func last() throws -> ModelType? { + return try reverse().first() + } + + // MARK: Conversion + + /// Returns a fetch request equivilent to the QuerySet + public var fetchRequest: NSFetchRequest { + let request = NSFetchRequest(entityName: entityName) + request.predicate = predicate + request.sortDescriptors = sortDescriptors + + if let range = range { + request.fetchOffset = range.lowerBound + request.fetchLimit = range.upperBound - range.lowerBound + } + + return request + } + + /// Returns an array of all objects matching the QuerySet + public func array() throws -> [ModelType] { + return try context.fetch(fetchRequest) + } + + // MARK: Count + + /// Returns the count of objects matching the QuerySet. + public func count() throws -> Int { + return try context.count(for: fetchRequest) + } + + // MARK: Exists + + /** Returns true if the QuerySet contains any results, and false if not. + :note: Returns nil if the operation could not be completed. + */ + public func exists() throws -> Bool { + let fetchRequest = self.fetchRequest + fetchRequest.fetchLimit = 1 + + let result = try context.count(for: fetchRequest) + return result != 0 + } + + // MARK: Deletion + + /// Deletes all the objects matching the QuerySet. + public func delete() throws -> Int { + let objects = try array() + let deletedCount = objects.count + + for object in objects { + context.delete(object) + } + + return deletedCount + } +} + +/// Returns true if the two given querysets are equivilent +public func == (lhs: QuerySet, rhs: QuerySet) -> Bool { + let context = lhs.context == rhs.context + let entityName = lhs.entityName == rhs.entityName + let sortDescriptors = lhs.sortDescriptors == rhs.sortDescriptors + let predicate = lhs.predicate == rhs.predicate + let startIndex = lhs.range?.lowerBound == rhs.range?.lowerBound + let endIndex = lhs.range?.upperBound == rhs.range?.upperBound + return context && entityName && sortDescriptors && predicate && startIndex && endIndex +} diff --git a/Sources/QueryKit/SortDescriptor.swift b/Sources/QueryKit/SortDescriptor.swift new file mode 100644 index 0000000..96bfc25 --- /dev/null +++ b/Sources/QueryKit/SortDescriptor.swift @@ -0,0 +1,23 @@ +import Foundation +import CoreData + +/// Represents a sort descriptor for a specific model +public struct SortDescriptor { + let sortDescriptor:NSSortDescriptor + + init(sortDescriptor:NSSortDescriptor) { + self.sortDescriptor = sortDescriptor + } +} + +extension Attribute { + /// Returns an ascending sort descriptor for the attribute + public func ascending() -> SortDescriptor { + return SortDescriptor(sortDescriptor: ascending()) + } + + /// Returns a descending sort descriptor for the attribute + public func descending() -> SortDescriptor { + return SortDescriptor(sortDescriptor: descending()) + } +} diff --git a/Tests/QueryKitTests/AttributeTests.swift b/Tests/QueryKitTests/AttributeTests.swift new file mode 100644 index 0000000..e411fac --- /dev/null +++ b/Tests/QueryKitTests/AttributeTests.swift @@ -0,0 +1,140 @@ +// +// AttributeTests.swift +// QueryKit +// +// Created by Kyle Fuller on 19/06/2014. +// +// + +import XCTest +import QueryKit + +class AttributeTests: XCTestCase { + var attribute:Attribute! + + override func setUp() { + super.setUp() + attribute = Attribute("age") + } + + func testAttributeHasKey() { + XCTAssertEqual(attribute.key, "age") + } + + func testAttributeExpression() { + XCTAssertEqual(attribute.expression.keyPath, "age") + } + + func testEqualAttributesAreEquatable() { + XCTAssertEqual(attribute, Attribute("age")) + } + + func testCompoundAttributeCreation() { + let personCompanyNameAttribute = Attribute(attributes:["company", "name"]) + + XCTAssertEqual(personCompanyNameAttribute.key, "company.name") + XCTAssertEqual(personCompanyNameAttribute.expression.keyPath, "company.name") + } + + // Sorting + + func testAscendingSortDescriptor() { + XCTAssertEqual(attribute.ascending(), NSSortDescriptor(key: "age", ascending: true)) + } + + func testDescendingSortDescriptor() { + XCTAssertEqual(attribute.descending(), NSSortDescriptor(key: "age", ascending: false)) + } + + // Operators + + func testEqualityOperator() { + let predicate:NSPredicate = (attribute == 10) + XCTAssertEqual(predicate, NSPredicate(format:"age == 10")) + } + + func testInequalityOperator() { + let predicate:NSPredicate = (attribute != 10) + XCTAssertEqual(predicate, NSPredicate(format:"age != 10")) + } + + func testGreaterThanOperator() { + let predicate:NSPredicate = (attribute > 10) + XCTAssertEqual(predicate, NSPredicate(format:"age > 10")) + } + + func testGreaterOrEqualThanOperator() { + let predicate:NSPredicate = (attribute >= 10) + XCTAssertEqual(predicate, NSPredicate(format:"age >= 10")) + } + + func testLessThanOperator() { + let predicate:NSPredicate = (attribute < 10) + XCTAssertEqual(predicate, NSPredicate(format:"age < 10")) + } + + func testLessOrEqualThanOperator() { + let predicate:NSPredicate = (attribute <= 10) + XCTAssertEqual(predicate, NSPredicate(format:"age <= 10")) + } + + func testLikeOperator() { + let predicate:NSPredicate = (attribute ~= 10) + XCTAssertEqual(predicate, NSPredicate(format:"age LIKE 10")) + } + + func testInOperator() { + let predicate:NSPredicate = (attribute << [5, 10]) + XCTAssertEqual(predicate, NSPredicate(format:"age IN %@", [5, 10])) + } + + func testBetweenRangeOperator() { + let predicate:NSPredicate = attribute << (5..<10) + XCTAssertEqual(predicate, NSPredicate(format:"age BETWEEN %@", [5, 10])) + } + + func testOptionalEqualityOperator() { + let attribute = Attribute("name") + let predicate:NSPredicate = (attribute == "kyle") + XCTAssertEqual(predicate, NSPredicate(format:"name == 'kyle'")) + } + + func testOptionalNSObjectEqualityOperator() { + let attribute = Attribute("name") + let predicate:NSPredicate = (attribute == "kyle") + XCTAssertEqual(predicate, NSPredicate(format:"name == 'kyle'")) + } + + func testEqualityOperatorWithNilRHS() { + let attribute = Attribute("name") + let predicate: NSPredicate = attribute == nil + XCTAssertEqual(predicate.description, "name == ") + } +} + +class CollectionAttributeTests: XCTestCase { + func testCountOfSet() { + let setAttribute = Attribute("names") + let countAttribute = count(setAttribute) + XCTAssertEqual(countAttribute, Attribute("names.@count")) + } + + func testCountOfOrderedSet() { + let setAttribute = Attribute("names") + let countAttribute = count(setAttribute) + XCTAssertEqual(countAttribute, Attribute("names.@count")) + } +} + +class BoolAttributeTests: XCTestCase { + var attribute:Attribute! + + override func setUp() { + super.setUp() + attribute = Attribute("hasName") + } + + func testNotAttributeReturnsPredicate() { + XCTAssertEqual(!attribute, NSPredicate(format:"hasName == NO")) + } +} diff --git a/Tests/QueryKitTests/ExpressionTests.swift b/Tests/QueryKitTests/ExpressionTests.swift new file mode 100644 index 0000000..9d823a7 --- /dev/null +++ b/Tests/QueryKitTests/ExpressionTests.swift @@ -0,0 +1,55 @@ +// +// ExpressionTests.swift +// QueryKit +// +// Created by Kyle Fuller on 06/07/2014. +// +// + +import XCTest +import QueryKit + +class ExpressionTests: XCTestCase { + var leftHandSide:NSExpression! + var rightHandSide:NSExpression! + + override func setUp() { + super.setUp() + + leftHandSide = NSExpression(forKeyPath: "age") + rightHandSide = NSExpression(forConstantValue: 10) + } + + func testEqualityOperator() { + XCTAssertEqual(leftHandSide == rightHandSide, NSPredicate(format:"age == 10")) + } + + func testInequalityOperator() { + XCTAssertEqual(leftHandSide != rightHandSide, NSPredicate(format:"age != 10")) + } + + func testGreaterThanOperator() { + let predicate:NSPredicate = leftHandSide > rightHandSide + XCTAssertEqual(predicate, NSPredicate(format:"age > 10")) + } + + func testGreaterOrEqualThanOperator() { + XCTAssertEqual(leftHandSide >= rightHandSide, NSPredicate(format:"age >= 10")) + } + + func testLessThanOperator() { + XCTAssertEqual(leftHandSide < rightHandSide, NSPredicate(format:"age < 10")) + } + + func testLessOrEqualThanOperator() { + XCTAssertEqual(leftHandSide <= rightHandSide, NSPredicate(format:"age <= 10")) + } + + func testLikeOperator() { + XCTAssertEqual(leftHandSide ~= rightHandSide, NSPredicate(format:"age LIKE 10")) + } + + func testInOperator() { + XCTAssertEqual(leftHandSide << rightHandSide, NSPredicate(format:"age IN 10")) + } +} diff --git a/Tests/QueryKitTests/KeyPathTests.swift b/Tests/QueryKitTests/KeyPathTests.swift new file mode 100644 index 0000000..09d44b8 --- /dev/null +++ b/Tests/QueryKitTests/KeyPathTests.swift @@ -0,0 +1,127 @@ +import XCTest +@testable import QueryKit + +class KeyPathTests: XCTestCase { + func testEqualityOperator() { + let predicate: Predicate = \User.name == "kyle" + XCTAssertEqual(predicate.predicate, NSPredicate(format:"name == 'kyle'")) + } + + func testEqualityOperatorWithOptional() { + let predicate: Predicate = \User.name == nil + XCTAssertEqual(predicate.predicate, NSPredicate(format:"name == %@", NSNull())) + } + + func testOptionalEqualityOperatorWithOptional() { + let predicate: Predicate = \User.createdAt == nil + XCTAssertEqual(predicate.predicate, NSPredicate(format:"createdAt == %@", NSNull())) + } + + func testInequalityOperator() { + let predicate: Predicate = \User.name != "kyle" + XCTAssertEqual(predicate.predicate, NSPredicate(format:"name != 'kyle'")) + } + + func testInqqualityOperatorWithOptional() { + let predicate: Predicate = \User.name != nil + XCTAssertEqual(predicate.predicate, NSPredicate(format:"name != %@", NSNull())) + } + + func testGreaterThanOperator() { + let predicate: Predicate = \User.age > 17 + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age > 17")) + } + + func testGreaterThanOrEqualOperator() { + let predicate: Predicate = \User.age >= 18 + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age >= 18")) + } + + func testLessThanOperator() { + let predicate: Predicate = \User.age < 18 + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age < 18")) + } + + func testLessThanOrEqualOperator() { + let predicate: Predicate = \User.age <= 17 + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age <= 17")) + } + + func testLikeOperator() { + let predicate: Predicate = \User.name ~= "k*" + XCTAssertEqual(predicate.predicate, NSPredicate(format:"name LIKE 'k*'")) + } + + func testInOperator() { + let predicate: Predicate = \User.age << [5, 10] + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age IN %@", [5, 10])) + } + + func testBetweenRangeOperator() { + let predicate: Predicate = \User.age << (32 ..< 64) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age BETWEEN %@", [32, 64])) + } +} + +class KeyPathNSPredicateTests: XCTestCase { + func testEqualityOperator() { + let predicate: NSPredicate = \User.name == "kyle" + XCTAssertEqual(predicate, NSPredicate(format:"name == 'kyle'")) + } + + func testEqualityOperatorWithOptional() { + let predicate: NSPredicate = \User.name == nil + XCTAssertEqual(predicate, NSPredicate(format:"name == %@", NSNull())) + } + + func testInequalityOperator() { + let predicate: NSPredicate = \User.name != "kyle" + XCTAssertEqual(predicate, NSPredicate(format:"name != 'kyle'")) + } + + func testInqqualityOperatorWithOptional() { + let predicate: NSPredicate = \User.name != nil + XCTAssertEqual(predicate, NSPredicate(format:"name != %@", NSNull())) + } + + func testGreaterThanOperator() { + let predicate: NSPredicate = \User.age > 17 + XCTAssertEqual(predicate, NSPredicate(format:"age > 17")) + } + + func testGreaterThanOrEqualOperator() { + let predicate: NSPredicate = \User.age >= 18 + XCTAssertEqual(predicate, NSPredicate(format:"age >= 18")) + } + + func testLessThanOperator() { + let predicate: NSPredicate = \User.age < 18 + XCTAssertEqual(predicate, NSPredicate(format:"age < 18")) + } + + func testLessThanOrEqualOperator() { + let predicate: NSPredicate = \User.age <= 17 + XCTAssertEqual(predicate, NSPredicate(format:"age <= 17")) + } + + func testLikeOperator() { + let predicate: NSPredicate = \User.name ~= "k*" + XCTAssertEqual(predicate, NSPredicate(format:"name LIKE 'k*'")) + } + + func testInOperator() { + let predicate: NSPredicate = \User.age << [5, 10] + XCTAssertEqual(predicate, NSPredicate(format:"age IN %@", [5, 10])) + } + + func testBetweenRangeOperator() { + let predicate: NSPredicate = \User.age << (32 ..< 64) + XCTAssertEqual(predicate, NSPredicate(format:"age BETWEEN %@", [32, 64])) + } +} + +class User: NSManagedObject { + @objc var name: String? + @NSManaged var age: Int + @objc var createdAt: Date? +} diff --git a/Tests/QueryKitTests/PredicateTests.swift b/Tests/QueryKitTests/PredicateTests.swift new file mode 100644 index 0000000..1746ecf --- /dev/null +++ b/Tests/QueryKitTests/PredicateTests.swift @@ -0,0 +1,120 @@ +// +// PredicateTests.swift +// QueryKit +// +// Created by Kyle Fuller on 19/06/2014. +// +// + +import XCTest +@testable import QueryKit + + +class NSPredicateTests: XCTestCase { + var namePredicate = NSPredicate(format: "name == Kyle") + var agePredicate = NSPredicate(format: "age >= 21") + + func testAndPredicate() { + let predicate = namePredicate && agePredicate + XCTAssertEqual(predicate, NSPredicate(format: "name == Kyle AND age >= 21")) + } + + func testOrPredicate() { + let predicate = namePredicate || agePredicate + XCTAssertEqual(predicate, NSPredicate(format: "name == Kyle OR age >= 21")) + } + + func testNotPredicate() { + let predicate = !namePredicate + XCTAssertEqual(predicate, NSPredicate(format: "NOT name == Kyle")) + } +} + + +class PredicateTests: XCTestCase { + var attribute:Attribute! + + override func setUp() { + super.setUp() + attribute = Attribute("age") + } + + // MARK: Operators + + func testEqualityOperator() { + let predicate:Predicate = attribute == 10 + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age == 10")) + } + + func testEqualityOperatorWithNilRHS() { + let attribute = Attribute("name") + let predicate: Predicate = attribute == nil + XCTAssertEqual(predicate.predicate.description, "name == ") + } + + func testInequalityOperator() { + let predicate:Predicate = (attribute != 10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age != 10")) + } + + func testGreaterThanOperator() { + let predicate:Predicate = (attribute > 10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age > 10")) + } + + func testGreaterOrEqualThanOperator() { + let predicate:Predicate = (attribute >= 10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age >= 10")) + } + + func testLessThanOperator() { + let predicate:Predicate = (attribute < 10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age < 10")) + } + + func testLessOrEqualThanOperator() { + let predicate:Predicate = (attribute <= 10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age <= 10")) + } + + func testLikeOperator() { + let predicate:Predicate = (attribute ~= 10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age LIKE 10")) + } + + func testInOperator() { + let predicate:Predicate = (attribute << [5, 10]) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age IN %@", [5, 10])) + } + + func testBetweenRangeOperator() { + let predicate:Predicate = attribute << (5..<10) + XCTAssertEqual(predicate.predicate, NSPredicate(format:"age BETWEEN %@", [5, 10])) + } + + func testNSObjectEqualityOperator() { + let attribute = Attribute("name") + let predicate:Predicate = (attribute == "kyle") + XCTAssertEqual(predicate.predicate, NSPredicate(format:"name == 'kyle'")) + } + + // MARK: + + var namePredicate = Predicate(predicate: NSPredicate(format: "name == Kyle")) + var agePredicate = Predicate(predicate: NSPredicate(format: "age >= 21")) + + func testAndPredicate() { + let predicate = namePredicate && agePredicate + XCTAssertEqual(predicate.predicate, NSPredicate(format: "name == Kyle AND age >= 21")) + } + + func testOrPredicate() { + let predicate = namePredicate || agePredicate + XCTAssertEqual(predicate.predicate, NSPredicate(format: "name == Kyle OR age >= 21")) + } + + func testNotPredicate() { + let predicate = !namePredicate + XCTAssertEqual(predicate.predicate, NSPredicate(format: "NOT name == Kyle")) + } +} diff --git a/Tests/QueryKitTests/QueryKitTests.swift b/Tests/QueryKitTests/QueryKitTests.swift new file mode 100644 index 0000000..fc429ab --- /dev/null +++ b/Tests/QueryKitTests/QueryKitTests.swift @@ -0,0 +1,127 @@ +// +// QueryKitTests.swift +// QueryKitTests +// +// Created by Kyle Fuller on 19/06/2014. +// +// + +import XCTest +import QueryKit +import CoreData + +@objc(Person) class Person : NSManagedObject { + @NSManaged var name:String + @NSManaged var company:Company? + + class var entityName:String { + return "Person" + } + + class var name:Attribute { + return Attribute("name") + } + + class var company:Attribute { + return Attribute("company") + } +} + +@objc(Company) class Company : NSManagedObject { + @NSManaged var name:String + + class var entityName:String { + return "Company" + } + + class var name:Attribute { + return Attribute("name") + } + + class func create(_ context:NSManagedObjectContext) -> Company { + return NSEntityDescription.insertNewObject(forEntityName: Company.entityName, into: context) as! Company + } +} + +extension Attribute where AttributeType: Company { + var name:Attribute { + return attribute(AttributeType.name) + } +} + +extension Person { + class func create(_ context:NSManagedObjectContext) -> Person { + return NSEntityDescription.insertNewObject(forEntityName: Person.entityName, into: context) as! Person + } +} + +func managedObjectModel() -> NSManagedObjectModel { + let companyEntity = NSEntityDescription() + companyEntity.name = Company.entityName + companyEntity.managedObjectClassName = "Company" + + let personEntity = NSEntityDescription() + personEntity.name = Person.entityName + personEntity.managedObjectClassName = "Person" + + let companyNameAttribute = NSAttributeDescription() + companyNameAttribute.name = "name" + companyNameAttribute.attributeType = NSAttributeType.stringAttributeType + companyNameAttribute.isOptional = false + + let companyPeopleAttribute = NSRelationshipDescription() + companyPeopleAttribute.name = "members" + companyPeopleAttribute.maxCount = 0 + companyPeopleAttribute.destinationEntity = personEntity + + let personNameAttribute = NSAttributeDescription() + personNameAttribute.name = "name" + personNameAttribute.attributeType = NSAttributeType.stringAttributeType + personNameAttribute.isOptional = false + + let personCompanyRelation = NSRelationshipDescription() + personCompanyRelation.name = "company" + personCompanyRelation.destinationEntity = companyEntity + personCompanyRelation.maxCount = 1 + personCompanyRelation.isOptional = true + + companyPeopleAttribute.inverseRelationship = personCompanyRelation + personCompanyRelation.inverseRelationship = companyPeopleAttribute + + companyEntity.properties = [companyNameAttribute, companyPeopleAttribute] + personEntity.properties = [personNameAttribute, personCompanyRelation] + + let model = NSManagedObjectModel() + model.entities = [personEntity, companyEntity] + + return model +} + +func persistentStoreCoordinator() -> NSPersistentStoreCoordinator { + let model = managedObjectModel() + let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) + do { + try persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil) + } catch { + print(error) + fatalError() + } + return persistentStoreCoordinator +} + +public func AssertNotThrow(_ closure: @autoclosure () throws -> R) -> R? { + var result: R? + AssertNotThrow() { + result = try closure() + } + return result +} + +public func AssertNotThrow(_ closure: () throws -> ()) { + do { + try closure() + } catch let error { + XCTFail("Catched error \(error), " + + "but did not expect any error.") + } +} diff --git a/Tests/QueryKitTests/QuerySetTests.swift b/Tests/QueryKitTests/QuerySetTests.swift new file mode 100644 index 0000000..5ecab25 --- /dev/null +++ b/Tests/QueryKitTests/QuerySetTests.swift @@ -0,0 +1,283 @@ +// +// QuerySetTests.swift +// QueryKit +// +// Created by Kyle Fuller on 06/07/2014. +// +// + +import XCTest +import CoreData +import QueryKit + + +class QuerySetTests: XCTestCase { + var context:NSManagedObjectContext! + var queryset:QuerySet! + + override func setUp() { + super.setUp() + + context = NSManagedObjectContext() + context.persistentStoreCoordinator = persistentStoreCoordinator() + + let company = Company.create(context) + company.name = "Cocode" + + for name in ["Kyle", "Orta", "Ayaka", "Mark", "Scott"] { + let person = Person.create(context) + person.name = name + + if name == "Kyle" { + person.company = company + } + } + + try! context.save() + + queryset = QuerySet(context, "Person") + } + + func testEqualQuerySetIsEquatable() { + XCTAssertEqual(queryset, QuerySet(context, "Person")) + } + + // MARK: Sorting + + func testOrderByKeyPathAscending() { + let qs = queryset.orderBy(\.name, ascending: true) + + XCTAssertEqual(qs.sortDescriptors, [ + NSSortDescriptor(key: "name", ascending: true), + ]) + } + + func testOrderByKeyPathDecending() { + let qs = queryset.orderBy(\.name, ascending: false) + + XCTAssertEqual(qs.sortDescriptors, [ + NSSortDescriptor(key: "name", ascending: false), + ]) + } + + func testOrderBySortDescriptor() { + let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) + let qs = queryset.orderBy(sortDescriptor) + XCTAssertTrue(qs.sortDescriptors == [sortDescriptor]) + } + + func testOrderBySortDescriptors() { + let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) + let qs = queryset.orderBy([sortDescriptor]) + XCTAssertTrue(qs.sortDescriptors == [sortDescriptor]) + } + + func testTypeSafeOrderBySortDescriptor() { + let qs = queryset.orderBy { $0.name.ascending() } + XCTAssertTrue(qs.sortDescriptors == [NSSortDescriptor(key: "name", ascending: true)]) + } + + func testTypeSafeOrderBySortDescriptors() { + let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) + let qs = queryset.orderBy { [$0.name.ascending() as SortDescriptor] } + XCTAssertTrue(qs.sortDescriptors == [sortDescriptor]) + } + + func testReverseOrdering() { + let nameSortDescriptor = NSSortDescriptor(key: "name", ascending: true) + let ageSortDescriptor = NSSortDescriptor(key: "age", ascending: true) + let qs = queryset.orderBy([nameSortDescriptor, ageSortDescriptor]).reverse() + + XCTAssertEqual(qs.sortDescriptors, [ + NSSortDescriptor(key: "name", ascending: false), + NSSortDescriptor(key: "age", ascending: false), + ]) + } + + // MARK: Filtering + + func testFilterKeyPath() { + let qs = queryset.filter(\.name == "Kyle") + XCTAssertEqual(qs.predicate?.description, "name == \"Kyle\"") + } + + func testFilterPredicate() { + let predicate = NSPredicate(format: "name == Kyle") + let qs = queryset.filter(predicate) + XCTAssertEqual(qs.predicate!, predicate) + } + + func testFilterPredicates() { + let predicateName = NSPredicate(format: "name == Kyle") + let predicateAge = NSPredicate(format: "age > 27") + + let qs = queryset.filter([predicateName, predicateAge]) + XCTAssertEqual(qs.predicate!, NSPredicate(format: "name == Kyle AND age > 27")) + } + + func testFilterBooleanAttribute() { + let qs = queryset.filter(Attribute("isEmployed")) + XCTAssertEqual(qs.predicate!, NSPredicate(format: "isEmployed == YES")) + } + + func testTypeSafeFilter() { + let qs = queryset.filter { $0.name == "Kyle" } + + XCTAssertEqual(qs.predicate?.description, "name == \"Kyle\"") + } + + func testTypeSafeFilerEqualWithNilRHS() { + let qs = queryset.filter { $0.name == nil } + XCTAssertEqual(qs.predicate?.description, "name == ") + } + + func testTypeSafeRelatedFilterPredicate() { + let at = Attribute("company") + XCTAssertEqual(at.name.key, "company.name") + let qs = queryset.filter { $0.company.name == "Cocode" } + + XCTAssertEqual(qs.predicate?.description, "company.name == \"Cocode\"") + } + + // MARK: Exclusion + + func testExcludeKeyPath() { + let qs = queryset.exclude(\.name == "Kyle") + XCTAssertEqual(qs.predicate?.description, "NOT name == \"Kyle\"") + } + + func testExcludePredicate() { + let predicate = NSPredicate(format: "name == Kyle") + let qs = queryset.exclude(predicate) + XCTAssertEqual(qs.predicate!, NSPredicate(format: "NOT name == Kyle")) + } + + func testExcludePredicates() { + let predicateName = NSPredicate(format: "name == Kyle") + let predicateAge = NSPredicate(format: "age > 27") + + let qs = queryset.exclude([predicateName, predicateAge]) + XCTAssertEqual(qs.predicate!, NSPredicate(format: "NOT (name == Kyle AND age > 27)")) + } + + func testExcludeBooleanAttribute() { + let qs = queryset.exclude(Attribute("isEmployed")) + XCTAssertEqual(qs.predicate!, NSPredicate(format: "isEmployed == NO")) + } + + func testTypeSafeExclude() { + let qs = queryset.exclude { $0.name == "Kyle" } + + XCTAssertEqual(qs.predicate?.description, "NOT name == \"Kyle\"") + } + + // Fetch Request + + func testConversionToFetchRequest() { + let predicate = NSPredicate(format: "name == Kyle") + let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) + let qs = queryset.filter(predicate).orderBy(sortDescriptor)[2..<4] + + let fetchRequest = qs.fetchRequest + + XCTAssertEqual(fetchRequest.entityName!, "Person") + XCTAssertEqual(fetchRequest.predicate!, predicate) + // XCTAssertEqual(fetchRequest.sortDescriptors!, [sortDescriptor]) + XCTAssertEqual(fetchRequest.fetchOffset, 2) + XCTAssertEqual(fetchRequest.fetchLimit, 2) + } + + // MARK: Subscripting + + func testSubscriptingAtIndex() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) + + let ayaka = try! qs.object(0) + let kyle = try! qs.object(1) + let mark = try! qs.object(2) + let orta = try! qs.object(3) + let scott = try! qs.object(4) + + XCTAssertEqual(ayaka!.name, "Ayaka") + XCTAssertEqual(kyle!.name, "Kyle") + XCTAssertEqual(mark!.name, "Mark") + XCTAssertEqual(orta!.name, "Orta") + XCTAssertEqual(scott!.name, "Scott") + } + + func testSubscriptingRange() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...2] + + XCTAssertEqual(qs.range!.startIndex, 0) + XCTAssertEqual(qs.range!.endIndex, 3) + } + + func testSubscriptingRangeSubscriptsCurrentRange() { + var qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) + qs = qs[2...5] + qs = qs[2...4] + + XCTAssertEqual(qs.range!.startIndex, 4) + XCTAssertEqual(qs.range!.endIndex, 5) + } + + // MARK: Getters + + func testFirst() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) + let name = try! qs.first()?.name + XCTAssertEqual(name, "Ayaka") + } + + func testLast() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) + let name = try! qs.last()?.name + XCTAssertEqual(name, "Scott") + } + + // MARK: Conversion + + func testConversionToArray() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...1] + let people = AssertNotThrow(try qs.array()) ?? [] + + XCTAssertEqual(people.count, 2) + } + + // MARK: Count + + func testCount() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true))[0...1] + let count = AssertNotThrow(try qs.count()) + + XCTAssertEqual(count!, 2) + } + + // MARK: Exists + + func testExistsReturnsTrueWithMatchingObjects() { + let qs = queryset.filter(NSPredicate(format: "name == %@", "Kyle")) + let exists = AssertNotThrow(try qs.exists()) ?? false + + XCTAssertTrue(exists) + } + + func testExistsReturnsFalseWithNoMatchingObjects() { + let qs = queryset.filter(NSPredicate(format: "name == %@", "None")) + let exists = AssertNotThrow(try qs.exists()) ?? true + + XCTAssertFalse(exists) + } + + // MARK: Deletion + + func testDelete() { + let qs = queryset.orderBy(NSSortDescriptor(key: "name", ascending: true)) + + let deletedCount = AssertNotThrow(try qs[0...1].delete()) ?? 0 + let count = AssertNotThrow(try qs.count()) ?? 0 + + XCTAssertEqual(deletedCount, 2) + XCTAssertEqual(count, 3) + } +} diff --git a/Tests/QueryKitTests/SortDescriptorTests.swift b/Tests/QueryKitTests/SortDescriptorTests.swift new file mode 100644 index 0000000..34f43ff --- /dev/null +++ b/Tests/QueryKitTests/SortDescriptorTests.swift @@ -0,0 +1,21 @@ +import XCTest +@testable import QueryKit + +class SortDescriptorTests: XCTestCase { + var attribute:Attribute! + + override func setUp() { + super.setUp() + attribute = Attribute("age") + } + + func testAscendingSortDescriptor() { + let sortDescriptor:SortDescriptor = attribute.ascending() + XCTAssertEqual(sortDescriptor.sortDescriptor, NSSortDescriptor(key: "age", ascending: true)) + } + + func testDescendingSortDescriptor() { + let sortDescriptor:SortDescriptor = attribute.descending() + XCTAssertEqual(sortDescriptor.sortDescriptor, NSSortDescriptor(key: "age", ascending: false)) + } +}