Files
RestKit/Code/ObjectMapping/RKDynamicRouter.m
Andras Hatvani d6d92fc6d4 * Implemented relation mapping in RKDynamicRouter
* Added specs covering the functionality
2011-05-04 12:27:50 +02:00

172 lines
6.0 KiB
Objective-C

//
// RKDynamicRouter.m
// RestKit
//
// Created by Blake Watters on 10/18/10.
// Copyright 2010 Two Toasters. All rights reserved.
//
#import "RKDynamicRouter.h"
#import "RKDynamicRouter.h"
#import "NSDictionary+RKRequestSerialization.h"
@implementation RKDynamicRouter
- (id)init {
if ((self = [super init])) {
_routes = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
[_routes release];
[super dealloc];
}
- (void)routeClass:(Class)class toResourcePath:(NSString*)resourcePath forMethodName:(NSString*)methodName {
NSString* className = NSStringFromClass(class);
if (nil == [_routes objectForKey:className]) {
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
[_routes setObject:dictionary forKey:className];
}
NSMutableDictionary* classRoutes = [_routes objectForKey:className];
if ([classRoutes objectForKey:methodName]) {
[NSException raise:nil format:@"A route has already been registered for class '%@' and HTTP method '%@'", className, methodName];
}
[classRoutes setValue:resourcePath forKey:methodName];
}
- (NSString*)HTTPVerbForMethod:(RKRequestMethod)method {
switch (method) {
case RKRequestMethodGET:
return @"GET";
break;
case RKRequestMethodPOST:
return @"POST";
break;
case RKRequestMethodPUT:
return @"PUT";
break;
case RKRequestMethodDELETE:
return @"DELETE";
break;
default:
return nil;
break;
}
}
// Public
- (void)routeClass:(Class<RKObjectMappable>)class toResourcePath:(NSString*)resourcePath {
[self routeClass:class toResourcePath:resourcePath forMethodName:@"ANY"];
}
- (void)routeClass:(Class)class toResourcePath:(NSString*)resourcePath forMethod:(RKRequestMethod)method {
NSString* methodName = [self HTTPVerbForMethod:method];
[self routeClass:class toResourcePath:resourcePath forMethodName:methodName];
}
#pragma mark RKRouter
- (NSString*)resourcePathForObject:(NSObject<RKObjectMappable>*)object method:(RKRequestMethod)method {
NSString* methodName = [self HTTPVerbForMethod:method];
NSString* className = NSStringFromClass([object class]);
NSDictionary* classRoutes = [_routes objectForKey:className];
NSString* resourcePath = nil;
if ((resourcePath = [classRoutes objectForKey:methodName])) {
return RKMakePathWithObject(resourcePath, object);
}
if ((resourcePath = [classRoutes objectForKey:@"ANY"])) {
return RKMakePathWithObject(resourcePath, object);
}
[NSException raise:nil format:@"Unable to find a routable path for object of type '%@' for HTTP Method '%@'", className, methodName];
return nil;
}
- (NSObject<RKRequestSerializable>*)serializationForObject:(NSObject<RKObjectMappable>*)object method:(RKRequestMethod)method {
// Don't return a serialization for a GET request
// There is an extensive discussion about this on the ASIHTTPRequest list
// See http://groups.google.com/group/asihttprequest/browse_thread/thread/ef79a8333dde6acb
if (method == RKRequestMethodGET) {
return nil;
}
// 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