Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Stash work on predicate/scope building.

  • Loading branch information...
commit 823a3b55dc078a0828fae98962d176f805e80edb 1 parent ff67893
@alloy authored
View
1  Rakefile
@@ -8,6 +8,7 @@ Motion::Project::App.setup do |app|
app/schema.rb
app/store_coordinator.rb
app/context.rb
+ app/predicate.rb
app/managed_object.rb
app/article.rb
View
2  app/article.rb
@@ -1,5 +1,5 @@
class Article < MotionData::ManagedObject
- belongsTo :author, :class => 'Author'
+ #hasOne :author, :class => 'Author'
property :title, String, :required => true
property :body, String, :required => true
View
2  app/author.rb
@@ -1,5 +1,5 @@
class Author < MotionData::ManagedObject
- hasMany :articles, :class => 'Article'
+ #hasMany :articles, :class => 'Article'
property :name, String, :required => true
end
View
92 app/fetch_request.rb
@@ -0,0 +1,92 @@
+module MotionData
+ class Scope
+ attr_reader :target, :predicate, :sortDescriptors, :context
+
+ def initWithTarget(target)
+ initWithTarget(target, predicate:nil, sortDescriptors:nil, inContext:MotionData::Context.current)
+ end
+
+ def initWithTarget(target, predicate:predicate, sortDescriptors:sortDescriptors, inContext:context)
+ if init
+ @target, @predicate, @sortDescriptors, @context = target, predicate, sortDescriptors, context
+ end
+ self
+ end
+
+ # Add finder conditions as a hash of requirements, a Scope, or a NSPredicate.
+ #
+ # The conditions are added using `AND`.
+ def where(conditions)
+ predicate = case conditions
+ when Hash
+ NSCompoundPredicate.andPredicateWithSubpredicates(conditions.map do |key, value|
+ lhs = NSExpression.expressionForKeyPath(key)
+ rhs = NSExpression.expressionForConstantValue(value)
+ NSComparisonPredicate.predicateWithLeftExpression(lhs,
+ rightExpression:rhs,
+ modifier:NSDirectPredicateModifier,
+ type:NSEqualToPredicateOperatorType,
+ options:0)
+ end)
+ when NSPredicate
+ conditions
+ when Scope
+
+ end
+
+ if @predicate
+ predicate = NSCompoundPredicate.andPredicateWithSubpredicates([@predicate, predicate])
+ end
+
+ Scope.alloc.initWithTarget(@target,
+ predicate:predicate,
+ sortDescriptors:@sortDescriptors,
+ inContext:@context)
+ end
+
+ # Add finder conditions as a hash of requirements, a Scope, or a NSPredicate.
+ #
+ # The conditions are added using `OR`.
+ def or(conditions)
+
+ end
+
+ # Sort ascending by an attribute, or a NSSortDescriptor.
+ def sortBy(attribute)
+ sortBy(attribute, ascending:true)
+ end
+
+ # Sort by an attribute.
+ def sortBy(attribute, ascending:ascending)
+
+ end
+
+ # Factory methods
+
+ # Returns a NSFetchRequest with the current scope.
+ def request
+
+ end
+
+ # Executes the request and returns the results as a set.
+ def set
+
+ end
+
+ # Returns a NSFetchedResultsController with this fetch request.
+ def controller(options = {})
+ NSFetchedResultsController.alloc.initWithFetchRequest(self,
+ managedObjectContext:MotionData::Context.current,
+ sectionNameKeyPath:options[:sectionNameKeyPath],
+ cacheName:options[:cacheName])
+ end
+
+ class ToManyRelationship < FetchRequest
+ # Returns the relationship set, normally provided by a Core Data to-many
+ # relationship.
+ def set
+
+ end
+ end
+ end
+end
View
55 app/predicate.rb
@@ -0,0 +1,55 @@
+module MotionData
+ class Predicate < NSComparisonPredicate
+ def inspect
+ "(#{predicateFormat})"
+ end
+
+ def and(predicate)
+ NSCompoundPredicate.andPredicateWithSubpredicates([self, predicate])
+ end
+
+ def or(predicate)
+ NSCompoundPredicate.orPredicateWithSubpredicates([self, predicate])
+ end
+
+ class KeyPathExpression
+ module Mixin
+ def keyPath(keyPath)
+ KeyPathExpression.new(keyPath)
+ end
+ alias_method :key, :keyPath
+ end
+
+ attr_reader :expression
+
+ def initialize(keyPath)
+ @expression = NSExpression.expressionForKeyPath(keyPath.to_s)
+ end
+
+ def <(value); comparisonWith(value, type:NSLessThanPredicateOperatorType); end
+ def >(value); comparisonWith(value, type:NSGreaterThanPredicateOperatorType); end
+ def <=(value); comparisonWith(value, type:NSLessThanOrEqualToPredicateOperatorType); end
+ def >=(value); comparisonWith(value, type:NSGreaterThanOrEqualToPredicateOperatorType); end
+ def !=(value); comparisonWith(value, type:NSNotEqualToPredicateOperatorType); end
+ def ==(value); comparisonWith(value, type:NSEqualToPredicateOperatorType); end
+
+
+ def between?(min, max); comparisonWith([min, max], type:NSBetweenPredicateOperatorType); end
+ def include?(value); comparisonWith(value, type:NSContainsPredicateOperatorType); end
+ def in?(value); comparisonWith(value, type:NSInPredicateOperatorType); end
+ def beginsWith?(string); comparisonWith(string, type:NSBeginsWithPredicateOperatorType); end
+ def endsWith?(string); comparisonWith(string, type:NSEndsWithPredicateOperatorType); end
+
+ private
+
+ def comparisonWith(value, type:comparisonType)
+ value = NSExpression.expressionForConstantValue(value)
+ Predicate.predicateWithLeftExpression(@expression,
+ rightExpression:value,
+ modifier:NSDirectPredicateModifier,
+ type:comparisonType,
+ options:0)
+ end
+ end
+ end
+end
View
3  examples/Recipes/app/controllers/recipe_list_table_view_controller.rb
@@ -35,6 +35,9 @@ def showRecipe(recipe, animated:animated)
end
def fetchedResultsController
+ # TODO
+ # @fetchedResultsController ||= Recipe.all.sortBy(:name, ascending:true).resultsController('Root').tap { |c| c.delegate = self }
+ # @fetchedResultsController ||= Recipe.all.sortBy(:name).resultsController('Root').tap { |c| c.delegate = self }
@fetchedResultsController ||= begin
request = NSFetchRequest.new
request.entity = Recipe.entityDescription
View
69 spec/predicate_spec.rb
@@ -0,0 +1,69 @@
+module MotionData
+
+ describe Predicate::KeyPathExpression do
+ extend Predicate::KeyPathExpression::Mixin
+
+ it "returns a expression for the left-hand side of a comparison" do
+ key(:property).expression.keyPath.should == 'property'
+ keyPath('property.subproperty').expression.keyPath.should == 'property.subproperty'
+ end
+
+ it "returns comparison predicates" do
+ {
+ (key(:amount) < 42) => 'amount < 42',
+ (key(:amount) > 42) => 'amount > 42',
+ (key(:amount) <= 42) => 'amount <= 42',
+ (key(:amount) >= 42) => 'amount >= 42',
+ (key(:amount) != 42) => 'amount != 42',
+ (key(:amount) == 42) => 'amount == 42',
+ }.each do |predicate, format|
+ predicate.predicateFormat.should == format
+ end
+ end
+
+ it "returns a `between` predicate" do
+ predicate = key(:amount).between?(21, 42)
+ predicate.predicateFormat.should == 'amount BETWEEN {21, 42}'
+ end
+
+ it "returns a `in` predicate" do
+ predicate = key(:amount).in?([21, 42])
+ predicate.predicateFormat.should == 'amount IN {21, 42}'
+ end
+
+ it "returns a `contains` predicate" do
+ predicate = key(:amount).include?(42)
+ predicate.predicateFormat.should == 'amount CONTAINS 42'
+ end
+
+ it "returns a `in collection` predicate" do
+ predicate = key(:amount).in?([21, 42])
+ predicate.predicateFormat.should == 'amount IN {21, 42}'
+ end
+
+ it "returns a `begin's with` predicate" do
+ predicate = key(:name).beginsWith?('bob')
+ predicate.predicateFormat.should == 'name BEGINSWITH "bob"'
+ end
+
+ it "returns a `end's with` predicate" do
+ predicate = key(:name).endsWith?('bob')
+ predicate.predicateFormat.should == 'name ENDSWITH "bob"'
+ end
+ end
+
+ describe Predicate do
+ extend Predicate::KeyPathExpression::Mixin
+
+ it "returns a compound `AND` predicate" do
+ predicate = (key(:amount) < 42).and(key(:amount) > 42)
+ predicate.predicateFormat.should == 'amount < 42 AND amount > 42'
+ end
+
+ it "returns a compound `OR` predicate" do
+ predicate = (key(:amount) < 42).or(key(:amount) > 42)
+ predicate.predicateFormat.should == 'amount < 42 OR amount > 42'
+ end
+ end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.