From d6d92fc6d451bad39e338c19485a42b966a81ec7 Mon Sep 17 00:00:00 2001 From: Andras Hatvani Date: Wed, 4 May 2011 12:27:50 +0200 Subject: [PATCH] * Implemented relation mapping in RKDynamicRouter * Added specs covering the functionality --- Code/ObjectMapping/RKDynamicRouter.m | 68 +++++++++++++- Specs/Models/RKCat.m | 10 +++ Specs/ObjectMapping/RKDynamicRouterSpec.m | 103 ++++++++++++++++++++++ 3 files changed, 179 insertions(+), 2 deletions(-) diff --git a/Code/ObjectMapping/RKDynamicRouter.m b/Code/ObjectMapping/RKDynamicRouter.m index f5640f3f..0e1fb1a3 100644 --- a/Code/ObjectMapping/RKDynamicRouter.m +++ b/Code/ObjectMapping/RKDynamicRouter.m @@ -100,8 +100,72 @@ return nil; } - // By default return a form encoded serializable dictionary - return [object propertiesForSerialization]; + // set up dictionary containers + NSDictionary* elementsAndProperties = [object propertiesForSerialization]; + NSDictionary* relationships = [object relationshipsForSerialization]; + int propertyCount = [elementsAndProperties count]; + int relationshipCount = [relationships count]; + int entryCount = propertyCount + relationshipCount; + NSMutableDictionary* resourceParams = [NSMutableDictionary dictionaryWithCapacity:entryCount]; + + // add elements and properties + for (NSString* elementName in [elementsAndProperties allKeys]) { + id value = [elementsAndProperties valueForKey:elementName]; + NSString* attributeName = [elementName stringByReplacingOccurrencesOfString:@"-" withString:@"_"]; + if (![attributeName isEqualToString:@"id"]) { + NSString* keyName = [NSString stringWithFormat:@"%@", attributeName]; + [resourceParams setValue:value forKey:keyName]; + } + } + + void (^handlePrimaryKey)(Class, NSMutableDictionary *, id) = ^(Class blockClass, NSMutableDictionary *attributes, id object) { + if ([blockClass respondsToSelector:@selector(primaryKeyProperty)]) { + NSString *primaryKey = [blockClass performSelector:@selector(primaryKeyProperty)]; + id primaryKeyValue = [object valueForKey:primaryKey]; + NSString* primaryKeyValueString = [NSString stringWithFormat:@"%@", primaryKeyValue]; + + // Primary key value isn't defined + if (primaryKeyValue == nil || [primaryKeyValueString isEqualToString:@"0"]) { + // Exclude id + [attributes removeObjectForKey:@"id"]; + } + } + }; + + // add nested relationships + for (NSString* elementName in [relationships allKeys]) { + NSObject *relationship = [relationships objectForKey:elementName]; + NSString *relationshipPath = [NSString stringWithFormat:@"%@", elementName]; + // to-many relation + if ([relationship isKindOfClass:[NSArray class]] || + [relationship isKindOfClass:[NSSet class]]) { + NSMutableArray *children = [NSMutableArray array]; + for (id child in (NSEnumerator *)relationship) { + Class class = [child class]; + NSMutableDictionary* childAttributes = [RKObjectMappableGetPropertiesByElement(child) mutableCopy]; + + handlePrimaryKey(class, childAttributes, child); + + [children addObject:[NSDictionary dictionaryWithDictionary:childAttributes]]; + } + + [resourceParams setValue:children + forKey:relationshipPath]; + // to-one relation + } else { + Class class = [relationship class]; + NSMutableDictionary* childAttributes = [RKObjectMappableGetPropertiesByElement(relationship) mutableCopy]; + + handlePrimaryKey(class, childAttributes, relationship); + + [resourceParams setValue:childAttributes + forKey:relationshipPath]; + } + + } + + return resourceParams; + } @end diff --git a/Specs/Models/RKCat.m b/Specs/Models/RKCat.m index 42e32c1f..9cfc2454 100644 --- a/Specs/Models/RKCat.m +++ b/Specs/Models/RKCat.m @@ -41,4 +41,14 @@ return @"railsID"; } ++ (NSDictionary*)elementToRelationshipMappings { + return [NSDictionary dictionaryWithObjectsAndKeys: + @"human", @"human", + nil]; +} + ++ (NSArray*)relationshipsToSerialize { + return [NSArray arrayWithObject:@"human"]; +} + @end diff --git a/Specs/ObjectMapping/RKDynamicRouterSpec.m b/Specs/ObjectMapping/RKDynamicRouterSpec.m index 2905d2b6..2c1c25df 100644 --- a/Specs/ObjectMapping/RKDynamicRouterSpec.m +++ b/Specs/ObjectMapping/RKDynamicRouterSpec.m @@ -9,6 +9,7 @@ #import "RKSpecEnvironment.h" #import "RKManagedObjectStore.h" #import "RKHuman.h" +#import "RKCat.h" @interface RKDynamicRouterSpec : NSObject { } @@ -110,4 +111,106 @@ [expectThat(resourcePath) should:be(@"/this/is/the/path")]; } +- (void)itShouldSerializeToOneRelationWithoutId +{ + RKDynamicRouter* router = [[[RKDynamicRouter alloc] init] autorelease]; + + RKHuman* human = [[RKHuman object] autorelease]; + RKCat* cat = [[RKCat object] autorelease]; + + cat.name = @"Cat"; + human.name = @"Owner"; + cat.human = human; + + NSDictionary* serialization = (NSDictionary*) [router serializationForObject:cat method:RKRequestMethodPOST]; + NSDictionary* serializedRelation = [serialization objectForKey:@"human"]; + [expectThat([serializedRelation objectForKey:@"name"]) should:be(@"Owner")]; +} + +- (void)itShouldSerializeToOneRelationWithId +{ + RKDynamicRouter* router = [[[RKDynamicRouter alloc] init] autorelease]; + + RKHuman* human = [[RKHuman object] autorelease]; + RKCat* cat = [[RKCat object] autorelease]; + + cat.name = @"Cat"; + human.railsID = [NSNumber numberWithInt:1]; + human.name = @"Owner"; + cat.human = human; + + NSDictionary* serialization = (NSDictionary*) [router serializationForObject:cat method:RKRequestMethodPOST]; + NSDictionary* serializedRelation = [serialization objectForKey:@"human"]; + [expectThat([serializedRelation objectForKey:@"name"]) should:be(@"Owner")]; +} + + +- (void)itShouldSerializeToManyRelationWithoutId +{ + RKDynamicRouter* router = [[[RKDynamicRouter alloc] init] autorelease]; + + RKHuman* human = [[RKHuman object] autorelease]; + human.name = @"Owner"; + RKCat* cat1 = [[RKCat object] autorelease]; + cat1.name = @"Cat1"; + [human addCatsObject:cat1]; + RKCat* cat2 = [[RKCat object] autorelease]; + cat2.name = @"Cat2"; + [human addCatsObject:cat2]; + + NSDictionary* serialization = (NSDictionary*) [router serializationForObject:human method:RKRequestMethodPOST]; + NSArray* serializedCats = [serialization objectForKey:@"cats"]; + + NSDictionary *serCat1 = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"age", + [NSNumber numberWithInt:0], @"birth_year", + @"Cat1", @"name", + nil]; + + NSDictionary *serCat2 = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"age", + [NSNumber numberWithInt:0], @"birth_year", + @"Cat2", @"name", + nil]; + [expectThat([serializedCats count]) should:be(2)]; + [expectThat([serializedCats containsObject:serCat1]) should:be(TRUE)]; + [expectThat([serializedCats containsObject:serCat2]) should:be(TRUE)]; +} + +- (void)itShouldSerializeToManyRelationWithId +{ + RKDynamicRouter* router = [[[RKDynamicRouter alloc] init] autorelease]; + + RKHuman* human = [[RKHuman object] autorelease]; + human.name = @"Owner"; + RKCat* cat1 = [[RKCat object] autorelease]; + cat1.name = @"Cat1"; + cat1.railsID = [NSNumber numberWithInt:1]; + [human addCatsObject:cat1]; + RKCat* cat2 = [[RKCat object] autorelease]; + cat2.name = @"Cat2"; + cat2.railsID = [NSNumber numberWithInt:2]; + [human addCatsObject:cat2]; + + NSDictionary* serialization = (NSDictionary*) [router serializationForObject:human method:RKRequestMethodPOST]; + NSArray* serializedCats = [serialization objectForKey:@"cats"]; + + NSDictionary *serCat1 = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"age", + [NSNumber numberWithInt:0], @"birth_year", + [NSNumber numberWithInt:1], @"id", + @"Cat1", @"name", + nil]; + + NSDictionary *serCat2 = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"age", + [NSNumber numberWithInt:0], @"birth_year", + [NSNumber numberWithInt:2], @"id", + @"Cat2", @"name", + nil]; + [expectThat([serializedCats count]) should:be(2)]; + [expectThat([serializedCats containsObject:serCat1]) should:be(TRUE)]; + [expectThat([serializedCats containsObject:serCat2]) should:be(TRUE)]; +} + @end