From 73fe4f12371136a60918b76411f23bfe8e341dcc Mon Sep 17 00:00:00 2001 From: Blake Watters Date: Fri, 30 Mar 2012 09:29:41 -0400 Subject: [PATCH] Added support for object mapping a single object instance onto an NSOrderedSet collection. fixes #616 --- Code/ObjectMapping/RKObjectMappingOperation.m | 21 +++++++++++++----- .../RKObjectMappingNextGenTest.m | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Code/ObjectMapping/RKObjectMappingOperation.m b/Code/ObjectMapping/RKObjectMappingOperation.m index b627c6a9..9c5260e3 100644 --- a/Code/ObjectMapping/RKObjectMappingOperation.m +++ b/Code/ObjectMapping/RKObjectMappingOperation.m @@ -404,8 +404,15 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { return appliedMappings; } +- (BOOL)isTypeACollection:(Class)type { + Class orderedSetClass = NSClassFromString(@"NSOrderedSet"); + return (type && ([type isSubclassOfClass:[NSSet class]] || + [type isSubclassOfClass:[NSArray class]] || + (orderedSetClass && [type isSubclassOfClass:orderedSetClass]))); +} + - (BOOL)isValueACollection:(id)value { - return ([value isKindOfClass:[NSSet class]] || [value isKindOfClass:[NSArray class]]); + return [self isTypeACollection:[value class]]; } - (BOOL)mapNestedObject:(id)anObject toObject:(id)anotherObject withRealtionshipMapping:(RKObjectRelationshipMapping*)relationshipMapping { @@ -474,15 +481,17 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { } // Handle case where incoming content is a single object, but we want a collection - Class relationshipType = [self.objectMapping classForProperty:relationshipMapping.destinationKeyPath]; - BOOL mappingToCollection = (relationshipType && - ([relationshipType isSubclassOfClass:[NSSet class]] || [relationshipType isSubclassOfClass:[NSArray class]])); + Class relationshipType = [self.objectMapping classForProperty:relationshipMapping.destinationKeyPath]; + BOOL mappingToCollection = [self isTypeACollection:relationshipType]; if (mappingToCollection && ![self isValueACollection:value]) { + Class orderedSetClass = NSClassFromString(@"NSOrderedSet"); RKLogDebug(@"Asked to map a single object into a collection relationship. Transforming to an instance of: %@", NSStringFromClass(relationshipType)); if ([relationshipType isSubclassOfClass:[NSArray class]]) { value = [relationshipType arrayWithObject:value]; } else if ([relationshipType isSubclassOfClass:[NSSet class]]) { value = [relationshipType setWithObject:value]; + } else if (orderedSetClass && [relationshipType isSubclassOfClass:orderedSetClass]) { + value = [relationshipType orderedSetWithObject:value]; } else { RKLogWarning(@"Failed to transform single object"); } @@ -530,7 +539,7 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { // If the relationship has changed, set it if ([self shouldSetValue:&destinationObject atKeyPath:relationshipMapping.destinationKeyPath]) { Class managedObjectClass = NSClassFromString(@"NSManagedObject"); - Class nsOrderSetClass = NSClassFromString(@"NSOrderedSet"); + Class nsOrderedSetClass = NSClassFromString(@"NSOrderedSet"); if (managedObjectClass && [self.destinationObject isKindOfClass:managedObjectClass]) { RKLogTrace(@"Found a managedObject collection. About to apply value via mutable[Set|Array]ValueForKey"); if ([destinationObject isKindOfClass:[NSSet class]]) { @@ -541,7 +550,7 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { RKLogTrace(@"Mapped NSArray relationship object from keyPath '%@' to '%@'. Value: %@", relationshipMapping.sourceKeyPath, relationshipMapping.destinationKeyPath, destinationObject); NSMutableArray* destinationArray = [self.destinationObject mutableArrayValueForKey:relationshipMapping.destinationKeyPath]; [destinationArray setArray:destinationObject]; - } else if (nsOrderSetClass && [destinationObject isKindOfClass:nsOrderSetClass]) { + } else if (nsOrderedSetClass && [destinationObject isKindOfClass:nsOrderedSetClass]) { RKLogTrace(@"Mapped NSOrderedSet relationship object from keyPath '%@' to '%@'. Value: %@", relationshipMapping.sourceKeyPath, relationshipMapping.destinationKeyPath, destinationObject); [self.destinationObject setValue:destinationObject forKey:relationshipMapping.destinationKeyPath]; } diff --git a/Tests/Logic/ObjectMapping/RKObjectMappingNextGenTest.m b/Tests/Logic/ObjectMapping/RKObjectMappingNextGenTest.m index 43094049..7ac9ed7d 100644 --- a/Tests/Logic/ObjectMapping/RKObjectMappingNextGenTest.m +++ b/Tests/Logic/ObjectMapping/RKObjectMappingNextGenTest.m @@ -1216,6 +1216,28 @@ assertThatUnsignedInteger([user.friends count], is(equalToInt(1))); } +- (void)testShouldMapANestedObjectToOrderedSetCollection { + RKObjectMapping* userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; + RKObjectAttributeMapping* nameMapping = [RKObjectAttributeMapping mappingFromKeyPath:@"name" toKeyPath:@"name"]; + [userMapping addAttributeMapping:nameMapping]; + RKObjectMapping* addressMapping = [RKObjectMapping mappingForClass:[RKTestAddress class]]; + RKObjectAttributeMapping* cityMapping = [RKObjectAttributeMapping mappingFromKeyPath:@"city" toKeyPath:@"city"]; + [addressMapping addAttributeMapping:cityMapping]; + + RKObjectRelationshipMapping* hasOneMapping = [RKObjectRelationshipMapping mappingFromKeyPath:@"address" toKeyPath:@"friendsOrderedSet" withMapping:addressMapping]; + [userMapping addRelationshipMapping:hasOneMapping]; + + RKObjectMapper* mapper = [RKObjectMapper new]; + id userInfo = [RKTestFixture parsedObjectWithContentsOfFixture:@"user.json"]; + RKTestUser* user = [RKTestUser user]; + BOOL success = [mapper mapFromObject:userInfo toObject:user atKeyPath:@"" usingMapping:userMapping]; + [mapper release]; + assertThatBool(success, is(equalToBool(YES))); + assertThat(user.name, is(equalTo(@"Blake Watters"))); + assertThat(user.friendsOrderedSet, isNot(nilValue())); + assertThatUnsignedInteger([user.friendsOrderedSet count], is(equalToInt(1))); +} + - (void)testShouldMapANestedObjectCollection { RKObjectMapping* userMapping = [RKObjectMapping mappingForClass:[RKTestUser class]]; RKObjectAttributeMapping* nameMapping = [RKObjectAttributeMapping mappingFromKeyPath:@"name" toKeyPath:@"name"];