diff --git a/Code/CoreData/RKManagedObject.m b/Code/CoreData/RKManagedObject.m index 0e4bbf1c..bad308e8 100644 --- a/Code/CoreData/RKManagedObject.m +++ b/Code/CoreData/RKManagedObject.m @@ -157,14 +157,8 @@ return [self valueForKey:[[self class] primaryKeyProperty]]; } -- (NSObject*)paramsForSerialization { - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - for (NSString* elementName in [[self class] elementToPropertyMappings]) { - NSString* propertyName = [[[self class] elementToPropertyMappings] objectForKey:elementName]; - [params setValue:[self valueForKey:propertyName] forKey:elementName]; - } - - return [NSDictionary dictionaryWithDictionary:params]; +- (NSDictionary*)propertiesForSerialization { + return RKObjectMappableGetPropertiesByElement(self); } - (BOOL)isNew { diff --git a/Code/ObjectMapping/RKDynamicRouter.m b/Code/ObjectMapping/RKDynamicRouter.m index 1b935642..1aa50c62 100644 --- a/Code/ObjectMapping/RKDynamicRouter.m +++ b/Code/ObjectMapping/RKDynamicRouter.m @@ -95,7 +95,7 @@ - (NSObject*)serializationForObject:(NSObject*)object method:(RKRequestMethod)method { // By default return a form encoded serializable dictionary - return [object paramsForSerialization]; + return [object propertiesForSerialization]; } @end diff --git a/Code/ObjectMapping/RKObject.m b/Code/ObjectMapping/RKObject.m index 3602283c..c99ed2a6 100644 --- a/Code/ObjectMapping/RKObject.m +++ b/Code/ObjectMapping/RKObject.m @@ -23,14 +23,8 @@ return [[self new] autorelease]; } -- (NSObject*)paramsForSerialization { - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - for (NSString* elementName in [[self class] elementToPropertyMappings]) { - NSString* propertyName = [[[self class] elementToPropertyMappings] objectForKey:elementName]; - [params setValue:[self valueForKey:propertyName] forKey:elementName]; - } - - return [NSDictionary dictionaryWithDictionary:params]; +- (NSDictionary*)propertiesForSerialization { + return RKObjectMappableGetPropertiesByElement(self); } @end diff --git a/Code/ObjectMapping/RKObjectLoader.m b/Code/ObjectMapping/RKObjectLoader.m index 5f45ebef..3169c26c 100644 --- a/Code/ObjectMapping/RKObjectLoader.m +++ b/Code/ObjectMapping/RKObjectLoader.m @@ -289,4 +289,23 @@ } } +// Give the target object a chance to modify the request +- (void)triggerWillSendForTargetObject { + if (self.targetObject) { + if ([self.targetObject respondsToSelector:@selector(willSendWithObjectLoader:)]) { + [self.targetObject willSendWithObjectLoader:self]; + } + } +} + +- (void)send { + [self triggerWillSendForTargetObject]; + [super send]; +} + +- (RKResponse*)sendSynchronously { + [self triggerWillSendForTargetObject]; + return [super sendSynchronously]; +} + @end diff --git a/Code/ObjectMapping/RKObjectManager.m b/Code/ObjectMapping/RKObjectManager.m index b7146e00..cf125e20 100644 --- a/Code/ObjectMapping/RKObjectManager.m +++ b/Code/ObjectMapping/RKObjectManager.m @@ -194,10 +194,7 @@ static RKObjectManager* sharedManager = nil; return loader; } -// TODO: Need to factor core data stuff out of here... -// TODO: Use notifications for this??? -// RKObjectManagerWillGETObject / RKObjectManagerWillPOSTObject / RKObjectManagerWillPUTObject / RKObjectManagerWillDELETEObject -// RKObjectManagerDidGETObject / RKObjectManagerDidPOSTObject / RKObjectManagerDidPUTObject / RKObjectManagerDidDELETEObject +// TODO: Need to factor core data stuff out of here... Use notifications (probably at RKObjectLoader level) to trigger save of the object store - (void)saveObjectStore { if (self.objectStore) { NSError* error = [self.objectStore save]; diff --git a/Code/ObjectMapping/RKObjectMappable.h b/Code/ObjectMapping/RKObjectMappable.h index b4fef810..a6616548 100644 --- a/Code/ObjectMapping/RKObjectMappable.h +++ b/Code/ObjectMapping/RKObjectMappable.h @@ -8,6 +8,7 @@ */ @protocol RKRequestSerializable; +@class RKObjectLoader; /** * Must be implemented by all classes utilizing the RKModelMapper to map REST @@ -40,17 +41,40 @@ @optional -/** - * Return a dictionary of values to be serialized for submission to a remote resource. The router - * will encode these parameters into a serialization format (form encoded, JSON, etc). This is - * required to use putObject: and postObject: for updating and creating remote object representations. - */ -- (NSObject*)paramsForSerialization; - /** * Must return a new autoreleased instance of the model class ready for mapping. Used to initialize the model * via any method other than alloc & init. */ + (id)object; +/** + * Return a dictionary of values to be serialized for submission to a remote resource. The router + * will encode these parameters into a serialization format (form encoded, JSON, etc). This is + * required to use putObject: and postObject: for updating and creating remote object representations. + */ +- (NSDictionary*)propertiesForSerialization; + +/** + * Invoked before the mappable object is sent with an Object Loader. This + * can be used to completely customize the behavior of an object loader at the + * model level before sending the request. Note that this is invoked after the + * router has processed and just before the object loader is sent. + * + * If you want to customize the behavior of the parameters sent with the request + * this is the right place to do so. + */ +- (void)willSendWithObjectLoader:(RKObjectLoader*)objectLoader; + @end + +/** + * Returns a dictionary containing all the mappable properties + * and their values for a given mappable object. + */ +NSDictionary* RKObjectMappableGetProperties(NSObject*object); + +/** + * Returns a dictionary containing all the mappable properties + * and their values keyed by the element name. + */ +NSDictionary* RKObjectMappableGetPropertiesByElement(NSObject*object); diff --git a/Code/ObjectMapping/RKObjectMappable.m b/Code/ObjectMapping/RKObjectMappable.m new file mode 100644 index 00000000..4b56c4f4 --- /dev/null +++ b/Code/ObjectMapping/RKObjectMappable.m @@ -0,0 +1,37 @@ +// +// RKObjectMappable.m +// RestKit +// +// Created by Blake Watters on 1/20/11. +// Copyright 2011 Two Toasters. All rights reserved. +// + +#import "RKObjectMappable.h" + +// Return all the mapped properties of object in a dictionary +NSDictionary* RKObjectMappableGetProperties(NSObject*object) { + NSDictionary* mappings = [[object class] elementToPropertyMappings]; + NSMutableDictionary* propertyNamesAndValues = [NSMutableDictionary dictionaryWithCapacity:[mappings count]]; + // Return all the properties of this model in a dictionary under their element names + for (NSString* elementName in mappings) { + NSString* propertyName = [mappings valueForKey:elementName]; + id propertyValue = [object valueForKey:propertyName]; + [propertyNamesAndValues setValue:propertyValue forKey:propertyName]; + } + + return [NSDictionary dictionaryWithDictionary:propertyNamesAndValues]; +} + +// Return all the mapped properties of object in a dictionary under their element names +NSDictionary* RKObjectMappableGetPropertiesByElement(NSObject*object) { + NSDictionary* mappings = [[object class] elementToPropertyMappings]; + NSMutableDictionary* elementsAndPropertyValues = [NSMutableDictionary dictionaryWithCapacity:[mappings count]]; + + for (NSString* elementName in mappings) { + NSString* propertyName = [mappings valueForKey:elementName]; + id propertyValue = [object valueForKey:propertyName]; + [elementsAndPropertyValues setValue:propertyValue forKey:elementName]; + } + + return [NSDictionary dictionaryWithDictionary:elementsAndPropertyValues]; +} diff --git a/Code/ObjectMapping/RKRailsRouter.m b/Code/ObjectMapping/RKRailsRouter.m index 0278fdf2..6c0a2a65 100644 --- a/Code/ObjectMapping/RKRailsRouter.m +++ b/Code/ObjectMapping/RKRailsRouter.m @@ -28,19 +28,6 @@ [_classToModelMappings setObject:modelName forKey:class]; } -- (NSDictionary*)elementNamesAndPropertyValuesForObject:(NSObject*)object { - NSDictionary* mappings = [[object class] elementToPropertyMappings]; - NSMutableDictionary* elementsAndPropertyValues = [NSMutableDictionary dictionaryWithCapacity:[mappings count]]; - // Return all the properties of this model in a dictionary under their element names - for (NSString* elementName in mappings) { - NSString* propertyName = [mappings valueForKey:elementName]; - id propertyValue = [object valueForKey:propertyName]; - [elementsAndPropertyValues setValue:propertyValue forKey:elementName]; - } - - return (NSDictionary*) elementsAndPropertyValues; -} - #pragma mark RKRouter - (NSObject*)serializationForObject:(NSObject*)object method:(RKRequestMethod)method { @@ -49,7 +36,7 @@ return nil; } - NSDictionary* elementsAndProperties = [self elementNamesAndPropertyValuesForObject:object]; + NSDictionary* elementsAndProperties = [object propertiesForSerialization]; NSMutableDictionary* resourceParams = [NSMutableDictionary dictionaryWithCapacity:[elementsAndProperties count]]; NSString* modelName = [_classToModelMappings objectForKey:[object class]]; if (nil == modelName) { diff --git a/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Controllers/DBPostTableViewController.m b/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Controllers/DBPostTableViewController.m index e2eb5c9e..06f06416 100644 --- a/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Controllers/DBPostTableViewController.m +++ b/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Controllers/DBPostTableViewController.m @@ -86,8 +86,8 @@ } } else { [items addObject:[TTTableLongTextItem itemWithText:_post.body]]; - NSString* url = _post.attachmentPath; - [items addObject:[TTTableImageItem itemWithText:@"" imageURL:url URL:nil]]; + NSString* imageURL = _post.attachmentPath; + [items addObject:[TTTableImageItem itemWithText:@"" imageURL:imageURL URL:nil]]; } if ([self.post isNewRecord]) { diff --git a/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Models/DBPost.m b/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Models/DBPost.m index 528bed52..203fef95 100644 --- a/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Models/DBPost.m +++ b/Examples/RKDiscussionBoardExample/DiscussionBoard/Code/Models/DBPost.m @@ -75,29 +75,29 @@ } /** - * Return a serializable representation of this object's properties. This - * serialization will be encoded by the router into a request body and - * sent to the remote service. - * - * A default implementation of paramsForSerialization is provided by the - * RKObject/RKManagedObject base classes, but can be overloaded in the subclass - * for customization. This is useful for including things like transient properties - * in your payloads. + * Invoked just before this Post is sent in an object loader request via + * getObject, postObject, putObject or deleteObject. Here we can manipulate + * the request at will. + * + * The router only has the ability to work with simple dictionaries, so to + * support uploading the attachment we are going to supply our own params + * for the request. */ -- (NSObject*)paramsForSerialization { - // TODO: This is broken! - // TODO: The Rails router does not respect paramsForSerialization. Need to fix that! +- (void)willSendWithObjectLoader:(RKObjectLoader *)objectLoader { RKParams* params = [RKParams params]; - [params setValue:self.body forParam:@"body"]; + + // NOTE - Since we have side-stepped the router, we need to + // nest the param names under the model name ourselves + [params setValue:self.body forParam:@"post[body]"]; NSLog(@"Self Body: %@", self.body); if (_newAttachment) { NSData* data = UIImagePNGRepresentation(_newAttachment); NSLog(@"Data Size: %d", [data length]); - RKParamsAttachment* attachment = [params setData:data MIMEType:@"application/octet-stream" forParam:@"attachment"]; + RKParamsAttachment* attachment = [params setData:data MIMEType:@"application/octet-stream" forParam:@"post[attachment]"]; attachment.fileName = @"image.png"; } - - return params; + + objectLoader.params = params; } - (BOOL)hasAttachment { diff --git a/RestKit.xcodeproj/project.pbxproj b/RestKit.xcodeproj/project.pbxproj index 95067972..f9565b74 100644 --- a/RestKit.xcodeproj/project.pbxproj +++ b/RestKit.xcodeproj/project.pbxproj @@ -88,6 +88,7 @@ 253A0933125525F100976E89 /* RKRequestFilterableTTModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 253A089F12551D8D00976E89 /* RKRequestFilterableTTModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 253A09E612552B5300976E89 /* ObjectMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 253A09E512552B5300976E89 /* ObjectMapping.h */; settings = {ATTRIBUTES = (Public, ); }; }; 253A09F612552BDC00976E89 /* Support.h in Headers */ = {isa = PBXBuildFile; fileRef = 253A09F512552BDC00976E89 /* Support.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 253E1B1112E9450700F3E4B0 /* RKObjectMappable.m in Sources */ = {isa = PBXBuildFile; fileRef = 253E1B1012E9450700F3E4B0 /* RKObjectMappable.m */; }; 25431EBB1255640800A315CF /* CoreData.h in Headers */ = {isa = PBXBuildFile; fileRef = 25431EBA1255640800A315CF /* CoreData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2543201C1256179900A315CF /* RKObjectSeeder.h in Headers */ = {isa = PBXBuildFile; fileRef = 253A088812551D8D00976E89 /* RKObjectSeeder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2543201D1256179900A315CF /* RKObjectSeeder.m in Sources */ = {isa = PBXBuildFile; fileRef = 253A088912551D8D00976E89 /* RKObjectSeeder.m */; }; @@ -481,6 +482,7 @@ 253A09E512552B5300976E89 /* ObjectMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectMapping.h; sourceTree = ""; }; 253A09F512552BDC00976E89 /* Support.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Support.h; sourceTree = ""; }; 253A0A8E1255300000976E89 /* Protect.command */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Protect.command; sourceTree = ""; }; + 253E1B1012E9450700F3E4B0 /* RKObjectMappable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectMappable.m; sourceTree = ""; }; 25431EBA1255640800A315CF /* CoreData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreData.h; sourceTree = ""; }; 25432040125618F000A315CF /* RKParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKParser.h; sourceTree = ""; }; 255DE03010FF9BDF00A85891 /* RKManagedObjectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKManagedObjectSpec.m; sourceTree = ""; }; @@ -880,6 +882,7 @@ 259562E3126D3B36004BAC4C /* RKDynamicRouter.m */, 259562E6126D3B43004BAC4C /* RKRailsRouter.h */, 259562E7126D3B43004BAC4C /* RKRailsRouter.m */, + 253E1B1012E9450700F3E4B0 /* RKObjectMappable.m */, ); path = ObjectMapping; sourceTree = ""; @@ -1700,6 +1703,7 @@ 253A09011255246900976E89 /* RKObjectPropertyInspector.m in Sources */, 259562E5126D3B36004BAC4C /* RKDynamicRouter.m in Sources */, 259562E9126D3B43004BAC4C /* RKRailsRouter.m in Sources */, + 253E1B1112E9450700F3E4B0 /* RKObjectMappable.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Specs/RKObjectSpec.m b/Specs/RKObjectSpec.m index cacb44d6..63a95369 100644 --- a/Specs/RKObjectSpec.m +++ b/Specs/RKObjectSpec.m @@ -33,7 +33,7 @@ nil]; } -- (void)itShouldReturnTheColorParamsForSerialization { +- (void)itShouldReturnTheColorPropertiesForSerialization { self.age = [NSNumber numberWithInt:10]; self.favoriteColor = @"blue"; @@ -41,7 +41,7 @@ @"myFavoriteColor", @"blue", @"myAge", [NSNumber numberWithInt:10], nil]; - [expectThat([self paramsForSerialization]) should:be(expectedParams)]; + [expectThat([self propertiesForSerialization]) should:be(expectedParams)]; } @end