Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Return the real Core data relionship set, but extend it with scope ca…

…pabilities.

Also fix the issues surrounding the subclass that's dynamically
generated by Core Data by inflecting the real class name from the
dynamic subclass name and always getting the entity description from the
real model class.
  • Loading branch information...
commit 9c45cd5fc44e95f7146951b89bb0fca489e8bff2 1 parent 87e781f
@alloy authored
View
2  TODO
@@ -1,3 +1,5 @@
+* Add to Scope classes: count/size
+
* Rename `new` methods, like Image.new or a relationship Recipe#newImage, to `insert` to adhere to CD name?
* Rename Schema to Model to adhere to CD name?
* The `class` of an association should be inflected from the name.
View
45 app/managed_object.rb
@@ -33,9 +33,41 @@ def inherited(klass)
MotionData::Schema.current.registerEntity(klass.entityDescription)
end
+ # Core Data dynamically creates subclasses of model classes in order to
+ # add the property accessors. These subclasses are named after the user’s
+ # class, but contain underscores.
+ #
+ # E.g. if the user’s model would be called `Author`, the dynamic subclass
+ # would be called `Author_Author_`.
+ def dynamicSubclass?
+ @dynamicSubclass = name.include?('_') if @dynamicSubclass.nil?
+ @dynamicSubclass
+ end
+
+ # Returns the model class as defined by the user, even if called on a
+ # class dynamically defined by Core Data.
+ def modelClass
+ @modelClass ||= begin
+ if dynamicSubclass?
+ # TODO this will probably break if the user namespaces the model class
+ Object.const_get(name.split('_').first)
+ else
+ self
+ end
+ end
+ end
+
+ # Returns the entity description from the model class as defined by the
+ # user, even if called on a class dynamically defined by Core Data.
def entityDescription
- @entityDescription ||= EntityDescription.new.tap do |ed|
- ed.name = ed.managedObjectClassName = name
+ @entityDescription ||= begin
+ if dynamicSubclass?
+ modelClass.entityDescription
+ else
+ EntityDescription.new.tap do |ed|
+ ed.name = ed.managedObjectClassName = name
+ end
+ end
end
end
@@ -88,12 +120,11 @@ def scopeByName(name)
# +[MotionDataManagedObjectBase defineRelationshipMethod:]
def relationshipByName(name)
willAccessValueForKey(name)
- scope = Scope::Relationship.alloc.initWithTarget(primitiveValueForKey(name),
- relationshipName:name,
- owner:self,
- ownerClass:self.class)
+ set = Scope::Relationship.extendRelationshipSetWithScope(primitiveValueForKey(name),
+ relationshipName:name,
+ owner:self)
didAccessValueForKey(name)
- scope
+ set
end
def writeAttribute(key, value)
View
61 app/scope.rb
@@ -139,11 +139,63 @@ def setByApplyingConditionsToSet(set)
class Scope
class Relationship < Scope::Set
- attr_accessor :relationshipName, :owner, :ownerClass
+ # A Core Data relationship set is extended with this module to provide
+ # scoping.
+ #
+ # TODO declare these delegating methods in a dynamic way which ensures we
+ # always forward all the methods from the Scope classes.
+ module SetExt
+ attr_accessor :__scope__
+
+ def new(properties = nil)
+ @__scope__.new(properties)
+ end
+
+ def where(conditions, *formatArguments)
+ @__scope__.where(conditions, *formatArguments)
+ end
+
+ def sortBy(keyPathOrSortDescriptor)
+ @__scope__.sortBy(keyPathOrSortDescriptor)
+ end
+
+ def sortBy(keyPath, ascending:ascending)
+ @__scope__.sortBy(keyPath, ascending:ascending)
+ end
+
+ def each(&block)
+ @__scope__.each(&block)
+ end
+
+ def array
+ @__scope__.array
+ end
+
+ # TODO RM bug? aliased methods in subclasses don't call overriden version of aliased method.
+ #alias_method :to_a, :array
+
+ def to_a
+ @__scope__.array
+ end
+
+ def set
+ self
+ end
+ end
+
+ def self.extendRelationshipSetWithScope(set, relationshipName:relationshipName, owner:owner)
+ set.extend SetExt
+ set.__scope__ = Relationship.alloc.initWithTarget(set,
+ relationshipName:relationshipName,
+ owner:owner)
+ set
+ end
+
+ attr_accessor :relationshipName, :owner
- def initWithTarget(target, relationshipName:relationshipName, owner:owner, ownerClass:ownerClass)
+ def initWithTarget(target, relationshipName:relationshipName, owner:owner)
if initWithTarget(target)
- @relationshipName, @owner, @ownerClass = relationshipName, owner, ownerClass
+ @relationshipName, @owner = relationshipName, owner
end
self
end
@@ -187,7 +239,7 @@ def method_missing(method, *args, &block)
private
def relationshipDescription
- @ownerClass.entityDescription.relationshipsByName[@relationshipName]
+ @owner.class.entityDescription.relationshipsByName[@relationshipName]
end
def targetEntityDescription
@@ -206,7 +258,6 @@ def scopeWithPredicate(predicate, sortDescriptors:sortDescriptors)
scope = super
scope.relationshipName = @relationshipName
scope.owner = @owner
- scope.ownerClass = @ownerClass
scope
end
end
View
2  spec/context_spec.rb
@@ -125,7 +125,7 @@ module MotionData
Author.new(:name => "Edgar Allan Poe")
sleep 0.1
end
- Author.all.size.should == 0
+ Author.all.to_a.size.should == 0
wait 0.3 do
Author.all.map(&:name).should == ["Edgar Allan Poe"]
end
View
26 spec/managed_object_spec.rb
@@ -10,6 +10,23 @@ module MotionData
#MagicalRecord.cleanUp
end
+ it "returns wether or not it's an actual model class defined by the user or a dynamic subclass defined by Core Data" do
+ Author.should.not.be.dynamicSubclass
+ Author.new.class.should.be.dynamicSubclass
+ end
+
+ it "always returns the model class defined by the user, not the dynamic subclass defined by Core Data" do
+ Author.modelClass.should == Author
+ Author.new.class.modelClass.should == Author
+ end
+
+ it "always returns the entity description of the model class defined by the user, not from the dynamic subclass defined by Core Data" do
+ Author.entityDescription.name.should == 'Author'
+ Author.entityDescription.managedObjectClassName.should == 'Author'
+ Author.new.class.entityDescription.name.should == 'Author'
+ Author.new.class.entityDescription.managedObjectClassName.should == 'Author'
+ end
+
describe "property types" do
it "includes String support" do
author = Author.new
@@ -49,14 +66,13 @@ module MotionData
Author.edgars.predicate.predicateFormat.should == 'name == "edgar"'
end
- it "returns a Scope::Relationship instead of the normal Core Data set" do
+ it "extends the normal Core Data realtionship set to act like a Scope::Relationship" do
author = Author.new
- author.articles.should.be.instance_of Scope::Relationship
- author.articles.owner.should == author
- author.articles.ownerClass.should == Author
+ author.articles.should.is_a? Scope::Relationship::SetExt
+ author.articles.__scope__.owner.should == author
author.articles.to_a.should == []
- #article1 = author.articles.new(:title => 'article1')
+ article1 = author.articles.new(:title => 'article1')
#article2 = author.articles.new(:title => 'article2')
#author.articles.withTitles.to_a.should == [article1, article2]
end
View
3  spec/scope_spec.rb
@@ -222,8 +222,7 @@ def set(*objects)
@articles = Scope::Relationship.alloc.initWithTarget(@author.primitiveValueForKey('articles'),
relationshipName: :articles,
- owner:@author,
- ownerClass:Author)
+ owner:@author)
end
it "wraps a Core Data relationship set" do
Please sign in to comment.
Something went wrong with that request. Please try again.