Added Object Mapping block helpers to RKObjectManager and RKObjectMapping. These enable you to perform ad-hoc object mapping very easily. Extended RKObjectRouter to match on superclasses if no specific route is found. This is helpful when using mocked objects with frameworks like Kiwi. fixes #238

This commit is contained in:
Blake Watters
2011-07-23 23:04:16 -04:00
parent 06e2f6665d
commit 80366afa84
7 changed files with 197 additions and 6 deletions

View File

@@ -211,6 +211,63 @@ typedef enum {
*/
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
////////////////////////////////////////////////////////
/// @name Block Configured Object Loaders
#if NS_BLOCKS_AVAILABLE
/**
Configure and send an object loader after yielding it to a block for configuration. This allows for very succinct on-the-fly
configuration of the request without obtaining an object reference via objectLoaderForObject: and then sending it yourself.
For example:
- (BOOL)changePassword:(NSString*)newPassword error:(NSError**)error {
if ([self validatePassword:newPassword error:error]) {
self.password = newPassword;
[[RKObjectManager sharedManager] sendObject:self method:RKRequestMethodPOST delegate:self block:^(RKObjectLoader* loader) {
loader.serializationMIMEType = RKMIMETypeJSON; // We want to send this request as JSON
loader.targetObject = nil; // Map the results back onto a new object instead of self
// Set up a custom serialization mapping to handle this request
loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
[mapping mapAttributes:@"password", nil];
}];
}];
}
}
*/
- (RKObjectLoader*)sendObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
GET a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
POST a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
PUT a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
DELETE a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
#endif
//////
/**

View File

@@ -190,6 +190,31 @@ static RKObjectManager* sharedManager = nil;
return loader;
}
#pragma mark - Block Configured Object Loaders
- (RKObjectLoader*)sendObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
RKObjectLoader* loader = [self objectLoaderForObject:object method:method delegate:delegate];
block(loader);
[loader send];
return loader;
}
- (RKObjectLoader*)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodGET delegate:delegate block:block];
}
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodPOST delegate:delegate block:block];
}
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodPUT delegate:delegate block:block];
}
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodDELETE delegate:delegate block:block];
}
#pragma mark - Object Instance Loaders for Non-nested JSON
- (RKObjectLoader*)getObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {

View File

@@ -123,6 +123,53 @@ relationship. Relationships are processed using an object mapping as well.
*/
+ (id)mappingForClass:(Class)objectClass;
/**
Returns an object mapping useful for configuring a serialization mapping. The object
class is configured as NSMutableDictionary
*/
+ (id)serializationMapping;
#if NS_BLOCKS_AVAILABLE
/**
Returns an object mapping targeting the specified class. The RKObjectMapping instance will
be yieled to the block so that you can perform on the fly configuration without having to
obtain a reference variable for the mapping.
For example, consider we have a one-off request that will load a few attributes for our object.
Using blocks, this is very succinct:
[[RKObjectManager sharedManager] postObject:self delegate:self block:^(RKObjectLoader* loader) {
loader.objectMapping = [RKObjectMapping mappingForClass:[Person class] block:^(RKObjectMapping* mapping) {
[mapping mapAttributes:@"email", @"first_name", nil];
}];
}];
*/
+ (id)mappingForClass:(Class)objectClass block:(void(^)(RKObjectMapping*))block;
/**
Returns serialization mapping for encoding a local object to a dictionary for transport. The RKObjectMapping instance will
be yieled to the block so that you can perform on the fly configuration without having to
obtain a reference variable for the mapping.
For example, consider we have a one-off request within which we want to post a subset of our object
data. Using blocks, this is very succinct:
- (BOOL)changePassword:(NSString*)newPassword error:(NSError**)error {
if ([self validatePassword:newPassword error:error]) {
self.password = newPassword;
[[RKObjectManager sharedManager] putObject:self delegate:self block:^(RKObjectLoader* loader) {
loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
[mapping mapAttributes:@"password", nil];
}];
}];
}
}
Using the block forms we are able to quickly configure and send this request on the fly.
*/
+ (id)serializationMappingWithBlock:(void(^)(RKObjectMapping*))block;
#endif
/**
Add a configured attribute mapping to this object mapping

View File

@@ -28,6 +28,23 @@ NSString* const RKObjectMappingNestingAttributeKeyName = @"<RK_NESTING_ATTRIBUTE
return [mapping autorelease];
}
+ (id)serializationMapping {
return [self mappingForClass:[NSMutableDictionary class]];
}
+ (id)mappingForClass:(Class)objectClass block:(void(^)(RKObjectMapping*))block {
RKObjectMapping* mapping = [self mappingForClass:objectClass];
block(mapping);
return mapping;
}
+ (id)serializationMappingWithBlock:(void(^)(RKObjectMapping*))block {
RKObjectMapping* mapping = [self serializationMapping];
block(mapping);
return mapping;
}
- (id)init {
self = [super init];
if (self) {

View File

@@ -27,12 +27,12 @@
- (void)routeClass:(Class)class toResourcePath:(NSString*)resourcePath forMethodName:(NSString*)methodName {
NSString* className = NSStringFromClass(class);
if (nil == [_routes objectForKey:className]) {
if (nil == [_routes objectForKey:class]) {
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
[_routes setObject:dictionary forKey:className];
[_routes setObject:dictionary forKey:class];
}
NSMutableDictionary* classRoutes = [_routes objectForKey:className];
NSMutableDictionary* classRoutes = [_routes objectForKey:class];
if ([classRoutes objectForKey:methodName]) {
[NSException raise:nil format:@"A route has already been registered for class '%@' and HTTP method '%@'", className, methodName];
}
@@ -75,8 +75,26 @@
- (NSString*)resourcePathForObject:(NSObject*)object method:(RKRequestMethod)method {
NSString* methodName = [self HTTPVerbForMethod:method];
NSString* className = NSStringFromClass([object class]);
NSDictionary* classRoutes = [_routes objectForKey:className];
NSString* className = NSStringFromClass([object class]);
NSDictionary* classRoutes = nil;
// Check for exact matches
for (Class possibleClass in _routes) {
if ([object isMemberOfClass:possibleClass]) {
classRoutes = [_routes objectForKey:possibleClass];
break;
}
}
// Check for superclass matches
if (! classRoutes) {
for (Class possibleClass in _routes) {
if ([object isKindOfClass:possibleClass]) {
classRoutes = [_routes objectForKey:possibleClass];
break;
}
}
}
NSString* resourcePath = nil;
if ((resourcePath = [classRoutes objectForKey:methodName])) {