Enable support for mapping a relationship flexibly via assignment policies. You can now map a relationship and assign its value by setting, replacing, or unioning (combining) the relationship. closes #1073, closes #989

This commit is contained in:
Blake Watters
2012-12-26 17:56:39 -05:00
parent b858f2753f
commit 92458a1e88
7 changed files with 294 additions and 5 deletions

View File

@@ -444,10 +444,35 @@ NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id valu
return YES;
}
- (BOOL)applyReplaceAssignmentPolicyForRelationshipMapping:(RKRelationshipMapping *)relationshipMapping
{
if (relationshipMapping.assignmentPolicy == RKReplaceAssignmentPolicy) {
if ([self.dataSource respondsToSelector:@selector(mappingOperation:deleteExistingValueOfRelationshipWithMapping:error:)]) {
NSError *error = nil;
BOOL success = [self.dataSource mappingOperation:self deleteExistingValueOfRelationshipWithMapping:relationshipMapping error:&error];
if (! success) {
RKLogError(@"Failed to delete existing value of relationship mapped with RKReplaceAssignmentPolicy: %@", error);
self.error = error;
return NO;
}
} else {
RKLogWarning(@"Requested mapping with `RKReplaceAssignmentPolicy` assignment policy, but the data source does not support it. Mapping has proceeded identically to the `RKSetAssignmentPolicy`.");
}
}
return YES;
}
- (BOOL)mapOneToOneRelationshipWithValue:(id)value mapping:(RKRelationshipMapping *)relationshipMapping
{
// One to one relationship
RKLogDebug(@"Mapping one to one relationship value at keyPath '%@' to '%@'", relationshipMapping.sourceKeyPath, relationshipMapping.destinationKeyPath);
if (relationshipMapping.assignmentPolicy == RKUnionAssignmentPolicy) {
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Invalid assignment policy: cannot union a one-to-one relationship." };
self.error = [NSError errorWithDomain:RKErrorDomain code:RKMappingErrorInvalidAssignmentPolicy userInfo:userInfo];
return NO;
}
id destinationObject = [self destinationObjectForMappingRepresentation:value withMapping:relationshipMapping.mapping];
if (! destinationObject) {
@@ -458,6 +483,10 @@ NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id valu
// If the relationship has changed, set it
if ([self shouldSetValue:&destinationObject atKeyPath:relationshipMapping.destinationKeyPath]) {
if (! [self applyReplaceAssignmentPolicyForRelationshipMapping:relationshipMapping]) {
return NO;
}
RKLogTrace(@"Mapped relationship object from keyPath '%@' to '%@'. Value: %@", relationshipMapping.sourceKeyPath, relationshipMapping.destinationKeyPath, destinationObject);
[self.destinationObject setValue:destinationObject forKey:relationshipMapping.destinationKeyPath];
} else {
@@ -500,6 +529,14 @@ NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id valu
RKLogWarning(@"WARNING: Detected a relationship mapping for a collection containing another collection. This is probably not what you want. Consider using a KVC collection operator (such as @unionOfArrays) to flatten your mappable collection.");
RKLogWarning(@"Key path '%@' yielded collection containing another collection rather than a collection of objects: %@", relationshipMapping.sourceKeyPath, value);
}
if (relationshipMapping.assignmentPolicy == RKUnionAssignmentPolicy) {
RKLogDebug(@"Mapping relationship with union assignment policy: constructing combined relationship value.");
id existingObjects = [self.destinationObject valueForKeyPath:relationshipMapping.destinationKeyPath];
NSArray *existingObjectsArray = RKTransformedValueWithClass(existingObjects, [NSArray class], nil);
[relationshipCollection addObjectsFromArray:existingObjectsArray];
}
for (id nestedObject in value) {
id mappableObject = [self destinationObjectForMappingRepresentation:nestedObject withMapping:relationshipMapping.mapping];
if (! mappableObject) {
@@ -520,6 +557,9 @@ NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id valu
// If the relationship has changed, set it
if ([self shouldSetValue:&valueForRelationship atKeyPath:relationshipMapping.destinationKeyPath]) {
if (! [self applyReplaceAssignmentPolicyForRelationshipMapping:relationshipMapping]) {
return NO;
}
if (! [self mapCoreDataToManyRelationshipValue:valueForRelationship withMapping:relationshipMapping]) {
RKLogTrace(@"Mapped relationship object from keyPath '%@' to '%@'. Value: %@", relationshipMapping.sourceKeyPath, relationshipMapping.destinationKeyPath, valueForRelationship);
[self.destinationObject setValue:valueForRelationship forKeyPath:relationshipMapping.destinationKeyPath];