-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adds NSArray CollectionType conformance and splits up PrediKit file i…
…nto multiple easy to digest files and better organizes files in the project for potential contributors.
- Loading branch information
1 parent
cc9dcfe
commit b7907cc
Showing
31 changed files
with
1,444 additions
and
1,089 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// | ||
// FinalizedIncluder.swift | ||
// PrediKit | ||
// | ||
// Created by Hector Matos on 5/30/16. | ||
// | ||
// | ||
|
||
import Foundation | ||
|
||
/** | ||
A class that finalizes an includer created within the builder closure of PrediKit's `NSPredicate` convenience initializer. All includers (basically any line of code within that closure) must end in a call to a function that returns an instance or subclassed instance of this class to create a valid `NSPredicate`. | ||
*/ | ||
public final class FinalizedIncluder<T: Reflectable> { | ||
private let builder: PredicateBuilder<T> | ||
private var finalArguments: [AnyObject?] | ||
|
||
private(set) var finalPredicateString: String | ||
private(set) var ANDPredicatesToCombine: [String] = [] | ||
private(set) var ORPredicatesToCombine: [String] = [] | ||
|
||
init(builder: PredicateBuilder<T>, arguments: [AnyObject?] = []) { | ||
self.builder = builder | ||
self.finalPredicateString = builder.predicateString | ||
self.finalArguments = arguments | ||
} | ||
|
||
private static func combine<T>(lhs: FinalizedIncluder<T>, rhs: FinalizedIncluder<T>, logicalAND: Bool) -> FinalizedIncluder<T> { | ||
let lhsPredicate = lhs.finalPredicateString | ||
let rhsPredicate = rhs.finalPredicateString | ||
let predicateFormat: String | ||
|
||
var lhsPredicatesToCombine = logicalAND ? lhs.ANDPredicatesToCombine : lhs.ORPredicatesToCombine | ||
var rhsPredicatesToCombine = logicalAND ? rhs.ANDPredicatesToCombine : rhs.ORPredicatesToCombine | ||
|
||
if lhsPredicatesToCombine.isEmpty && rhsPredicatesToCombine.isEmpty { | ||
lhsPredicatesToCombine = [lhsPredicate, rhsPredicate] | ||
rhsPredicatesToCombine = lhsPredicatesToCombine | ||
} else if rhsPredicatesToCombine.isEmpty { | ||
//Operators associate to the left so there's no need to check if the lhs combination predicate array is empty since it will always have something if it gets here by failing the first check. | ||
lhsPredicatesToCombine.append(rhsPredicate) | ||
rhsPredicatesToCombine = lhsPredicatesToCombine | ||
} else { | ||
lhsPredicatesToCombine.appendContentsOf(rhsPredicatesToCombine) | ||
rhsPredicatesToCombine = lhsPredicatesToCombine | ||
} | ||
|
||
if logicalAND { | ||
lhs.ANDPredicatesToCombine = lhsPredicatesToCombine | ||
rhs.ANDPredicatesToCombine = lhsPredicatesToCombine | ||
|
||
predicateFormat = "(\(lhsPredicatesToCombine.joinWithSeparator(" && ")))" | ||
} else { | ||
lhs.ORPredicatesToCombine = lhsPredicatesToCombine | ||
rhs.ORPredicatesToCombine = lhsPredicatesToCombine | ||
|
||
predicateFormat = "(\(lhsPredicatesToCombine.joinWithSeparator(" || ")))" | ||
} | ||
|
||
lhs.finalPredicateString = predicateFormat | ||
|
||
lhs.builder.predicateString = lhs.finalPredicateString | ||
rhs.builder.predicateString = lhs.builder.predicateString | ||
|
||
lhs.finalArguments = lhs.finalArguments + rhs.finalArguments | ||
rhs.finalArguments = lhs.finalArguments | ||
lhs.builder.arguments = lhs.finalArguments | ||
rhs.builder.arguments = lhs.finalArguments | ||
|
||
return lhs | ||
} | ||
} | ||
|
||
/** | ||
Convenience infix `&&` operator that combines two `FinalizedIncluder<T>` instances into one `FinalizedIncluder<T>` that represents the ANDed compound of the `finalizedPredicate` properties in each instance. | ||
Essentially, you use this operator to join together two includers. | ||
*/ | ||
public func && <T>(lhs: FinalizedIncluder<T>, rhs: FinalizedIncluder<T>) -> FinalizedIncluder<T> { | ||
return .combine(lhs, rhs: rhs, logicalAND: true) | ||
} | ||
|
||
/** | ||
Convenience infix `||` operator that combines two `FinalizedIncluder<T>` instances into one `FinalizedIncluder<T>` that represents the ORed compound of the `finalizedPredicate` properties in each instance. | ||
Essentially, you use this operator to join together two includers. | ||
*/ | ||
public func || <T>(lhs: FinalizedIncluder<T>, rhs: FinalizedIncluder<T>) -> FinalizedIncluder<T> { | ||
return .combine(lhs, rhs: rhs, logicalAND: false) | ||
} | ||
|
||
/** | ||
Convenience prefix `!` operator that turns `FinalizedIncluder<T>` into its NOT version. | ||
Essentially, you use this operator to indicate the opposite of an includer. | ||
*/ | ||
public prefix func ! <T>(rhs: FinalizedIncluder<T>) -> FinalizedIncluder<T> { | ||
rhs.finalPredicateString = "!(\(rhs.finalPredicateString))" | ||
rhs.builder.predicateString = rhs.finalPredicateString | ||
return rhs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// | ||
// PredicateBuilder.swift | ||
// PrediKit | ||
// | ||
// Created by Hector Matos on 5/30/16. | ||
// | ||
// | ||
|
||
import Foundation | ||
|
||
/** | ||
The class that gets passed into the builder closure of PrediKit's `NSPredicate` convenience initializer. | ||
*/ | ||
public class PredicateBuilder<T: Reflectable> { | ||
let type: T.Type | ||
var predicateString: String = "" | ||
var arguments: [AnyObject?] = [] | ||
|
||
/** | ||
Used to indicate that you want to query the actual object checked when the predicate is run. Behaves like the `SELF` in the SQL-like query: | ||
NSPredicate(format: "SELF in names") | ||
*/ | ||
public var SELF: BasicQuery<T> { | ||
return BasicQuery(builder: self, property: "SELF") | ||
} | ||
|
||
init(type: T.Type) { | ||
self.type = type | ||
} | ||
|
||
/** | ||
Describes the key of class `T`'s `String` property you want to query. For example, when creating a predicate that compares a class' string property to a given string: | ||
class Kraken: NSObject { | ||
var theyCallMe: String | ||
} | ||
NSPredicate(format: "theyCallMe == 'Chief Supreme'") | ||
The `property` parameter would be the "theyCallMe" in the example predicate format. | ||
- Parameters: | ||
- property: The name of the property in the class of type `T` | ||
- file: Name of the file the function is being called from. Defaults to `#file` | ||
- line: Number of the line the function is being called from. Defaults to `#line` | ||
*/ | ||
public func string(property: Selector, file: String = #file, line: Int = #line) -> StringQuery<T> { | ||
return StringQuery(builder: self, property: validatedProperty(property, file: file, line: line)) | ||
} | ||
|
||
/** | ||
Describes the key of class `T`'s number type property you want to query. For example, when creating a predicate that compares a number property to a given value: | ||
class Kraken: NSObject { | ||
var numberOfHumansEaten: Int | ||
} | ||
NSPredicate(format: "numberOfHumansEaten >= 6") | ||
The `property` parameter would be the "numberOfHumansEaten" in the example predicate format. | ||
- Parameters: | ||
- property: The name of the property in the class of type `T` | ||
- file: Name of the file the function is being called from. Defaults to `#file` | ||
- line: Number of the line the function is being called from. Defaults to `#line` | ||
*/ | ||
public func number(property: Selector, file: String = #file, line: Int = #line) -> NumberQuery<T> { | ||
return NumberQuery(builder: self, property: validatedProperty(property, file: file, line: line)) | ||
} | ||
|
||
/** | ||
Describes the key of class `T`'s `NSDate` property you want to query. For example, when creating a predicate compares a class' date property to another given date: | ||
class Kraken: NSObject { | ||
var birthdate: NSDate | ||
} | ||
NSPredicate(format: "birthdate == %@", NSDate()) | ||
The `property` parameter would be the "birthdate" in the example predicate format. | ||
- Parameters: | ||
- property: The name of the property in the class of type `T` | ||
- file: Name of the file the function is being called from. Defaults to `#file` | ||
- line: Number of the line the function is being called from. Defaults to `#line` | ||
*/ | ||
public func date(property: Selector, file: String = #file, line: Int = #line) -> DateQuery<T> { | ||
return DateQuery(builder: self, property: validatedProperty(property, file: file, line: line)) | ||
} | ||
|
||
/** | ||
Describes the key of class `T`'s `Bool` property you want to query. For example, when creating a predicate that checks against a given `Bool` flag in a class: | ||
class Kraken: NSObject { | ||
var isHungry: Bool | ||
} | ||
NSPredicate(format: "isHungry == true") | ||
The `property` parameter would be the "isHungry" in the example predicate format. | ||
- Parameters: | ||
- property: The name of the property in the class of type `T` | ||
- file: Name of the file the function is being called from. Defaults to `#file` | ||
- line: Number of the line the function is being called from. Defaults to `#line` | ||
*/ | ||
public func bool(property: Selector, file: String = #file, line: Int = #line) -> BooleanQuery<T> { | ||
return BooleanQuery(builder: self, property: validatedProperty(property, file: file, line: line)) | ||
} | ||
|
||
/** | ||
Describes the key of class `T`'s `CollectionType` property you want to query. This is also the starting point for subqueries on list properties. For example, when creating a predicate that checks if a class' array property has 5 objects: | ||
class Kraken: NSObject { | ||
var friends: [LegendaryCreatures] | ||
} | ||
NSPredicate(format: "friends.@count == 5") | ||
The `property` parameter would be the "friends" in the example predicate format. | ||
- Parameters: | ||
- property: The name of the property in the class of type `T` | ||
- file: Name of the file the function is being called from. Defaults to `#file` | ||
- line: Number of the line the function is being called from. Defaults to `#line` | ||
*/ | ||
public func collection(property: Selector, file: String = #file, line: Int = #line) -> SequenceQuery<T> { | ||
return SequenceQuery(builder: self, property: validatedProperty(property, file: file, line: line)) | ||
} | ||
|
||
/** | ||
Describes a member with a custom type that belongs to class `T` that you want to query. Recursive since it returns an instance of PredicateMemberQuery that is a subclass of PredicateBuilder. For example, when creating a predicate for a specific custom type member: | ||
class Kraken: NSObject { | ||
var friend: LegendaryCreature | ||
} | ||
NSPredicate(format: "friend == %@", legendaryCreature) | ||
The `property` parameter would be the "friend" in the example predicate format. | ||
- Parameters: | ||
- property: The name of the property member in the class of type `T` | ||
- memberType: The Reflectable type of the property member | ||
- file: Name of the file the function is being called from. Defaults to `__FILE__` | ||
- line: Number of the line the function is being called from. Defaults to `__LINE__` | ||
*/ | ||
public func member<U: protocol<Reflectable, AnyObject>>(property: Selector, ofType memberType: U.Type, file: String = #file, line: Int = #line) -> MemberQuery<T, U> { | ||
return MemberQuery(builder: self, property: validatedProperty(property), memberType: memberType) | ||
} | ||
|
||
internal func validatedProperty(property: Selector, file: String = #file, line: Int = #line) -> String { | ||
if !type.properties().contains(property) && self.type != NSObject.self { | ||
#if DEBUG | ||
print("\(String(type)) does not seem to contain property \"\(property)\". This could be due to the optionality of a value type. Possible property key values:\n\(type.properties()).\nWarning in file:\(file) at line \(line)") | ||
#endif | ||
} | ||
|
||
return String(property) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// | ||
// PredicateSubqueryBuilder.swift | ||
// PrediKit | ||
// | ||
// Created by Hector Matos on 5/30/16. | ||
// | ||
// | ||
|
||
import Foundation | ||
|
||
/** | ||
A class that facilitates the creation of subqueries against `T`'s `CollectionType` properties. Used in tandem with the `PredicateSequenceQuery<T>` class. | ||
*/ | ||
public final class PredicateSubqueryBuilder<T: Reflectable>: PredicateBuilder<T> { | ||
override init(type: T.Type) { | ||
super.init(type: type) | ||
} | ||
|
||
override func validatedProperty(property: Selector, file: String, line: Int) -> String { | ||
let subqueryProperty = super.validatedProperty(property, file: file, line: line) | ||
return "$\(String(type))Item.\(subqueryProperty)" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// NSArray+PrediKit.swift | ||
// PrediKit | ||
// | ||
// Created by Hector Matos on 5/30/16. | ||
// | ||
// | ||
|
||
import Foundation | ||
|
||
extension NSArray: CollectionType { | ||
public typealias SubSequence = [AnyObject] | ||
|
||
public var startIndex: Int { return 0 } | ||
public var endIndex: Int { return count } | ||
|
||
public subscript (bounds: Range<Int>) -> [AnyObject] { | ||
return subarrayWithRange(NSRange(location: bounds.startIndex, length: bounds.endIndex - bounds.startIndex)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// | ||
// PrediKit.swift | ||
// KrakenDev | ||
// | ||
// Created by Hector Matos on 5/13/16. | ||
// Copyright © 2016 KrakenDev. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/** | ||
This is where the magic happens. This extension allows anyone to construct an `NSPredicate` from a closure. | ||
*/ | ||
public extension NSPredicate { | ||
/** | ||
A generic convenience initializer that accepts a `Reflectable` type and a builder closure that allows you to construct includers that describe the resulting `NSPredicate` instance. | ||
- Parameters: | ||
- type: The `Reflectable` class type that you'll be querying against. The type you supply here is what PrediKit will inspect to ensure the property names you specify in your includers are contained in that class' property list. | ||
- builder: A closure that you use to generate includers that construct each subpredicate in the created `NSPredicate` | ||
*/ | ||
convenience init<T: Reflectable>(_ type: T.Type, @noescape builder: ((includeIf: PredicateBuilder<T>) -> Void)) { | ||
let predicateBuilder = PredicateBuilder(type: type) | ||
builder(includeIf: predicateBuilder) | ||
|
||
if predicateBuilder.predicateString.isEmpty { | ||
self.init(value: false) | ||
} else { | ||
self.init(format: predicateBuilder.predicateString, argumentArray: predicateBuilder.arguments.flatMap({$0})) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
Convenience infix `&&` operator that combines two `NSPredicate` instances into one ANDed `NSCompoundPredicate` | ||
*/ | ||
public func && (lhs: NSPredicate, rhs: NSPredicate) -> NSPredicate { | ||
return NSCompoundPredicate(andPredicateWithSubpredicates: [lhs, rhs]) | ||
} | ||
|
||
/** | ||
Convenience infix `||` operator that combines two `NSPredicate` instances into one ORed `NSCompoundPredicate` | ||
*/ | ||
public func || (lhs: NSPredicate, rhs: NSPredicate) -> NSPredicate { | ||
return NSCompoundPredicate(orPredicateWithSubpredicates: [lhs, rhs]) | ||
} | ||
|
Oops, something went wrong.