Commits

Tuukka Norri committed c0e5230

Work on to-one relationships' KVO notifications
- Had to disable the change in BXPredicateVisitor.m as it had unexpected side-effects. This causes some regression. Unit tests pass, though.
- Change notifications for inverse to-one relationships are now posted only if there are applicable relationships.
- Delete notifications included objects that didn't belong to the notification's entity. Fixed this.
- Noticed that one-to-one relationships had unneeded (and non-functional) -makeAttributeDependency and -removeAttributeDependency. Removed them.
- Fixed value returning in BXRelationshipDescription.
- -willChangeInverseToOneRelationships:from:to: and -didChangeInverseToOneRelationships:from:to: now make the needed changes to value cache.
- BXForeignKey doesn't try to create bad object IDs any longer.

  • Participants
  • Parent commits e98bfd5

Comments (0)

Files changed (6)

Sources/BXDatabaseContext.m

 			if (0 < [objects count])
 			{
 				id rels = [entity inverseToOneRelationships];
-				NSDictionary* oldTargets = [self targetsByObject: objects forRelationships: rels fireFaults: NO];
-				NSDictionary* newTargets = [self targetsByObject: objects forRelationships: rels fireFaults: YES];
-				TSEnumerate (currentObject, e, [objects objectEnumerator])
-					[currentObject willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				NSDictionary* oldTargets = nil;
+				NSDictionary* newTargets = nil;
+				if (0 < [rels count])
+				{
+					oldTargets = [self targetsByObject: objects forRelationships: rels fireFaults: NO];
+					newTargets = [self targetsByObject: objects forRelationships: rels fireFaults: YES];
+					TSEnumerate (currentObject, e, [objects objectEnumerator])
+						[currentObject willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				}
 				
 				//Fault the objects and send the notifications
 				if (shouldFault)
 										  nil];
 				
 				[nc postNotificationName: kBXUpdateEarlyNotification object: entity userInfo: userInfo];
-				TSEnumerate (currentObject, e, [objects objectEnumerator])
-					[currentObject didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				if (0 < [rels count])
+				{
+					TSEnumerate (currentObject, e, [objects objectEnumerator])
+						[currentObject didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				}
 				
 				[nc postNotificationName: kBXUpdateNotification object: entity userInfo: userInfo];
 			}
 			NSArray* objects = [self faultsWithIDs: objectIDs];
 			
 			id rels = [entity inverseToOneRelationships];
-			NSDictionary* oldTargets = [self targetsByObject: objects forRelationships: rels fireFaults: NO];
-			NSDictionary* newTargets = [self targetsByObject: objects forRelationships: rels fireFaults: YES];
-			TSEnumerate (currentObject, e, [objects objectEnumerator])
-				[currentObject willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+			NSDictionary* oldTargets = nil;
+			NSDictionary* newTargets = nil;
+			if (0 < [rels count])
+			{
+				oldTargets = [self targetsByObject: objects forRelationships: rels fireFaults: NO];
+				newTargets= [self targetsByObject: objects forRelationships: rels fireFaults: YES];
+				TSEnumerate (currentObject, e, [objects objectEnumerator])
+					[currentObject willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+			}
 
             //Send the notifications
             NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                 nil];
 			
 			[nc postNotificationName: kBXInsertEarlyNotification object: entity userInfo: userInfo];
-			TSEnumerate (currentObject, e, [objects objectEnumerator])
-				[currentObject didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+			if (0 < [rels count])
+			{
+				TSEnumerate (currentObject, e, [objects objectEnumerator])
+					[currentObject didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+			}
 
 			[nc postNotificationName: kBXInsertNotification object: entity userInfo: userInfo];
         }
         //Post the notifications
         TSEnumerate (entity, e, [idsByEntity keyEnumerator])
         {
-			id rels = [entity inverseToOneRelationships];
+			NSArray* objectIDs = [idsByEntity objectForKey: entity];
 			id objects = [mObjects objectsForKeys: objectIDs notFoundMarker: [NSNull null]];
 
 			TSEnumerate (currentID, e, [objectIDs objectEnumerator])
 				[[self registeredObjectWithID: currentID] setDeleted: kBXObjectDeleted];
         
-			NSDictionary* oldTargets = [self targetsByObject: objects forRelationships: rels fireFaults: NO];
-			NSDictionary* newTargets = [self targetsByObject: objects forRelationships: rels fireFaults: YES];
-			TSEnumerate (currentObject, e, [objects objectEnumerator])
+			id rels = [entity inverseToOneRelationships];
+			NSDictionary* oldTargets = nil;
+			NSDictionary* newTargets = nil;
+			if (0 < [rels count])
 			{
-				if ([NSNull null] != currentObject)
-					[currentObject willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				oldTargets= [self targetsByObject: objects forRelationships: rels fireFaults: NO];
+				newTargets = [self targetsByObject: objects forRelationships: rels fireFaults: YES];
+				TSEnumerate (currentObject, e, [objects objectEnumerator])
+				{
+					if ([NSNull null] != currentObject)
+						[currentObject willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				}
 			}
         
 			//Send the notifications
 			
 			[nc postNotificationName: kBXDeleteEarlyNotification object: entity userInfo: userInfo];
 			
-			TSEnumerate (currentObject, e, [objects objectEnumerator])
+			if (0 < [rels count])
 			{
-				if ([NSNull null] != currentObject)
-					[currentObject didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
-			}			
+				TSEnumerate (currentObject, e, [objects objectEnumerator])
+				{
+					if ([NSNull null] != currentObject)
+						[currentObject didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				}
+			}
 			
 			[nc postNotificationName: kBXDeleteNotification object: entity userInfo: userInfo];
 		}

Sources/BXDatabaseObject.m

 				BXAttributeDescription* attr = [[entity attributesByName] objectForKey: aKey];
 				
 				NSSet* rels = [attr dependentRelationships];
-				id oldTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
-				[self setCachedValue: aVal forKey: aKey];
-				id newTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
-				[self willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				id oldTargets = nil;
+				id newTargets = nil;
+				if (0 < [rels count])
+				{
+					oldTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
+					[self setCachedValue: aVal forKey: aKey];
+					newTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
+					[self willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				}
 				
 				[mContext executeUpdateObject: self entity: nil predicate: nil
 							   withDictionary: [NSDictionary dictionaryWithObject: aVal forKey: attr]
 										error: &error];
 				
-				[self didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+				if (0 < [rels count])
+					[self didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
 				break;
 			}
 				
 	TSEnumerate (currentAttr, e, [dict objectEnumerator])
 		[rels unionSet: [currentAttr dependentRelationships]];
 
-	id oldTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
-	[self setCachedValuesForKeysWithDictionary: aDict];
-	id newTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
-	[self willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+	id oldTargets = nil;
+	id newTargets = nil;
+	if (0 < [rels count])
+	{
+		oldTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
+		[self setCachedValuesForKeysWithDictionary: aDict];
+		newTargets = [[rels PGTSKeyCollect] registeredTargetFor: self fireFault: NO];
+		[self willChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+	}
 		
     if (! [mContext executeUpdateObject: self entity: nil predicate: nil withDictionary: dict error: &error])
 		[[mContext internalDelegate] databaseContext: mContext hadError: error willBePassedOn: NO];
 	
-	[self didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
+	if (0 < [rels count])
+		[self didChangeInverseToOneRelationships: rels from: oldTargets to: newTargets];
 }
 
 /** 
 									  to: (NSDictionary *) newTargets 
 								callback: (void (*)(id, NSString*)) callback
 {
+	//The given relationships respond to -isInverse with YES.
 	ExpectV (relationships);
-	ExpectV (oldTargets);
-	ExpectV (newTargets);
-	
 	TSEnumerate (currentRelationship, e, [relationships objectEnumerator])
 	{
 		callback (self, [currentRelationship name]);
 - (void) willChangeInverseToOneRelationships: (id) relationships from: (NSDictionary *) oldTargets to: (NSDictionary *) newTargets
 {
 	[self changeInverseToOneRelationships: relationships from: oldTargets to: newTargets callback: &WillChange];
+	@synchronized (mValues)
+	{
+		TSEnumerate (currentRel, e, [relationships objectEnumerator])
+			[mValues removeObjectForKey: [currentRel name]];
+	}
 }
 
 - (void) didChangeInverseToOneRelationships: (id) relationships from: (NSDictionary *) oldTargets to: (NSDictionary *) newTargets
 {
+	@synchronized (mValues)
+	{
+		TSEnumerate (currentRel, e, [relationships objectEnumerator])
+		{
+			id newValue = [newTargets objectForKey: currentRel];
+			if (newValue)
+				[mValues setObject: newValue forKey: [currentRel name]];
+		}
+	}
 	[self changeInverseToOneRelationships: relationships from: oldTargets to: newTargets callback: &DidChange];
 }
 @end

Sources/BXForeignKey.m

 		if (fireFault)
 			value = [object primitiveValueForKey: objectKey];
 		else
+		{
 			value = [object cachedValueForKey: objectKey];
+			if ([NSNull null] == value)
+				value = nil;
+		}
 		
 		if (value)
 			[values setObject: value forKey: name];

Sources/BXOneToOneRelationshipDescription.m

 	NSPredicate* retval = [objectID predicate];
 	return retval;
 }
-
-
-- (void) removeAttributeDependency
-{
-	if (! [self isInverse])
-		[super removeAttributeDependency];
-}
-
-
-- (void) makeAttributeDependency
-{
-	if (! [self isInverse])
-		[super makeAttributeDependency];
-}
 @end

Sources/BXPredicateVisitor.m

 	//First we collect the relationships.
 	//Remember to change the second test, if the last component may be a function name 
 	//instead of an attribute name.
-	while ((currentKey = [e nextObject]) && currentKey != [components lastObject])
+	while ((currentKey = [e nextObject]))// && currentKey != [components lastObject])
 	{
 		property = [[entity relationshipsByName] objectForKey: currentKey];
 		if (! property)

Sources/BXRelationshipDescription.m

 	id retval = nil;
 	//If we can determine an object ID, fetch the target object from the context's cache.
     if (! [self isToMany] && [self isInverse])
-		[self toOneTargetFor: databaseObject registeredOnly: NO fireFault: YES error: error];
+		retval = [self toOneTargetFor: databaseObject registeredOnly: NO fireFault: YES error: error];
 	else
 	{
 		BXEntityDescription* entity = [self destinationEntity];