Improve relationship connection behaviors when all connection attributes resolve to nil. Add logic for skipping the connection entirely. refs #1099

This commit is contained in:
Blake Watters
2012-12-26 10:53:16 -05:00
parent d1568cad63
commit 7d63ca36ac
2 changed files with 46 additions and 7 deletions

View File

@@ -38,6 +38,11 @@ static id RKMutableSetValueForRelationship(NSRelationshipDescription *relationsh
return [relationship isOrdered] ? [NSMutableOrderedSet orderedSet] : [NSMutableSet set];
}
static BOOL RKConnectionAttributeValuesIsNotConnectable(NSDictionary *attributeValues)
{
return [[NSSet setWithArray:[attributeValues allValues]] isEqualToSet:[NSSet setWithObject:[NSNull null]]];
}
static NSDictionary *RKConnectionAttributeValuesWithObject(RKConnectionDescription *connection, NSManagedObject *managedObject)
{
NSCAssert([connection isForeignKeyConnection], @"Only valid for a foreign key connection");
@@ -47,7 +52,7 @@ static NSDictionary *RKConnectionAttributeValuesWithObject(RKConnectionDescripti
id sourceValue = [managedObject valueForKey:sourceAttribute];
[destinationEntityAttributeValues setValue:sourceValue ?: [NSNull null] forKey:destinationAttribute];
}
return [[destinationEntityAttributeValues allValues] isEqualToArray:@[ [NSNull null] ]] ? nil : destinationEntityAttributeValues;
return RKConnectionAttributeValuesIsNotConnectable(destinationEntityAttributeValues) ? nil : destinationEntityAttributeValues;
}
@interface RKRelationshipConnectionOperation ()
@@ -136,14 +141,19 @@ static NSDictionary *RKConnectionAttributeValuesWithObject(RKConnectionDescripti
return result;
}
- (id)findConnected
- (id)findConnected:(BOOL *)shouldConnectRelationship
{
*shouldConnectRelationship = YES;
id connectionResult = nil;
if (self.connection.sourcePredicate && ![self.connection.sourcePredicate evaluateWithObject:self.managedObject]) return nil;
if ([self.connection isForeignKeyConnection]) {
NSDictionary *attributeValues = RKConnectionAttributeValuesWithObject(self.connection, self.managedObject);
if ([attributeValues count] == 0) return nil;
// If there are no attribute values available for connecting, skip the connection entirely
if (! attributeValues) {
*shouldConnectRelationship = NO;
return nil;
}
NSSet *managedObjects = [self.managedObjectCache managedObjectsWithEntity:[self.connection.relationship destinationEntity]
attributeValues:attributeValues
inManagedObjectContext:self.managedObjectContext];
@@ -174,10 +184,13 @@ static NSDictionary *RKConnectionAttributeValuesWithObject(RKConnectionDescripti
NSString *relationshipName = self.connection.relationship.name;
RKLogTrace(@"Connecting relationship '%@' with mapping: %@", relationshipName, self.connection);
[self.managedObjectContext performBlockAndWait:^{
self.connectedValue = [self findConnected];
[self.managedObject setValue:self.connectedValue forKeyPath:relationshipName];
RKLogDebug(@"Connected relationship '%@' to object '%@'", relationshipName, self.connectedValue);
if (self.connectionBlock) self.connectionBlock(self, self.connectedValue);
BOOL shouldConnect = YES;
self.connectedValue = [self findConnected:&shouldConnect];
if (shouldConnect) {
[self.managedObject setValue:self.connectedValue forKeyPath:relationshipName];
RKLogDebug(@"Connected relationship '%@' to object '%@'", relationshipName, self.connectedValue);
if (self.connectionBlock) self.connectionBlock(self, self.connectedValue);
}
}];
}

View File

@@ -340,4 +340,30 @@
assertThat(human.cats, hasItems(lola, nil));
}
- (void)testConnectionOfOptionalRelationshipIsSkippedWhenAllAttributesEvaluateToNil
{
RKHuman *human = [RKTestFactory insertManagedObjectForEntityForName:@"Human" inManagedObjectContext:nil withProperties:nil];
RKCat *asia = [RKTestFactory insertManagedObjectForEntityForName:@"Cat" inManagedObjectContext:nil withProperties:@{@"birthYear": @2011}];
asia.sex = @"female";
asia.name = @"Asia";
RKCat *lola = [RKTestFactory insertManagedObjectForEntityForName:@"Cat" inManagedObjectContext:nil withProperties:@{@"birthYear": @2012}];
lola.sex = @"female";
lola.name = nil;
human.cats = [NSSet setWithObject:asia];
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:[RKTestFactory managedObjectStore]];
[mapping addConnectionForRelationship:@"cats" connectedBy:@[ @"sex", @"name" ]];
RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
RKConnectionDescription *connection = [mapping connectionForRelationship:@"cats"];
RKRelationshipConnectionOperation *operation = [[RKRelationshipConnectionOperation alloc] initWithManagedObject:human connection:connection managedObjectCache:managedObjectCache];
[operation start];
// Operation should be skipped due to lack of connectable attributes
assertThat(human.cats, hasCountOf(1));
assertThat(human.cats, hasItems(asia, nil));
}
@end