module CDQ class CDQRelationshipQuery < CDQTargetedQuery def initialize(owner, name, set = nil, opts = {}) @owner = owner ? WeakRef.new(owner) : nil @relationship_name = name @set = set ? WeakRef.new(set) : nil relationship = owner.entity.relationshipsByName[name] if relationship.isToMany if @owner.ordered_set?(name) @set ||= @owner.mutableOrderedSetValueForKey(name) else @set ||= @owner.mutableSetValueForKey(name) end end @inverse_rel = relationship.inverseRelationship entity_description = relationship.destinationEntity target_class = constantize(entity_description.managedObjectClassName) super(entity_description, target_class, opts) if @inverse_rel.isToMany @predicate = self.where(@inverse_rel.name.to_sym).contains(owner).predicate else @predicate = self.where(@inverse_rel.name.to_sym => @owner).predicate end end def dealloc super end # Creates a new managed object within the target relationship # def new(opts = {}) super(opts).tap do |obj| add(obj) end end # Add an existing object to the relationship # def add(obj) @set.addObject obj end alias_method :<<, :add # Remove objects from the relationship # def remove(obj) @set.removeObject obj end def remove_all @set.removeAllObjects end def self.extend_set(set, owner, name) set.extend SetExt set.extend Enumerable set.__query__ = self.new(owner, name, set) set end # A Core Data relationship set is extended with this module to provide # scoping by forwarding messages to a CDQRelationshipQuery instance knows # how to create further queries based on the underlying relationship. module SetExt attr_accessor :__query__ def set self end # This works in a special way. If we're extending a regular NSSet, it will # create a new method that calls allObjects. If we're extending a NSOrderedSet, # the override will not work, and we get the array method already defined on # NSOrderedSet, which is actually exactly what we want. def array self.allObjects end def first(n = 1) n == 1 ? array.first : array.first(n) end def last(n = 1) n == 1 ? array.last : array.last(n) end # duplicating a lot of common methods because it's way faster than using method_missing # def each(*args, &block) array.each(*args, &block) end def add(obj) @__query__.add(obj) end alias_method :<<, :add def create(opts = {}) @__query__.create(opts) end def new(opts = {}) @__query__.new(opts) end def remove(opts = {}) @__query__.remove(opts) end def where(*args) @__query__.where(*args) end def sort_by(*args) @__query__.sort_by(*args) end def limit(*args) @__query__.limit(*args) end def offset(*args) @__query__.offset(*args) end def respond_to?(method) super(method) || @__query__.respond_to?(method) end def method_missing(method, *args, &block) if @__query__.respond_to?(method) @__query__.send(method, *args, &block) else super end end end end end