From 4e03ba7befeb5014ee97c20741f9eb9a24e26ca0 Mon Sep 17 00:00:00 2001 From: Blake Watters Date: Sun, 31 Jul 2011 19:37:42 -0400 Subject: [PATCH] Renamed Polymorphic mapping to dynamic mapping after consulting with the community. Dropped abstract superclass in favor of a RKObjectMappingDefinition protocol. Caught missing cases with dynamic object mapping + targetObject. Updated docs and method signatures to reflect the updates. --- .../RKManagedObjectMappingOperation.m | 8 +- Code/ObjectMapping/RKObjectAbstractMapping.h | 19 ---- Code/ObjectMapping/RKObjectAbstractMapping.m | 25 ----- ...phicMapping.h => RKObjectDynamicMapping.h} | 37 ++++---- ...phicMapping.m => RKObjectDynamicMapping.m} | 40 ++++---- Code/ObjectMapping/RKObjectMapper.h | 8 +- Code/ObjectMapping/RKObjectMapper.m | 71 +++++++------- Code/ObjectMapping/RKObjectMapper_Private.h | 8 +- Code/ObjectMapping/RKObjectMapping.h | 16 ++-- Code/ObjectMapping/RKObjectMapping.m | 22 ++--- .../ObjectMapping/RKObjectMappingDefinition.h | 13 +++ Code/ObjectMapping/RKObjectMappingOperation.h | 4 +- Code/ObjectMapping/RKObjectMappingOperation.m | 36 +++---- Code/ObjectMapping/RKObjectMappingProvider.h | 15 ++- Code/ObjectMapping/RKObjectMappingProvider.m | 6 +- .../RKObjectRelationshipMapping.h | 11 ++- .../RKObjectRelationshipMapping.m | 8 +- Docs/Object Mapping.md | 52 +++++----- RestKit.xcodeproj/project.pbxproj | 62 ++++++------ .../JSON/{Polymorphic => Dynamic}/boy.json | 0 .../{Polymorphic => Dynamic}/friends.json | 0 .../JSON/{Polymorphic => Dynamic}/girl.json | 0 .../JSON/{Polymorphic => Dynamic}/mixed.json | 0 ...ppingModels.h => RKDynamicMappingModels.h} | 2 +- ...ppingModels.m => RKDynamicMappingModels.m} | 4 +- ...ingSpec.m => RKObjectDynamicMappingSpec.m} | 22 ++--- .../RKObjectMappingNextGenSpec.m | 94 ++++++++++++++++--- 27 files changed, 315 insertions(+), 268 deletions(-) delete mode 100644 Code/ObjectMapping/RKObjectAbstractMapping.h delete mode 100644 Code/ObjectMapping/RKObjectAbstractMapping.m rename Code/ObjectMapping/{RKObjectPolymorphicMapping.h => RKObjectDynamicMapping.h} (62%) rename Code/ObjectMapping/{RKObjectPolymorphicMapping.m => RKObjectDynamicMapping.m} (70%) create mode 100644 Code/ObjectMapping/RKObjectMappingDefinition.h rename Specs/Fixtures/JSON/{Polymorphic => Dynamic}/boy.json (100%) rename Specs/Fixtures/JSON/{Polymorphic => Dynamic}/friends.json (100%) rename Specs/Fixtures/JSON/{Polymorphic => Dynamic}/girl.json (100%) rename Specs/Fixtures/JSON/{Polymorphic => Dynamic}/mixed.json (100%) rename Specs/Models/{RKPolymorphicMappingModels.h => RKDynamicMappingModels.h} (91%) rename Specs/Models/{RKPolymorphicMappingModels.m => RKDynamicMappingModels.m} (76%) rename Specs/ObjectMapping/{RKObjectPolymorphicMappingSpec.m => RKObjectDynamicMappingSpec.m} (85%) diff --git a/Code/CoreData/RKManagedObjectMappingOperation.m b/Code/CoreData/RKManagedObjectMappingOperation.m index 0efa72ec..5bd13332 100644 --- a/Code/CoreData/RKManagedObjectMappingOperation.m +++ b/Code/CoreData/RKManagedObjectMappingOperation.m @@ -50,12 +50,12 @@ for (NSString* relationshipName in relationshipsAndPrimaryKeyAttributes) { NSString* primaryKeyAttribute = [relationshipsAndPrimaryKeyAttributes objectForKey:relationshipName]; RKObjectRelationshipMapping* relationshipMapping = [self.objectMapping mappingForKeyPath:relationshipName]; - RKObjectAbstractMapping* abstractMapping = relationshipMapping.mapping; - if (! [abstractMapping isKindOfClass:[RKObjectMapping class]]) { - RKLogWarning(@"Can only connect relationships for RKObjectMapping relationships. Found %@: Skipping...", NSStringFromClass([abstractMapping class])); + id mapping = relationshipMapping.mapping; + if (! [mapping isKindOfClass:[RKObjectMapping class]]) { + RKLogWarning(@"Can only connect relationships for RKObjectMapping relationships. Found %@: Skipping...", NSStringFromClass([mapping class])); continue; } - RKObjectMapping* objectMapping = (RKObjectMapping*)abstractMapping; + RKObjectMapping* objectMapping = (RKObjectMapping*)mapping; NSAssert(relationshipMapping, @"Unable to find relationship mapping '%@' to connect by primaryKey", relationshipName); NSAssert([relationshipMapping isKindOfClass:[RKObjectRelationshipMapping class]], @"Expected mapping for %@ to be a relationship mapping", relationshipName); NSAssert([relationshipMapping.mapping isKindOfClass:[RKManagedObjectMapping class]], @"Can only connect RKManagedObjectMapping relationships"); diff --git a/Code/ObjectMapping/RKObjectAbstractMapping.h b/Code/ObjectMapping/RKObjectAbstractMapping.h deleted file mode 100644 index 9569f486..00000000 --- a/Code/ObjectMapping/RKObjectAbstractMapping.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// RKObjectAbstractMapping.h -// RestKit -// -// Created by Blake Watters on 7/29/11. -// Copyright 2011 RestKit. All rights reserved. -// - -#import - -/** - An abstract superclass for RKObjectMapping and RKObjectPolymorphic mapping. - Provides type safety checks - */ -@interface RKObjectAbstractMapping : NSObject - -- (BOOL)forceCollectionMapping; -- (Class)objectClass; -@end diff --git a/Code/ObjectMapping/RKObjectAbstractMapping.m b/Code/ObjectMapping/RKObjectAbstractMapping.m deleted file mode 100644 index c08c97a7..00000000 --- a/Code/ObjectMapping/RKObjectAbstractMapping.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// RKObjectAbstractMapping.m -// RestKit -// -// Created by Blake Watters on 7/29/11. -// Copyright 2011 RestKit. All rights reserved. -// - -#import "RKObjectAbstractMapping.h" - -@implementation RKObjectAbstractMapping - -- (BOOL)forceCollectionMapping { - @throw [NSException exceptionWithName:NSInternalInconsistencyException - reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] - userInfo:nil]; -} - -- (Class)objectClass { - @throw [NSException exceptionWithName:NSInternalInconsistencyException - reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] - userInfo:nil]; -} - -@end diff --git a/Code/ObjectMapping/RKObjectPolymorphicMapping.h b/Code/ObjectMapping/RKObjectDynamicMapping.h similarity index 62% rename from Code/ObjectMapping/RKObjectPolymorphicMapping.h rename to Code/ObjectMapping/RKObjectDynamicMapping.h index 8cfd3aa7..29b6004e 100644 --- a/Code/ObjectMapping/RKObjectPolymorphicMapping.h +++ b/Code/ObjectMapping/RKObjectDynamicMapping.h @@ -6,13 +6,13 @@ // Copyright 2011 Two Toasters. All rights reserved. // -#import "RKObjectAbstractMapping.h" +#import "RKObjectMappingDefinition.h" #import "RKObjectMapping.h" /** Return the appropriate object mapping given a mappable data */ -@protocol RKObjectPolymorphicMappingDelegate +@protocol RKObjectDynamicMappingDelegate @required - (RKObjectMapping*)objectMappingForData:(id)data; @@ -20,19 +20,19 @@ @end #ifdef NS_BLOCKS_AVAILABLE -typedef RKObjectMapping*(^RKObjectPolymorphicMappingDelegateBlock)(id); +typedef RKObjectMapping*(^RKObjectDynamicMappingDelegateBlock)(id); #endif /** - Defines a polymorphic object mapping that determines the appropriate concrete + Defines a dynamic object mapping that determines the appropriate concrete object mapping to apply at mapping time. This allows you to map very similar payloads differently depending on the type of data contained therein. */ -@interface RKObjectPolymorphicMapping : RKObjectAbstractMapping { +@interface RKObjectDynamicMapping : NSObject { NSMutableArray* _matchers; - id _delegate; + id _delegate; #ifdef NS_BLOCKS_AVAILABLE - RKObjectPolymorphicMappingDelegateBlock _delegateBlock; + RKObjectDynamicMappingDelegateBlock _objectMappingForDataBlock; #endif BOOL _forceCollectionMapping; } @@ -43,14 +43,14 @@ typedef RKObjectMapping*(^RKObjectPolymorphicMappingDelegateBlock)(id); @see RKDynamicObjectMappingDelegate */ -@property (nonatomic, assign) id delegate; +@property (nonatomic, assign) id delegate; #ifdef NS_BLOCKS_AVAILABLE /** A block to invoke to determine the appropriate concrete object mapping to apply to the mappable data. */ -@property (nonatomic, copy) RKObjectPolymorphicMappingDelegateBlock delegateBlock; +@property (nonatomic, copy) RKObjectDynamicMappingDelegateBlock objectMappingForDataBlock; #endif /** @@ -61,35 +61,32 @@ typedef RKObjectMapping*(^RKObjectPolymorphicMappingDelegateBlock)(id); @property (nonatomic, assign) BOOL forceCollectionMapping; /** - Return a new auto-released polymorphic object mapping + Return a new auto-released dynamic object mapping */ -+ (RKObjectPolymorphicMapping*)polymorphicMapping; ++ (RKObjectDynamicMapping*)dynamicMapping; #if NS_BLOCKS_AVAILABLE /** - Return a new auto-released polymorphic object mapping after yielding it to the block for configuration + Return a new auto-released dynamic object mapping after yielding it to the block for configuration */ -+ (RKObjectPolymorphicMapping*)polymorphicMappingWithBlock:(void(^)(RKObjectPolymorphicMapping*))block; ++ (RKObjectDynamicMapping*)dynamicMappingWithBlock:(void(^)(RKObjectDynamicMapping*))block; #endif -//+ (id)mappingForClass:(Class)objectClass block:(void(^)(RKObjectMapping*))block { -// TODO: polymorphicMappingWithBlock - /** - Defines a polymorphic mapping rule stating that when the value of the key property matches the specified + Defines a dynamic mapping rule stating that when the value of the key property matches the specified value, the objectMapping should be used. For example, suppose that we have a JSON fragment for a person that we want to map differently based on the gender of the person. When the gender is 'male', we want to use the Boy class and when then the gender - is 'female' we want to use the Girl class. We might define our polymorphic mapping like so: + is 'female' we want to use the Girl class. We might define our dynamic mapping like so: - RKObjectPolymorphicMapping* mapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectdynamicMapping* mapping = [RKObjectdynamicMapping dynamicMapping]; [mapping setObjectMapping:boyMapping whenValueOfKeyPath:@"gender" isEqualTo:@"male"]; [mapping setObjectMapping:boyMapping whenValueOfKeyPath:@"gender" isEqualTo:@"female"]; */ -- (void)setObjectMapping:(RKObjectMapping*)objectMapping whenValueOfKeyPath:(NSString*)key isEqualTo:(id)value; +- (void)setObjectMapping:(RKObjectMapping*)objectMapping whenValueOfKeyPath:(NSString*)keyPath isEqualTo:(id)value; /** Invoked by the RKObjectMapper and RKObjectMappingOperation to determine the appropriate RKObjectMapping to use diff --git a/Code/ObjectMapping/RKObjectPolymorphicMapping.m b/Code/ObjectMapping/RKObjectDynamicMapping.m similarity index 70% rename from Code/ObjectMapping/RKObjectPolymorphicMapping.m rename to Code/ObjectMapping/RKObjectDynamicMapping.m index a47b4c67..4eb23aed 100644 --- a/Code/ObjectMapping/RKObjectPolymorphicMapping.m +++ b/Code/ObjectMapping/RKObjectDynamicMapping.m @@ -6,7 +6,7 @@ // Copyright 2011 Two Toasters. All rights reserved. // -#import "RKObjectPolymorphicMapping.h" +#import "RKObjectDynamicMapping.h" #import "../Support/RKLog.h" // Set Logging Component @@ -16,8 +16,8 @@ // Implemented in RKObjectMappingOperation BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue); -@interface RKObjectPolymorphicMappingMatcher : NSObject { - NSString* _key; +@interface RKObjectDynamicMappingMatcher : NSObject { + NSString* _keyPath; id _value; RKObjectMapping* _objectMapping; } @@ -29,14 +29,14 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue); - (NSString*)matchDescription; @end -@implementation RKObjectPolymorphicMappingMatcher +@implementation RKObjectDynamicMappingMatcher @synthesize objectMapping = _objectMapping; - (id)initWithKey:(NSString*)key value:(id)value objectMapping:(RKObjectMapping*)objectMapping { self = [super init]; if (self) { - _key = [key retain]; + _keyPath = [key retain]; _value = [value retain]; _objectMapping = [objectMapping retain]; } @@ -45,38 +45,38 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue); } - (void)dealloc { - [_key release]; + [_keyPath release]; [_value release]; [_objectMapping release]; [super dealloc]; } - (BOOL)isMatchForData:(id)data { - return RKObjectIsValueEqualToValue([data valueForKeyPath:_key], _value); + return RKObjectIsValueEqualToValue([data valueForKeyPath:_keyPath], _value); } - (NSString*)matchDescription { - return [NSString stringWithFormat:@"%@ == %@", _key, _value]; + return [NSString stringWithFormat:@"%@ == %@", _keyPath, _value]; } @end /////////////////////////////////////////////////////////////////////////////////////////////////// -@implementation RKObjectPolymorphicMapping +@implementation RKObjectDynamicMapping @synthesize delegate = _delegate; -@synthesize delegateBlock = _delegateBlock; +@synthesize objectMappingForDataBlock = _objectMappingForDataBlock; @synthesize forceCollectionMapping = _forceCollectionMapping; -+ (RKObjectPolymorphicMapping*)polymorphicMapping { ++ (RKObjectDynamicMapping*)dynamicMapping { return [[self new] autorelease]; } #if NS_BLOCKS_AVAILABLE -+ (RKObjectPolymorphicMapping*)polymorphicMappingWithBlock:(void(^)(RKObjectPolymorphicMapping*))block { - RKObjectPolymorphicMapping* mapping = [self polymorphicMapping]; ++ (RKObjectDynamicMapping*)dynamicMappingWithBlock:(void(^)(RKObjectDynamicMapping*))block { + RKObjectDynamicMapping* mapping = [self dynamicMapping]; block(mapping); return mapping; } @@ -97,9 +97,9 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue); [super dealloc]; } -- (void)setObjectMapping:(RKObjectMapping*)objectMapping whenValueOfKeyPath:(NSString*)key isEqualTo:(id)value { - RKLogDebug(@"Adding dynamic object mapping for key '%@' with value '%@' to destination class: %@", key, value, NSStringFromClass(objectMapping.objectClass)); - RKObjectPolymorphicMappingMatcher* matcher = [[RKObjectPolymorphicMappingMatcher alloc] initWithKey:key value:value objectMapping:objectMapping]; +- (void)setObjectMapping:(RKObjectMapping*)objectMapping whenValueOfKeyPath:(NSString*)keyPath isEqualTo:(id)value { + RKLogDebug(@"Adding dynamic object mapping for key '%@' with value '%@' to destination class: %@", keyPath, value, NSStringFromClass(objectMapping.objectClass)); + RKObjectDynamicMappingMatcher* matcher = [[RKObjectDynamicMappingMatcher alloc] initWithKey:keyPath value:value objectMapping:objectMapping]; [_matchers addObject:matcher]; [matcher release]; } @@ -111,7 +111,7 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue); RKLogTrace(@"Performing dynamic object mapping for mappable data: %@", data); // Consult the declarative matchers first - for (RKObjectPolymorphicMappingMatcher* matcher in _matchers) { + for (RKObjectDynamicMappingMatcher* matcher in _matchers) { if ([matcher isMatchForData:data]) { RKLogTrace(@"Found declarative match for data: %@.", [matcher matchDescription]); return matcher.objectMapping; @@ -127,10 +127,10 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue); } } - if (self.delegateBlock) { - mapping = self.delegateBlock(data); + if (self.objectMappingForDataBlock) { + mapping = self.objectMappingForDataBlock(data); if (mapping) { - RKLogTrace(@"Found dynamic delegateBlock match. DelegateBlock = %@", self.delegateBlock); + RKLogTrace(@"Found dynamic delegateBlock match. objectMappingForDataBlock = %@", self.objectMappingForDataBlock); } } diff --git a/Code/ObjectMapping/RKObjectMapper.h b/Code/ObjectMapping/RKObjectMapper.h index afc2a214..5788fc5c 100644 --- a/Code/ObjectMapping/RKObjectMapper.h +++ b/Code/ObjectMapping/RKObjectMapper.h @@ -26,12 +26,12 @@ - (void)objectMapperWillBeginMapping:(RKObjectMapper*)objectMapper; - (void)objectMapperDidFinishMapping:(RKObjectMapper*)objectMapper; - (void)objectMapper:(RKObjectMapper*)objectMapper didAddError:(NSError*)error; -- (void)objectMapper:(RKObjectMapper*)objectMapper didFindMappableObject:(id)object atKeyPath:(NSString*)keyPath withMapping:(RKObjectAbstractMapping*)mapping; +- (void)objectMapper:(RKObjectMapper*)objectMapper didFindMappableObject:(id)object atKeyPath:(NSString*)keyPath withMapping:(id)mapping; - (void)objectMapper:(RKObjectMapper*)objectMapper didNotFindMappableObjectAtKeyPath:(NSString*)keyPath; -- (void)objectMapper:(RKObjectMapper*)objectMapper willMapFromObject:(id)sourceObject toObject:(id)destinationObject atKeyPath:(NSString*)keyPath usingMapping:(RKObjectAbstractMapping*)objectMapping; -- (void)objectMapper:(RKObjectMapper*)objectMapper didMapFromObject:(id)sourceObject toObject:(id)destinationObject atKeyPath:(NSString*)keyPath usingMapping:(RKObjectAbstractMapping*)objectMapping; -- (void)objectMapper:(RKObjectMapper*)objectMapper didFailMappingFromObject:(id)sourceObject toObject:(id)destinationObject withError:(NSError*)error atKeyPath:(NSString*)keyPath usingMapping:(RKObjectAbstractMapping*)objectMapping; +- (void)objectMapper:(RKObjectMapper*)objectMapper willMapFromObject:(id)sourceObject toObject:(id)destinationObject atKeyPath:(NSString*)keyPath usingMapping:(id)objectMapping; +- (void)objectMapper:(RKObjectMapper*)objectMapper didMapFromObject:(id)sourceObject toObject:(id)destinationObject atKeyPath:(NSString*)keyPath usingMapping:(id)objectMapping; +- (void)objectMapper:(RKObjectMapper*)objectMapper didFailMappingFromObject:(id)sourceObject toObject:(id)destinationObject withError:(NSError*)error atKeyPath:(NSString*)keyPath usingMapping:(id)objectMapping; @end @interface RKObjectMapper : NSObject { diff --git a/Code/ObjectMapping/RKObjectMapper.m b/Code/ObjectMapping/RKObjectMapper.m index f9a60f1d..26a35345 100644 --- a/Code/ObjectMapping/RKObjectMapper.m +++ b/Code/ObjectMapping/RKObjectMapper.m @@ -97,14 +97,21 @@ #pragma mark - Mapping Primitives -- (id)mapObject:(id)mappableObject atKeyPath:(NSString*)keyPath usingMapping:(RKObjectAbstractMapping*)objectMapping { +- (id)mapObject:(id)mappableObject atKeyPath:(NSString*)keyPath usingMapping:(id)mapping { NSAssert([mappableObject respondsToSelector:@selector(setValue:forKeyPath:)], @"Expected self.object to be KVC compliant"); id destinationObject = nil; if (self.targetObject) { - // If we find a mapping for this type and keyPath, map the entire dictionary to the target object destinationObject = self.targetObject; - if (objectMapping && NO == [[self.targetObject class] isSubclassOfClass:objectMapping.objectClass]) { + RKObjectMapping* objectMapping = nil; + if ([mapping isKindOfClass:[RKObjectDynamicMapping class]]) { + objectMapping = [(RKObjectDynamicMapping*)mapping objectMappingForDictionary:mappableObject]; + } else if ([mapping isKindOfClass:[RKObjectMapping class]]) { + objectMapping = (RKObjectMapping*)mapping; + } else { + NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([mapping class])); + } + if (NO == [[self.targetObject class] isSubclassOfClass:objectMapping.objectClass]) { NSString* errorMessage = [NSString stringWithFormat: @"Expected an object mapping for class of type '%@', provider returned one for '%@'", NSStringFromClass([self.targetObject class]), NSStringFromClass(objectMapping.objectClass)]; @@ -112,11 +119,11 @@ return nil; } } else { - destinationObject = [self objectWithMapping:objectMapping andData:mappableObject]; + destinationObject = [self objectWithMapping:mapping andData:mappableObject]; } - if (objectMapping && destinationObject) { - BOOL success = [self mapFromObject:mappableObject toObject:destinationObject atKeyPath:keyPath usingMapping:objectMapping]; + if (mapping && destinationObject) { + BOOL success = [self mapFromObject:mappableObject toObject:destinationObject atKeyPath:keyPath usingMapping:mapping]; if (success) { return destinationObject; } @@ -129,7 +136,7 @@ return nil; } -- (NSArray*)mapCollection:(NSArray*)mappableObjects atKeyPath:(NSString*)keyPath usingMapping:(RKObjectAbstractMapping*)mapping { +- (NSArray*)mapCollection:(NSArray*)mappableObjects atKeyPath:(NSString*)keyPath usingMapping:(id)mapping { NSAssert(mappableObjects != nil, @"Cannot map without an collection of mappable objects"); NSAssert(mapping != nil, @"Cannot map without a mapping to consult"); @@ -174,7 +181,7 @@ } // The workhorse of this entire process. Emits object loading operations -- (BOOL)mapFromObject:(id)mappableObject toObject:(id)destinationObject atKeyPath:keyPath usingMapping:(RKObjectAbstractMapping*)mapping { +- (BOOL)mapFromObject:(id)mappableObject toObject:(id)destinationObject atKeyPath:keyPath usingMapping:(id)mapping { NSAssert(destinationObject != nil, @"Cannot map without a target object to assign the results to"); NSAssert(mappableObject != nil, @"Cannot map without a collection of attributes"); NSAssert(mapping != nil, @"Cannot map without an mapping"); @@ -204,6 +211,27 @@ return success; } +- (id)objectWithMapping:(id)mapping andData:(id)mappableData { + NSAssert([mapping conformsToProtocol:@protocol(RKObjectMappingDefinition)], @"Expected an object implementing RKObjectMappingDefinition"); + RKObjectMapping* objectMapping = nil; + if ([mapping isKindOfClass:[RKObjectDynamicMapping class]]) { + objectMapping = [(RKObjectDynamicMapping*)mapping objectMappingForDictionary:mappableData]; + if (! objectMapping) { + RKLogDebug(@"Mapping %@ declined mapping for data %@: returned nil objectMapping", mapping, mappableData); + } + } else if ([mapping isKindOfClass:[RKObjectMapping class]]) { + objectMapping = (RKObjectMapping*)mapping; + } else { + NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([mapping class])); + } + + if (objectMapping) { + return [objectMapping mappableObjectForData:mappableData]; + } + + return nil; +} + // Primary entry point for the mapper. - (RKObjectMappingResult*)performMapping { NSAssert(self.sourceObject != nil, @"Cannot perform object mapping without a source object to map from"); @@ -218,8 +246,8 @@ // Perform the mapping BOOL foundMappable = NO; NSMutableDictionary* results = [NSMutableDictionary dictionary]; - NSDictionary* keyPathsAndObjectMappings = [self.mappingProvider mappingsByKeyPath]; - for (NSString* keyPath in keyPathsAndObjectMappings) { + NSDictionary* mappingsByKeyPath = [self.mappingProvider mappingsByKeyPath]; + for (NSString* keyPath in mappingsByKeyPath) { id mappingResult; id mappableValue; @@ -244,7 +272,7 @@ // Found something to map foundMappable = YES; - RKObjectAbstractMapping* mapping = [keyPathsAndObjectMappings objectForKey:keyPath]; + id mapping = [mappingsByKeyPath objectForKey:keyPath]; if ([self.delegate respondsToSelector:@selector(objectMapper:didFindMappableObject:atKeyPath:withMapping:)]) { [self.delegate objectMapper:self didFindMappableObject:mappableValue atKeyPath:keyPath withMapping:mapping]; } @@ -278,25 +306,4 @@ return [RKObjectMappingResult mappingResultWithDictionary:results]; } -- (id)objectWithMapping:(RKObjectAbstractMapping*)abstractMapping andData:(id)mappableData { - NSAssert(! [abstractMapping isMemberOfClass:[RKObjectAbstractMapping class]], @"Expected a concrete subclass of RKObjectAbstractMapping"); - RKObjectMapping* objectMapping = nil; - if ([abstractMapping isKindOfClass:[RKObjectPolymorphicMapping class]]) { - objectMapping = [(RKObjectPolymorphicMapping*)abstractMapping objectMappingForDictionary:mappableData]; - if (! objectMapping) { - RKLogDebug(@"Mapping %@ declined mapping for data %@: returned nil objectMapping", abstractMapping, mappableData); - } - } else if ([abstractMapping isKindOfClass:[RKObjectMapping class]]) { - objectMapping = (RKObjectMapping*)abstractMapping; - } else { - NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([abstractMapping class])); - } - - if (objectMapping) { - return [objectMapping mappableObjectForData:mappableData]; - } - - return nil; -} - @end diff --git a/Code/ObjectMapping/RKObjectMapper_Private.h b/Code/ObjectMapping/RKObjectMapper_Private.h index 002390b3..bcfa8f98 100644 --- a/Code/ObjectMapping/RKObjectMapper_Private.h +++ b/Code/ObjectMapping/RKObjectMapper_Private.h @@ -8,9 +8,9 @@ @interface RKObjectMapper (Private) -- (id)mapObject:(id)mappableObject atKeyPath:keyPath usingMapping:(RKObjectAbstractMapping*)mapping; -- (NSArray*)mapCollection:(NSArray*)mappableObjects atKeyPath:(NSString*)keyPath usingMapping:(RKObjectAbstractMapping*)mapping; -- (BOOL)mapFromObject:(id)mappableObject toObject:(id)destinationObject atKeyPath:keyPath usingMapping:(RKObjectAbstractMapping*)mapping; -- (id)objectWithMapping:(RKObjectAbstractMapping*)objectMapping andData:(id)mappableData; +- (id)mapObject:(id)mappableObject atKeyPath:keyPath usingMapping:(id)mapping; +- (NSArray*)mapCollection:(NSArray*)mappableObjects atKeyPath:(NSString*)keyPath usingMapping:(id)mapping; +- (BOOL)mapFromObject:(id)mappableObject toObject:(id)destinationObject atKeyPath:keyPath usingMapping:(id)mapping; +- (id)objectWithMapping:(id)objectMapping andData:(id)mappableData; @end diff --git a/Code/ObjectMapping/RKObjectMapping.h b/Code/ObjectMapping/RKObjectMapping.h index 49a0df2c..4d4cf77b 100644 --- a/Code/ObjectMapping/RKObjectMapping.h +++ b/Code/ObjectMapping/RKObjectMapping.h @@ -7,7 +7,7 @@ // #import -#import "RKObjectAbstractMapping.h" +#import "RKObjectMappingDefinition.h" #import "RKObjectAttributeMapping.h" #import "RKObjectRelationshipMapping.h" @@ -31,7 +31,7 @@ relationship. Relationships are processed using an object mapping as well. Instances of RKObjectMapping are used to configure RKObjectMappingOperation instances, which actually perform the mapping work. Both object loading and serialization are defined in terms of object mappings. */ -@interface RKObjectMapping : RKObjectAbstractMapping { +@interface RKObjectMapping : NSObject { Class _objectClass; NSMutableArray* _mappings; NSMutableArray* _dateFormatStrings; @@ -248,9 +248,9 @@ relationship. Relationships are processed using an object mapping as well. @param relationshipKey A key-value coding key corresponding to a value in the mappable source object and a property on the destination class that have the same name. - @param objectOrPolymorphicMapping An RKObjectMapping or RKObjectPolymorphic mapping to apply when mapping the relationship + @param objectOrDynamicMapping An RKObjectMapping or RKObjectDynamic mapping to apply when mapping the relationship */ -- (void)mapRelationship:(NSString*)relationshipKey withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping; +- (void)mapRelationship:(NSString*)relationshipKey withMapping:(id)objectOrDynamicMapping; /** Syntactic sugar to improve readability when defining a relationship mapping. Implies that the mapping @@ -258,7 +258,7 @@ relationship. Relationships are processed using an object mapping as well. @see mapRelationship:withObjectMapping: */ -- (void)hasMany:(NSString*)keyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping; +- (void)hasMany:(NSString*)keyPath withMapping:(id)objectOrDynamicMapping; /** Syntactic sugar to improve readability when defining a relationship mapping. Implies that the mapping @@ -266,7 +266,7 @@ relationship. Relationships are processed using an object mapping as well. @see mapRelationship:withObjectMapping: */ -- (void)hasOne:(NSString*)keyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping; +- (void)hasOne:(NSString*)keyPath withMapping:(id)objectOrDynamicMapping; /** Instantiate and add an RKObjectAttributeMapping instance targeting a keyPath within the mappable @@ -307,7 +307,7 @@ relationship. Relationships are processed using an object mapping as well. @param objectMapping An object mapping to use when processing the nested objects @see RKObjectRelationshipMapping */ -- (void)mapKeyPath:(NSString *)sourceKeyPath toRelationship:(NSString*)destinationRelationship withMapping:(RKObjectAbstractMapping *)objectOrPolymorphicMapping; +- (void)mapKeyPath:(NSString *)sourceKeyPath toRelationship:(NSString*)destinationRelationship withMapping:(id)objectOrDynamicMapping; /** Instantiate and add an RKObjectRelationshipMapping instance targeting a keyPath within the mappable @@ -322,7 +322,7 @@ relationship. Relationships are processed using an object mapping as well. @see mapKeyPath:toRelationship:withObjectMapping: */ -- (void)mapKeyPath:(NSString *)relationshipKeyPath toRelationship:(NSString*)keyPath withMapping:(RKObjectAbstractMapping *)objectOrPolymorphicMapping serialize:(BOOL)serialize; +- (void)mapKeyPath:(NSString *)relationshipKeyPath toRelationship:(NSString*)keyPath withMapping:(id)objectOrDynamicMapping serialize:(BOOL)serialize; /** Quickly define a group of attribute mappings using alternating keyPath and attribute names. You must provide diff --git a/Code/ObjectMapping/RKObjectMapping.m b/Code/ObjectMapping/RKObjectMapping.m index ee534d7e..98f6a125 100644 --- a/Code/ObjectMapping/RKObjectMapping.m +++ b/Code/ObjectMapping/RKObjectMapping.m @@ -140,17 +140,17 @@ NSString* const RKObjectMappingNestingAttributeKeyName = @")objectOrDynamicMapping serialize:(BOOL)serialize { + RKObjectRelationshipMapping* mapping = [RKObjectRelationshipMapping mappingFromKeyPath:relationshipKeyPath toKeyPath:keyPath withMapping:objectOrDynamicMapping reversible:serialize]; [self addRelationshipMapping:mapping]; } -- (void)mapKeyPath:(NSString *)relationshipKeyPath toRelationship:(NSString*)keyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping { - [self mapKeyPath:relationshipKeyPath toRelationship:keyPath withMapping:objectOrPolymorphicMapping serialize:YES]; +- (void)mapKeyPath:(NSString *)relationshipKeyPath toRelationship:(NSString*)keyPath withMapping:(id)objectOrDynamicMapping { + [self mapKeyPath:relationshipKeyPath toRelationship:keyPath withMapping:objectOrDynamicMapping serialize:YES]; } -- (void)mapRelationship:(NSString*)relationshipKeyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping { - [self mapKeyPath:relationshipKeyPath toRelationship:relationshipKeyPath withMapping:objectOrPolymorphicMapping]; +- (void)mapRelationship:(NSString*)relationshipKeyPath withMapping:(id)objectOrDynamicMapping { + [self mapKeyPath:relationshipKeyPath toRelationship:relationshipKeyPath withMapping:objectOrDynamicMapping]; } - (void)mapKeyPath:(NSString*)sourceKeyPath toAttribute:(NSString*)destinationKeyPath { @@ -158,12 +158,12 @@ NSString* const RKObjectMappingNestingAttributeKeyName = @")objectOrDynamicMapping { + [self mapRelationship:keyPath withMapping:objectOrDynamicMapping]; } -- (void)hasOne:(NSString*)keyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping { - [self mapRelationship:keyPath withMapping:objectOrPolymorphicMapping]; +- (void)hasOne:(NSString*)keyPath withMapping:(id)objectOrDynamicMapping { + [self mapRelationship:keyPath withMapping:objectOrDynamicMapping]; } - (void)removeAllMappings { @@ -191,7 +191,7 @@ NSString* const RKObjectMappingNestingAttributeKeyName = @" mapping = relationshipMapping.mapping; if (! [mapping isKindOfClass:[RKObjectMapping class]]) { RKLogWarning(@"Unable to generate inverse mapping for relationship '%@': %@ relationships cannot be inversed.", relationshipMapping.sourceKeyPath, NSStringFromClass([mapping class])); continue; diff --git a/Code/ObjectMapping/RKObjectMappingDefinition.h b/Code/ObjectMapping/RKObjectMappingDefinition.h new file mode 100644 index 00000000..b63e6af5 --- /dev/null +++ b/Code/ObjectMapping/RKObjectMappingDefinition.h @@ -0,0 +1,13 @@ +// +// RKObjectMappingDefinition.h +// RestKit +// +// Created by Blake Watters on 7/31/11. +// Copyright 2011 RestKit. All rights reserved. +// + +@protocol RKObjectMappingDefinition + +- (BOOL)forceCollectionMapping; + +@end \ No newline at end of file diff --git a/Code/ObjectMapping/RKObjectMappingOperation.h b/Code/ObjectMapping/RKObjectMappingOperation.h index 2fcbbb5c..949c7c43 100644 --- a/Code/ObjectMapping/RKObjectMappingOperation.h +++ b/Code/ObjectMapping/RKObjectMappingOperation.h @@ -59,12 +59,12 @@ Create a new mapping operation configured to transform the object representation in a source object to a new destination object according to an object mapping definition */ -+ (RKObjectMappingOperation*)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKObjectAbstractMapping*)mapping; ++ (RKObjectMappingOperation*)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(id)mapping; /** Initialize a mapping operation for an object and set of data at a particular key path with an object mapping definition */ -- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(RKObjectAbstractMapping*)mapping; +- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(id)mapping; /** Process all mappable values from the mappable dictionary and assign them to the target object diff --git a/Code/ObjectMapping/RKObjectMappingOperation.m b/Code/ObjectMapping/RKObjectMappingOperation.m index bb24de6c..eddfa778 100644 --- a/Code/ObjectMapping/RKObjectMappingOperation.m +++ b/Code/ObjectMapping/RKObjectMappingOperation.m @@ -58,11 +58,11 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { @synthesize objectMapping = _objectMapping; @synthesize delegate = _delegate; -+ (RKObjectMappingOperation*)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKObjectAbstractMapping*)objectMapping { ++ (RKObjectMappingOperation*)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(id)objectMapping { return [[[self alloc] initWithSourceObject:sourceObject destinationObject:destinationObject mapping:objectMapping] autorelease]; } -- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(RKObjectAbstractMapping*)objectMapping { +- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(id)objectMapping { NSAssert(sourceObject != nil, @"Cannot perform a mapping operation without a sourceObject object"); NSAssert(destinationObject != nil, @"Cannot perform a mapping operation without a destinationObject"); NSAssert(objectMapping != nil, @"Cannot perform a mapping operation without a mapping"); @@ -72,9 +72,9 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { _sourceObject = [sourceObject retain]; _destinationObject = [destinationObject retain]; - if ([objectMapping isKindOfClass:[RKObjectPolymorphicMapping class]]) { - _objectMapping = [[(RKObjectPolymorphicMapping*)objectMapping objectMappingForDictionary:_sourceObject] retain]; - RKLogDebug(@"RKObjectMappingOperation was initialized with a polymorphic mapping. Determined concrete mapping = %@", _objectMapping); + if ([objectMapping isKindOfClass:[RKObjectDynamicMapping class]]) { + _objectMapping = [[(RKObjectDynamicMapping*)objectMapping objectMappingForDictionary:_sourceObject] retain]; + RKLogDebug(@"RKObjectMappingOperation was initialized with a dynamic mapping. Determined concrete mapping = %@", _objectMapping); } else if ([objectMapping isKindOfClass:[RKObjectMapping class]]) { _objectMapping = (RKObjectMapping*)[objectMapping retain]; } @@ -352,18 +352,18 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { destinationObject = [NSMutableArray arrayWithCapacity:[value count]]; for (id nestedObject in value) { - RKObjectAbstractMapping* abstractMapping = relationshipMapping.mapping; + id mapping = relationshipMapping.mapping; RKObjectMapping* objectMapping = nil; - if ([abstractMapping isKindOfClass:[RKObjectPolymorphicMapping class]]) { - objectMapping = [(RKObjectPolymorphicMapping*)abstractMapping objectMappingForDictionary:nestedObject]; + if ([mapping isKindOfClass:[RKObjectDynamicMapping class]]) { + objectMapping = [(RKObjectDynamicMapping*)mapping objectMappingForDictionary:nestedObject]; if (! objectMapping) { - RKLogDebug(@"Mapping %@ declined mapping for data %@: returned nil objectMapping", abstractMapping, nestedObject); + RKLogDebug(@"Mapping %@ declined mapping for data %@: returned nil objectMapping", mapping, nestedObject); continue; } - } else if ([abstractMapping isKindOfClass:[RKObjectMapping class]]) { - objectMapping = (RKObjectMapping*)abstractMapping; + } else if ([mapping isKindOfClass:[RKObjectMapping class]]) { + objectMapping = (RKObjectMapping*)mapping; } else { - NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([abstractMapping class])); + NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([mapping class])); } id mappedObject = [objectMapping mappableObjectForData:nestedObject]; if ([self mapNestedObject:nestedObject toObject:mappedObject withRealtionshipMapping:relationshipMapping]) { @@ -380,14 +380,14 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) { // One to one relationship RKLogDebug(@"Mapping one to one relationship value at keyPath '%@' to '%@'", relationshipMapping.sourceKeyPath, relationshipMapping.destinationKeyPath); - RKObjectAbstractMapping* abstractMapping = relationshipMapping.mapping; + id mapping = relationshipMapping.mapping; RKObjectMapping* objectMapping = nil; - if ([abstractMapping isKindOfClass:[RKObjectPolymorphicMapping class]]) { - objectMapping = [(RKObjectPolymorphicMapping*)abstractMapping objectMappingForDictionary:value]; - } else if ([abstractMapping isKindOfClass:[RKObjectMapping class]]) { - objectMapping = (RKObjectMapping*)abstractMapping; + if ([mapping isKindOfClass:[RKObjectDynamicMapping class]]) { + objectMapping = [(RKObjectDynamicMapping*)mapping objectMappingForDictionary:value]; + } else if ([mapping isKindOfClass:[RKObjectMapping class]]) { + objectMapping = (RKObjectMapping*)mapping; } - NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([abstractMapping class])); + NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([mapping class])); destinationObject = [objectMapping mappableObjectForData:value]; if ([self mapNestedObject:value toObject:destinationObject withRealtionshipMapping:relationshipMapping]) { appliedMappings = YES; diff --git a/Code/ObjectMapping/RKObjectMappingProvider.h b/Code/ObjectMapping/RKObjectMappingProvider.h index f1b56f55..3623252a 100644 --- a/Code/ObjectMapping/RKObjectMappingProvider.h +++ b/Code/ObjectMapping/RKObjectMappingProvider.h @@ -7,7 +7,7 @@ // #import "RKObjectMapping.h" -#import "RKObjectPolymorphicMapping.h" +#import "RKObjectDynamicMapping.h" /** Responsible for providing object mappings to an instance of the object mapper @@ -19,21 +19,26 @@ NSMutableDictionary* _serializationMappings; } +/** + Returns a new auto-released mapping provider + */ ++ (RKObjectMappingProvider*)mappingProvider; + /** Instructs the mapping provider to use the mapping provided when it encounters content at the specified key path */ -- (void)setMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping forKeyPath:(NSString*)keyPath; +- (void)setMapping:(id)objectOrDynamicMapping forKeyPath:(NSString*)keyPath; /** - Returns the RKObjectMapping or RKObjectPolymorphic mapping configured for use + Returns the RKObjectMapping or RKObjectDynamic mapping configured for use when mappable content is encountered at keyPath */ -- (RKObjectAbstractMapping*)mappingForKeyPath:(NSString*)keyPath; +- (id)mappingForKeyPath:(NSString*)keyPath; /** Returns a dictionary where the keys are mappable keyPaths and the values are the RKObjectMapping - or RKObjectPolymorphic mappings to use for mappable data that appears at the keyPath. + or RKObjectDynamic mappings to use for mappable data that appears at the keyPath. */ - (NSDictionary*)mappingsByKeyPath; diff --git a/Code/ObjectMapping/RKObjectMappingProvider.m b/Code/ObjectMapping/RKObjectMappingProvider.m index 3b8579dc..e5f2ca26 100644 --- a/Code/ObjectMapping/RKObjectMappingProvider.m +++ b/Code/ObjectMapping/RKObjectMappingProvider.m @@ -10,6 +10,10 @@ @implementation RKObjectMappingProvider ++ (RKObjectMappingProvider*)mappingProvider { + return [[self new] autorelease]; +} + - (id)init { if ((self = [super init])) { _objectMappings = [NSMutableArray new]; @@ -30,7 +34,7 @@ [_mappingsByKeyPath setValue:mapping forKey:keyPath]; } -- (RKObjectAbstractMapping*)mappingForKeyPath:(NSString*)keyPath { +- (id)mappingForKeyPath:(NSString*)keyPath { return [_mappingsByKeyPath objectForKey:keyPath]; } diff --git a/Code/ObjectMapping/RKObjectRelationshipMapping.h b/Code/ObjectMapping/RKObjectRelationshipMapping.h index af69cb15..1048c884 100644 --- a/Code/ObjectMapping/RKObjectRelationshipMapping.h +++ b/Code/ObjectMapping/RKObjectRelationshipMapping.h @@ -8,19 +8,20 @@ #import #import "RKObjectAttributeMapping.h" +#import "RKObjectMappingDefinition.h" -@class RKObjectAbstractMapping; +@class RKObjectmapping; @interface RKObjectRelationshipMapping : RKObjectAttributeMapping { - RKObjectAbstractMapping* _mapping; + id _mapping; BOOL _reversible; } -@property (nonatomic, retain) RKObjectAbstractMapping* mapping; +@property (nonatomic, retain) id mapping; @property (nonatomic, assign) BOOL reversible; -+ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping; ++ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(id)objectOrDynamicMapping; -+ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping reversible:(BOOL)reversible; ++ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(id)objectOrDynamicMapping reversible:(BOOL)reversible; @end diff --git a/Code/ObjectMapping/RKObjectRelationshipMapping.m b/Code/ObjectMapping/RKObjectRelationshipMapping.m index 31701181..1093c3b2 100644 --- a/Code/ObjectMapping/RKObjectRelationshipMapping.m +++ b/Code/ObjectMapping/RKObjectRelationshipMapping.m @@ -13,15 +13,15 @@ @synthesize mapping = _mapping; @synthesize reversible = _reversible; -+ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping reversible:(BOOL)reversible { ++ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(id)objectOrDynamicMapping reversible:(BOOL)reversible { RKObjectRelationshipMapping* relationshipMapping = (RKObjectRelationshipMapping*) [self mappingFromKeyPath:sourceKeyPath toKeyPath:destinationKeyPath]; relationshipMapping.reversible = reversible; - relationshipMapping.mapping = objectOrPolymorphicMapping; + relationshipMapping.mapping = objectOrDynamicMapping; return relationshipMapping; } -+ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(RKObjectAbstractMapping*)objectOrPolymorphicMapping { - return [self mappingFromKeyPath:sourceKeyPath toKeyPath:destinationKeyPath withMapping:objectOrPolymorphicMapping reversible:YES]; ++ (RKObjectRelationshipMapping*)mappingFromKeyPath:(NSString*)sourceKeyPath toKeyPath:(NSString*)destinationKeyPath withMapping:(id)objectOrDynamicMapping { + return [self mappingFromKeyPath:sourceKeyPath toKeyPath:destinationKeyPath withMapping:objectOrDynamicMapping reversible:YES]; } - (id)copyWithZone:(NSZone *)zone { diff --git a/Docs/Object Mapping.md b/Docs/Object Mapping.md index cd480ac9..1c16c976 100644 --- a/Docs/Object Mapping.md +++ b/Docs/Object Mapping.md @@ -580,30 +580,30 @@ mapping.forceCollectionMapping = YES; [mapping mapFromKeyPath:@"(username).email" toAttribute:"email"]; [mapping mapFromKeyPath:@"(username).favoriteAnimal" toAttribute:"favoriteAnimal"]; ``` -### Polymorphic Object Mapping +### Dynamic Object Mapping Thus far we have examined clear-cut cases where the appropriate object mapping can be determined either by consulting the key path or by the developer directly providing the mapping. Sometimes it is desirable to dynamically determine the appropriate object mapping to use at mapping time. Perhaps we have a collection of objects with identical attribute names, but we wish to represent them differently. Or maybe we are loading a collection of objects that are not KVC compliant, but contain a mixture -of types that we would like to model. RestKit supports such use cases via the RKObjectPolymorphicMapping class. -RKObjectPolymorphicMapping is a sibling class to RKObjectMapping and can be added to instances of RKObjectMappingProvider and -used to configure RKObjectMappingOperation instances. RKObjectPolymorphicMapping allows you to hook into the mapping process +of types that we would like to model. RestKit supports such use cases via the RKObjectDynamicMapping class. +RKObjectDynamicMapping is a sibling class to RKObjectMapping and can be added to instances of RKObjectMappingProvider and +used to configure RKObjectMappingOperation instances. RKObjectDynamicMapping allows you to hook into the mapping process and determine an appropriate RKObjectMapping to use on a per-object basis. -When RestKit is performing a mapping operation and the current mapping being applied is an RKObjectPolymorphicMapping instance, -the polymorphic mapping will be sent the `objectMappingForDictionary:` message with the NSDictionary that is currently being -mapped. The polymorphic mapping is responsible for introspecting the contents of the dictionary and returning an RKObjectMapping +When RestKit is performing a mapping operation and the current mapping being applied is an RKObjectDynamicMapping instance, +the dynamic mapping will be sent the `objectMappingForDictionary:` message with the NSDictionary that is currently being +mapped. The dynamic mapping is responsible for introspecting the contents of the dictionary and returning an RKObjectMapping instance that can be used to map the data into a concrete object. There are three ways in which the determination of the appropriate object mapping can be made: -1. Via a declarative matcher on an attribute within the mappable data. If your polymorphic data contains an attribute that can -be used to infer the appropriate object type, then you are in luck -- RestKit can handle the polymorphic mapping via simple +1. Via a declarative matcher on an attribute within the mappable data. If your dynamic data contains an attribute that can +be used to infer the appropriate object type, then you are in luck -- RestKit can handle the dynamic mapping via simple configuration. 2. Via a delegate callback. If your data requires some special analysis or you want to dynamically construct an object mapping -to handle the data, you can assign a delegate to the RKObjectPolymorphicMapping and you will be called back to perform whatever +to handle the data, you can assign a delegate to the RKObjectDynamicMapping and you will be called back to perform whatever logic you need to implement the object mapping lookup/construction. -3. Via a delegate block invocation. Similar to the delegate configuration, you can assign a delegateBlock to the RKObjectPolymorphicMapping that will be invoked to determine the appropriate RKObjectMapping to use for the mappable data. +3. Via a delegate block invocation. Similar to the delegate configuration, you can assign a delegateBlock to the RKObjectDynamicMapping that will be invoked to determine the appropriate RKObjectMapping to use for the mappable data. To illustrate these concepts, let's consider the following JSON fragment: @@ -634,7 +634,7 @@ To illustrate these concepts, let's consider the following JSON fragment: In this JSON we have a dictionary containing an array of people at the "people" key path. We want to map each of the people within that collection into different classes: `Boy` and `Girl`. Our meaningful attributes are the name and -the friends, which is itself a polymorphic collection of people. The `type` attribute will be used to determine what +the friends, which is itself a dynamic collection of people. The `type` attribute will be used to determine what the appropriate destination mapping and class will be. Let's set it up: ```objc @@ -643,19 +643,19 @@ RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[Boy class]]; [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; -RKObjectPolymorphicMapping* polymorphicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; -[boyMapping mapKeyPath:@"friends" toRelationship:@"friends" withMapping:polymorphicMapping]; -[girlMapping mapKeyPath:@"friends" toRelationship:@"friends" withMapping:polymorphicMapping]; +RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; +[boyMapping mapKeyPath:@"friends" toRelationship:@"friends" withMapping:dynamicMapping]; +[girlMapping mapKeyPath:@"friends" toRelationship:@"friends" withMapping:dynamicMapping]; // Connect our mapping to RestKit's mapping provider -[[RKObjectManager sharedManager].mappingProvider setMapping:polymorphicMapping forKeyPath:@"people"]; +[[RKObjectManager sharedManager].mappingProvider setMapping:dynamicMapping forKeyPath:@"people"]; -// Configure the polymorphic mapping via matchers -[polymorphicMapping setObjectMapping:boyMapping whenValueOfKeyPath:@"type" isEqualTo:@"Boy"]; -[polymorphicMapping setObjectMapping:girlMapping whenValueOfKeyPath:@"type" isEqualTo:@"Girl"]; +// Configure the dynamic mapping via matchers +[dynamicMapping setObjectMapping:boyMapping whenValueOfKeyPath:@"type" isEqualTo:@"Boy"]; +[dynamicMapping setObjectMapping:girlMapping whenValueOfKeyPath:@"type" isEqualTo:@"Girl"]; -// Configure the polymorphic mapping via a delegate -polymorphicMapping.delegate = self; +// Configure the dynamic mapping via a delegate +dynamicMapping.delegate = self; - (RKObjectMapping*)objectMappingForData:(id)data { // Dynamically construct an object mapping for the data @@ -672,8 +672,8 @@ polymorphicMapping.delegate = self; return nil; } -// Configure the polymorphic mapping via a block -polymorphicMapping.delegateBlock = ^ RKObjectMapping* (id mappableData) { +// Configure the dynamic mapping via a block +dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { if ([[mappableData valueForKey:@"type"] isEqualToString:@"Boy"]) { return boyMapping; } else if ([[mappableData valueForKey:@"type"] isEqualToString:@"Girl"]) { @@ -685,12 +685,12 @@ polymorphicMapping.delegateBlock = ^ RKObjectMapping* (id mappableData) { ``` Notable within this code are the calls to `setObjectMapping:whenValueOfKeyPath:isEqualTo:`. This is the declarative -matcher form of polymorphic configuration. When you use these matchers, RestKit will invoke `valueForKeyPath:` on your +matcher form of dynamic configuration. When you use these matchers, RestKit will invoke `valueForKeyPath:` on your mappable data and then attempt to compare the resulting value with the value provided in the invocation. If you have a simple string or numeric value that can be used to differentiate your mappings, then you don't need to use the -delegate or block callbacks at all to perform polymorphic mapping. +delegate or block callbacks at all to perform dynamic mapping. -That's all there is to it. RestKit will invoke the polymorphic mapping with the data and apply whatever object +That's all there is to it. RestKit will invoke the dynamic mapping with the data and apply whatever object mapping is returned to that data. You can even decline the mapping of individual elements by returning a nil mapping. This can be useful to filter out unwanted information deep within an object graph. diff --git a/RestKit.xcodeproj/project.pbxproj b/RestKit.xcodeproj/project.pbxproj index 8264a068..63a873fd 100644 --- a/RestKit.xcodeproj/project.pbxproj +++ b/RestKit.xcodeproj/project.pbxproj @@ -53,8 +53,6 @@ 250D5C0013A069C400471F0E /* lcl_config_components.h in Headers */ = {isa = PBXBuildFile; fileRef = 250D5BFF13A069C400471F0E /* lcl_config_components.h */; settings = {ATTRIBUTES = (Public, ); }; }; 250D5C0213A069EA00471F0E /* lcl_config_extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 250D5C0113A069EA00471F0E /* lcl_config_extensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 250D5C0413A06A4D00471F0E /* lcl_config_logger.h in Headers */ = {isa = PBXBuildFile; fileRef = 250D5C0313A06A4D00471F0E /* lcl_config_logger.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 250F060913E3A1D900FAAFBF /* RKObjectAbstractMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 250F060713E3A1D800FAAFBF /* RKObjectAbstractMapping.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 250F060A13E3A1D900FAAFBF /* RKObjectAbstractMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 250F060813E3A1D900FAAFBF /* RKObjectAbstractMapping.m */; }; 250F060C13E3B2F900FAAFBF /* friends.json in Resources */ = {isa = PBXBuildFile; fileRef = 250F060B13E3B2F900FAAFBF /* friends.json */; }; 2515E7BA13B36A7D00E013A4 /* ArrayOfResults.json in Resources */ = {isa = PBXBuildFile; fileRef = 2515E7B913B36A7D00E013A4 /* ArrayOfResults.json */; }; 2515E7BD13B36AC100E013A4 /* RKObjectLoaderSpecResultModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2515E7BC13B36AC100E013A4 /* RKObjectLoaderSpecResultModel.m */; }; @@ -68,11 +66,11 @@ 251D14AC133597B800959061 /* RKManagedObjectLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 251D14AA133597B800959061 /* RKManagedObjectLoader.h */; settings = {ATTRIBUTES = (Public, ); }; }; 251D14AD133597B800959061 /* RKManagedObjectLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 251D14AB133597B800959061 /* RKManagedObjectLoader.m */; }; 2523363E11E7A1F00048F9B4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F6C3A9510FE7524008F47C5 /* UIKit.framework */; }; - 252CF8B613E254C60093BBD6 /* RKObjectPolymorphicMappingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8B513E254C60093BBD6 /* RKObjectPolymorphicMappingSpec.m */; }; + 252CF8B613E254C60093BBD6 /* RKObjectDynamicMappingSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8B513E254C60093BBD6 /* RKObjectDynamicMappingSpec.m */; }; 252CF8B913E255D70093BBD6 /* boy.json in Resources */ = {isa = PBXBuildFile; fileRef = 252CF8B813E255D70093BBD6 /* boy.json */; }; 252CF8BB13E255E20093BBD6 /* girl.json in Resources */ = {isa = PBXBuildFile; fileRef = 252CF8BA13E255E20093BBD6 /* girl.json */; }; 252CF8BD13E255EB0093BBD6 /* mixed.json in Resources */ = {isa = PBXBuildFile; fileRef = 252CF8BC13E255EB0093BBD6 /* mixed.json */; }; - 252CF8C013E25FE00093BBD6 /* RKPolymorphicMappingModels.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8BF13E25FE00093BBD6 /* RKPolymorphicMappingModels.m */; }; + 252CF8C013E25FE00093BBD6 /* RKDynamicMappingModels.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8BF13E25FE00093BBD6 /* RKDynamicMappingModels.m */; }; 2530078E137838D30074F3FD /* RKObjectMapper_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2530078D137838D30074F3FD /* RKObjectMapper_Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; 253007A2137876770074F3FD /* OCHamcrestIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 253007A1137876770074F3FD /* OCHamcrestIOS.framework */; }; 2538C05C12A6C44A0006903C /* RKRequestQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 2538C05A12A6C44A0006903C /* RKRequestQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -263,10 +261,11 @@ 25DBB3A113A2486400CE90F1 /* RKLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 25DBB39F13A2486300CE90F1 /* RKLog.m */; }; 25DBB3A413A2506900CE90F1 /* RKObjectMappingOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 25952DEF136C8F9C00D04F93 /* RKObjectMappingOperationSpec.m */; }; 25DBB3A513A2506C00CE90F1 /* RKManagedObjectMappingOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 257FB70C1395DEB5003A628E /* RKManagedObjectMappingOperationSpec.m */; }; + 25E9682E13E6156100ABAE92 /* RKObjectMappingDefinition.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E9682D13E6156100ABAE92 /* RKObjectMappingDefinition.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25F5182513724866009B2E22 /* RKObjectRelationshipMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 25F5182313724865009B2E22 /* RKObjectRelationshipMapping.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25F5182613724866009B2E22 /* RKObjectRelationshipMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 25F5182413724866009B2E22 /* RKObjectRelationshipMapping.m */; }; - 25FB6D5613E4836C00F48969 /* RKObjectPolymorphicMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 252CF8B013E250730093BBD6 /* RKObjectPolymorphicMapping.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25FB6DBD13E4858400F48969 /* RKObjectPolymorphicMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8B113E250730093BBD6 /* RKObjectPolymorphicMapping.m */; }; + 25FB6D5613E4836C00F48969 /* RKObjectDynamicMapping.h in Headers */ = {isa = PBXBuildFile; fileRef = 252CF8B013E250730093BBD6 /* RKObjectDynamicMapping.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25FB6DBD13E4858400F48969 /* RKObjectDynamicMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = 252CF8B113E250730093BBD6 /* RKObjectDynamicMapping.m */; }; 3F032A7910FFB89100F35142 /* RKCat.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F032A7810FFB89100F35142 /* RKCat.m */; }; 3F032AA810FFBBCD00F35142 /* RKHouse.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F032AA710FFBBCD00F35142 /* RKHouse.m */; }; 3F032AAB10FFBC1F00F35142 /* RKResident.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F032AAA10FFBC1F00F35142 /* RKResident.m */; }; @@ -438,9 +437,7 @@ 250D5BFF13A069C400471F0E /* lcl_config_components.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lcl_config_components.h; sourceTree = ""; }; 250D5C0113A069EA00471F0E /* lcl_config_extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lcl_config_extensions.h; sourceTree = ""; }; 250D5C0313A06A4D00471F0E /* lcl_config_logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lcl_config_logger.h; sourceTree = ""; }; - 250F060713E3A1D800FAAFBF /* RKObjectAbstractMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectAbstractMapping.h; sourceTree = ""; }; - 250F060813E3A1D900FAAFBF /* RKObjectAbstractMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectAbstractMapping.m; sourceTree = ""; }; - 250F060B13E3B2F900FAAFBF /* friends.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = friends.json; path = Polymorphic/friends.json; sourceTree = ""; }; + 250F060B13E3B2F900FAAFBF /* friends.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = friends.json; path = Dynamic/friends.json; sourceTree = ""; }; 2515E7B913B36A7D00E013A4 /* ArrayOfResults.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ArrayOfResults.json; sourceTree = ""; }; 2515E7BB13B36AC100E013A4 /* RKObjectLoaderSpecResultModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectLoaderSpecResultModel.h; sourceTree = ""; }; 2515E7BC13B36AC100E013A4 /* RKObjectLoaderSpecResultModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectLoaderSpecResultModel.m; sourceTree = ""; }; @@ -454,14 +451,14 @@ 251D14AA133597B800959061 /* RKManagedObjectLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKManagedObjectLoader.h; sourceTree = ""; }; 251D14AB133597B800959061 /* RKManagedObjectLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKManagedObjectLoader.m; sourceTree = ""; }; 2523360511E79F090048F9B4 /* libRestKitThree20.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRestKitThree20.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 252CF8B013E250730093BBD6 /* RKObjectPolymorphicMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectPolymorphicMapping.h; sourceTree = ""; }; - 252CF8B113E250730093BBD6 /* RKObjectPolymorphicMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectPolymorphicMapping.m; sourceTree = ""; }; - 252CF8B513E254C60093BBD6 /* RKObjectPolymorphicMappingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectPolymorphicMappingSpec.m; sourceTree = ""; }; - 252CF8B813E255D70093BBD6 /* boy.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = boy.json; path = Polymorphic/boy.json; sourceTree = ""; }; - 252CF8BA13E255E20093BBD6 /* girl.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = girl.json; path = Polymorphic/girl.json; sourceTree = ""; }; - 252CF8BC13E255EB0093BBD6 /* mixed.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = mixed.json; path = Polymorphic/mixed.json; sourceTree = ""; }; - 252CF8BE13E25FE00093BBD6 /* RKPolymorphicMappingModels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKPolymorphicMappingModels.h; sourceTree = ""; }; - 252CF8BF13E25FE00093BBD6 /* RKPolymorphicMappingModels.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKPolymorphicMappingModels.m; sourceTree = ""; }; + 252CF8B013E250730093BBD6 /* RKObjectDynamicMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectDynamicMapping.h; sourceTree = ""; }; + 252CF8B113E250730093BBD6 /* RKObjectDynamicMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectDynamicMapping.m; sourceTree = ""; }; + 252CF8B513E254C60093BBD6 /* RKObjectDynamicMappingSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectDynamicMappingSpec.m; sourceTree = ""; }; + 252CF8B813E255D70093BBD6 /* boy.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = boy.json; path = Dynamic/boy.json; sourceTree = ""; }; + 252CF8BA13E255E20093BBD6 /* girl.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = girl.json; path = Dynamic/girl.json; sourceTree = ""; }; + 252CF8BC13E255EB0093BBD6 /* mixed.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = mixed.json; path = Dynamic/mixed.json; sourceTree = ""; }; + 252CF8BE13E25FE00093BBD6 /* RKDynamicMappingModels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKDynamicMappingModels.h; sourceTree = ""; }; + 252CF8BF13E25FE00093BBD6 /* RKDynamicMappingModels.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKDynamicMappingModels.m; sourceTree = ""; }; 2530078D137838D30074F3FD /* RKObjectMapper_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectMapper_Private.h; sourceTree = ""; }; 253007A1137876770074F3FD /* OCHamcrestIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrestIOS.framework; sourceTree = ""; }; 2538C05A12A6C44A0006903C /* RKRequestQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKRequestQueue.h; sourceTree = ""; }; @@ -680,6 +677,7 @@ 25D6390613517D8B000879B1 /* RKAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKAlert.m; sourceTree = ""; }; 25DBB39F13A2486300CE90F1 /* RKLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKLog.m; sourceTree = ""; }; 25E075981279D9AB00B22EC9 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; + 25E9682D13E6156100ABAE92 /* RKObjectMappingDefinition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectMappingDefinition.h; sourceTree = ""; }; 25F5182313724865009B2E22 /* RKObjectRelationshipMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectRelationshipMapping.h; sourceTree = ""; }; 25F5182413724866009B2E22 /* RKObjectRelationshipMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectRelationshipMapping.m; sourceTree = ""; }; 3F032A7710FFB89100F35142 /* RKCat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKCat.h; sourceTree = ""; }; @@ -957,7 +955,7 @@ path = NSLogger; sourceTree = ""; }; - 252CF8B713E255B90093BBD6 /* Polymorphic */ = { + 252CF8B713E255B90093BBD6 /* Dynamic */ = { isa = PBXGroup; children = ( 250F060B13E3B2F900FAAFBF /* friends.json */, @@ -965,7 +963,7 @@ 252CF8BA13E255E20093BBD6 /* girl.json */, 252CF8BC13E255EB0093BBD6 /* mixed.json */, ); - name = Polymorphic; + name = Dynamic; sourceTree = ""; }; 253A085F12551D8D00976E89 /* Code */ = { @@ -1081,10 +1079,9 @@ 25A1CB4213840C5300A7D5C9 /* RKParserRegistry.m */, 3FD12C841379AD64008B996A /* RKRouter.h */, 257FB6FE1395D36D003A628E /* RKObjectMapperError.h */, - 252CF8B013E250730093BBD6 /* RKObjectPolymorphicMapping.h */, - 252CF8B113E250730093BBD6 /* RKObjectPolymorphicMapping.m */, - 250F060713E3A1D800FAAFBF /* RKObjectAbstractMapping.h */, - 250F060813E3A1D900FAAFBF /* RKObjectAbstractMapping.m */, + 252CF8B013E250730093BBD6 /* RKObjectDynamicMapping.h */, + 252CF8B113E250730093BBD6 /* RKObjectDynamicMapping.m */, + 25E9682D13E6156100ABAE92 /* RKObjectMappingDefinition.h */, ); path = ObjectMapping; sourceTree = ""; @@ -1186,8 +1183,8 @@ 256FDE54112DB0B90077F340 /* RKObjectMapperSpecModel.m */, 2515E7BB13B36AC100E013A4 /* RKObjectLoaderSpecResultModel.h */, 2515E7BC13B36AC100E013A4 /* RKObjectLoaderSpecResultModel.m */, - 252CF8BE13E25FE00093BBD6 /* RKPolymorphicMappingModels.h */, - 252CF8BF13E25FE00093BBD6 /* RKPolymorphicMappingModels.m */, + 252CF8BE13E25FE00093BBD6 /* RKDynamicMappingModels.h */, + 252CF8BF13E25FE00093BBD6 /* RKDynamicMappingModels.m */, ); path = Models; sourceTree = ""; @@ -1351,7 +1348,7 @@ 25952ED9136F563E00D04F93 /* JSON */ = { isa = PBXGroup; children = ( - 252CF8B713E255B90093BBD6 /* Polymorphic */, + 252CF8B713E255B90093BBD6 /* Dynamic */, 2515E7B913B36A7D00E013A4 /* ArrayOfResults.json */, 251939EC13ABA06D0073A39B /* DynamicKeysWithRelationship.json */, 251939E613AABED40073A39B /* DynamicKeys.json */, @@ -1431,7 +1428,7 @@ 3F5356EC13785DA300132100 /* RKObjectSerializerSpec.m */, 25A1CB4613840E6000A7D5C9 /* RKParserRegistrySpec.m */, 259BEF5013C3B12B00487F66 /* RKObjectMappingResultSpec.m */, - 252CF8B513E254C60093BBD6 /* RKObjectPolymorphicMappingSpec.m */, + 252CF8B513E254C60093BBD6 /* RKObjectDynamicMappingSpec.m */, ); path = ObjectMapping; sourceTree = ""; @@ -1598,9 +1595,9 @@ 3FD12C851379AD64008B996A /* RKRouter.h in Headers */, 25A1CB4313840C5400A7D5C9 /* RKParserRegistry.h in Headers */, 25A1CA8A137DD33900A7D5C9 /* RKObjectLoader_Internals.h in Headers */, - 250F060913E3A1D900FAAFBF /* RKObjectAbstractMapping.h in Headers */, - 25FB6D5613E4836C00F48969 /* RKObjectPolymorphicMapping.h in Headers */, + 25FB6D5613E4836C00F48969 /* RKObjectDynamicMapping.h in Headers */, 257FB6FF1395D36D003A628E /* RKObjectMapperError.h in Headers */, + 25E9682E13E6156100ABAE92 /* RKObjectMappingDefinition.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2103,7 +2100,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 25FB6DBD13E4858400F48969 /* RKObjectPolymorphicMapping.m in Sources */, + 25FB6DBD13E4858400F48969 /* RKObjectDynamicMapping.m in Sources */, 253A08FB1255246500976E89 /* RKObjectLoader.m in Sources */, 253A08FD1255246600976E89 /* RKObjectManager.m in Sources */, 253A09011255246900976E89 /* RKObjectPropertyInspector.m in Sources */, @@ -2118,7 +2115,6 @@ 257D2D7113759D70008E9649 /* RKObjectMappingResult.m in Sources */, 253E04C613798D72005D2E15 /* RKErrorMessage.m in Sources */, 25A1CB4413840C5400A7D5C9 /* RKParserRegistry.m in Sources */, - 250F060A13E3A1D900FAAFBF /* RKObjectAbstractMapping.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2242,8 +2238,8 @@ 250CAF5813BF9F2D00A1330E /* RKManagedObjectStoreSpec.m in Sources */, 259BEF5113C3B12B00487F66 /* RKObjectMappingResultSpec.m in Sources */, 258E490113C51FE600C9C883 /* RKJSONParserJSONKitSpec.m in Sources */, - 252CF8B613E254C60093BBD6 /* RKObjectPolymorphicMappingSpec.m in Sources */, - 252CF8C013E25FE00093BBD6 /* RKPolymorphicMappingModels.m in Sources */, + 252CF8B613E254C60093BBD6 /* RKObjectDynamicMappingSpec.m in Sources */, + 252CF8C013E25FE00093BBD6 /* RKDynamicMappingModels.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Specs/Fixtures/JSON/Polymorphic/boy.json b/Specs/Fixtures/JSON/Dynamic/boy.json similarity index 100% rename from Specs/Fixtures/JSON/Polymorphic/boy.json rename to Specs/Fixtures/JSON/Dynamic/boy.json diff --git a/Specs/Fixtures/JSON/Polymorphic/friends.json b/Specs/Fixtures/JSON/Dynamic/friends.json similarity index 100% rename from Specs/Fixtures/JSON/Polymorphic/friends.json rename to Specs/Fixtures/JSON/Dynamic/friends.json diff --git a/Specs/Fixtures/JSON/Polymorphic/girl.json b/Specs/Fixtures/JSON/Dynamic/girl.json similarity index 100% rename from Specs/Fixtures/JSON/Polymorphic/girl.json rename to Specs/Fixtures/JSON/Dynamic/girl.json diff --git a/Specs/Fixtures/JSON/Polymorphic/mixed.json b/Specs/Fixtures/JSON/Dynamic/mixed.json similarity index 100% rename from Specs/Fixtures/JSON/Polymorphic/mixed.json rename to Specs/Fixtures/JSON/Dynamic/mixed.json diff --git a/Specs/Models/RKPolymorphicMappingModels.h b/Specs/Models/RKDynamicMappingModels.h similarity index 91% rename from Specs/Models/RKPolymorphicMappingModels.h rename to Specs/Models/RKDynamicMappingModels.h index 17a8475d..e25ee5d4 100644 --- a/Specs/Models/RKPolymorphicMappingModels.h +++ b/Specs/Models/RKDynamicMappingModels.h @@ -1,5 +1,5 @@ // -// RKPolymorphicMappingModels.h +// RKDynamicMappingModels.h // RestKit // // Created by Blake Watters on 7/28/11. diff --git a/Specs/Models/RKPolymorphicMappingModels.m b/Specs/Models/RKDynamicMappingModels.m similarity index 76% rename from Specs/Models/RKPolymorphicMappingModels.m rename to Specs/Models/RKDynamicMappingModels.m index 3e2d70f0..2aba7234 100644 --- a/Specs/Models/RKPolymorphicMappingModels.m +++ b/Specs/Models/RKDynamicMappingModels.m @@ -1,12 +1,12 @@ // -// RKPolymorphicMappingModels.m +// RKDynamicMappingModels.m // RestKit // // Created by Blake Watters on 7/28/11. // Copyright 2011 RestKit. All rights reserved. // -#import "RKPolymorphicMappingModels.h" +#import "RKDynamicMappingModels.h" @implementation Person @synthesize name; diff --git a/Specs/ObjectMapping/RKObjectPolymorphicMappingSpec.m b/Specs/ObjectMapping/RKObjectDynamicMappingSpec.m similarity index 85% rename from Specs/ObjectMapping/RKObjectPolymorphicMappingSpec.m rename to Specs/ObjectMapping/RKObjectDynamicMappingSpec.m index 6a44230d..c7a19ba9 100644 --- a/Specs/ObjectMapping/RKObjectPolymorphicMappingSpec.m +++ b/Specs/ObjectMapping/RKObjectDynamicMappingSpec.m @@ -1,5 +1,5 @@ // -// RKObjectPolymorphicMappingSpec.m +// RKObjectDynamicMappingSpec.m // RestKit // // Created by Blake Watters on 7/28/11. @@ -7,17 +7,17 @@ // #import "RKSpecEnvironment.h" -#import "RKObjectPolymorphicMapping.h" -#import "RKPolymorphicMappingModels.h" +#import "RKObjectDynamicMapping.h" +#import "RKDynamicMappingModels.h" -@interface RKObjectPolymorphicMappingSpec : RKSpec +@interface RKObjectDynamicMappingSpec : RKSpec @end -@implementation RKObjectPolymorphicMappingSpec +@implementation RKObjectDynamicMappingSpec - (void)itShouldPickTheAppropriateMappingBasedOnAnAttributeValue { - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class] block:^(RKObjectMapping* mapping) { [mapping mapAttributes:@"name", nil]; }]; @@ -35,7 +35,7 @@ } - (void)itShouldMatchOnAnNSNumberAttributeValue { - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class] block:^(RKObjectMapping* mapping) { [mapping mapAttributes:@"name", nil]; }]; @@ -53,7 +53,7 @@ } - (void)itShouldPickTheAppropriateMappingBasedOnDelegateCallback { - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; dynamicMapping.delegate = self; RKObjectMapping* mapping = [dynamicMapping objectMappingForDictionary:RKSpecParseFixture(@"girl.json")]; assertThat(mapping, is(notNilValue())); @@ -64,8 +64,8 @@ } - (void)itShouldPickTheAppropriateMappingBasedOnBlockDelegateCallback { - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; - dynamicMapping.delegateBlock = ^ RKObjectMapping* (id data) { + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id data) { if ([[data valueForKey:@"type"] isEqualToString:@"Girl"]) { return [RKObjectMapping mappingForClass:[Girl class] block:^(RKObjectMapping* mapping) { [mapping mapAttributes:@"name", nil]; @@ -88,7 +88,7 @@ - (void)itShouldFailAnAssertionWhenInvokedWithSomethingOtherThanADictionary { NSException* exception = nil; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; @try { [dynamicMapping objectMappingForDictionary:(NSDictionary*)[NSArray array]]; } diff --git a/Specs/ObjectMapping/RKObjectMappingNextGenSpec.m b/Specs/ObjectMapping/RKObjectMappingNextGenSpec.m index b63286dc..986bb1e2 100644 --- a/Specs/ObjectMapping/RKObjectMappingNextGenSpec.m +++ b/Specs/ObjectMapping/RKObjectMappingNextGenSpec.m @@ -17,7 +17,7 @@ #import "RKObjectMapper.h" #import "RKObjectMapper_Private.h" #import "RKObjectMapperError.h" -#import "RKPolymorphicMappingModels.h" +#import "RKDynamicMappingModels.h" //////////////////////////////////////////////////////////////////////////////// @@ -1291,15 +1291,15 @@ assertThat([mappingProvider objectMappingsForClass:[RKExampleUser class]], is(equalTo([NSArray arrayWithObjects:firstMapping, secondMapping, thirdMapping, nil]))); } -#pragma mark - RKObjectPolymorphicMapping +#pragma mark - RKObjectDynamicMapping - (void)itShouldMapASingleObjectDynamically { RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[Boy class]]; [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; - dynamicMapping.delegateBlock = ^ RKObjectMapping* (id mappableData) { + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { if ([[mappableData valueForKey:@"type"] isEqualToString:@"Boy"]) { return boyMapping; } else if ([[mappableData valueForKey:@"type"] isEqualToString:@"Girl"]) { @@ -1325,7 +1325,7 @@ [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; [dynamicMapping setObjectMapping:boyMapping whenValueOfKeyPath:@"type" isEqualTo:@"Boy"]; [dynamicMapping setObjectMapping:girlMapping whenValueOfKeyPath:@"type" isEqualTo:@"Girl"]; @@ -1340,12 +1340,12 @@ assertThat(user.name, is(equalTo(@"Blake Watters"))); } -- (void)itShouldACollectionOfObjectsPolymorphically { +- (void)itShouldACollectionOfObjectsDynamically { RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[Boy class]]; [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; [dynamicMapping setObjectMapping:boyMapping whenValueOfKeyPath:@"type" isEqualTo:@"Boy"]; [dynamicMapping setObjectMapping:girlMapping whenValueOfKeyPath:@"type" isEqualTo:@"Girl"]; @@ -1365,12 +1365,12 @@ assertThat(girl.name, is(equalTo(@"Sarah"))); } -- (void)itShouldMapAPolymorphicRelationship { +- (void)itShouldMapARelationshipDynamically { RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[Boy class]]; [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; [dynamicMapping setObjectMapping:boyMapping whenValueOfKeyPath:@"type" isEqualTo:@"Boy"]; [dynamicMapping setObjectMapping:girlMapping whenValueOfKeyPath:@"type" isEqualTo:@"Girl"]; [boyMapping mapKeyPath:@"friends" toRelationship:@"friends" withMapping:dynamicMapping]; @@ -1398,8 +1398,8 @@ [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; - dynamicMapping.delegateBlock = ^ RKObjectMapping* (id mappableData) { + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { if ([[mappableData valueForKey:@"type"] isEqualToString:@"Boy"]) { return boyMapping; } else if ([[mappableData valueForKey:@"type"] isEqualToString:@"Girl"]) { @@ -1428,8 +1428,8 @@ [boyMapping mapAttributes:@"name", nil]; RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; [girlMapping mapAttributes:@"name", nil]; - RKObjectPolymorphicMapping* dynamicMapping = [RKObjectPolymorphicMapping polymorphicMapping]; - dynamicMapping.delegateBlock = ^ RKObjectMapping* (id mappableData) { + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { if ([[mappableData valueForKey:@"type"] isEqualToString:@"Boy"]) { return boyMapping; } else if ([[mappableData valueForKey:@"type"] isEqualToString:@"Girl"]) { @@ -1459,4 +1459,72 @@ assertThat(boy.name, is(equalTo(@"John Doe"))); } +- (void)itShouldMapATargetObjectWithADynamicMapping { + RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[Boy class]]; + [boyMapping mapAttributes:@"name", nil]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { + if ([[mappableData valueForKey:@"type"] isEqualToString:@"Boy"]) { + return boyMapping; + } + + return nil; + }; + + RKObjectMappingProvider* provider = [RKObjectMappingProvider mappingProvider]; + [provider setMapping:dynamicMapping forKeyPath:@""]; + + id userInfo = RKSpecParseFixture(@"boy.json"); + Boy* blake = [[Boy new] autorelease]; + RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:userInfo mappingProvider:provider]; + mapper.targetObject = blake; + Boy* user = [[mapper performMapping] asObject]; + assertThat(user, is(instanceOf([Boy class]))); + assertThat(user.name, is(equalTo(@"Blake Watters"))); +} + +- (void)itShouldFailWithAnErrorIfATargetObjectIsProvidedAndTheDynamicMappingReturnsNil { + RKObjectMapping* boyMapping = [RKObjectMapping mappingForClass:[Boy class]]; + [boyMapping mapAttributes:@"name", nil]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { + return nil; + }; + + RKObjectMappingProvider* provider = [RKObjectMappingProvider mappingProvider]; + [provider setMapping:dynamicMapping forKeyPath:@""]; + + id userInfo = RKSpecParseFixture(@"boy.json"); + Boy* blake = [[Boy new] autorelease]; + RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:userInfo mappingProvider:provider]; + mapper.targetObject = blake; + Boy* user = [[mapper performMapping] asObject]; + assertThat(user, is(nilValue())); + assertThat(mapper.errors, hasCountOf(1)); +} + +- (void)itShouldFailWithAnErrorIfATargetObjectIsProvidedAndTheDynamicMappingReturnsTheIncorrectType { + RKObjectMapping* girlMapping = [RKObjectMapping mappingForClass:[Girl class]]; + [girlMapping mapAttributes:@"name", nil]; + RKObjectDynamicMapping* dynamicMapping = [RKObjectDynamicMapping dynamicMapping]; + dynamicMapping.objectMappingForDataBlock = ^ RKObjectMapping* (id mappableData) { + if ([[mappableData valueForKey:@"type"] isEqualToString:@"Girl"]) { + return girlMapping; + } + + return nil; + }; + + RKObjectMappingProvider* provider = [RKObjectMappingProvider mappingProvider]; + [provider setMapping:dynamicMapping forKeyPath:@""]; + + id userInfo = RKSpecParseFixture(@"girl.json"); + Boy* blake = [[Boy new] autorelease]; + RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:userInfo mappingProvider:provider]; + mapper.targetObject = blake; + Boy* user = [[mapper performMapping] asObject]; + assertThat(user, is(nilValue())); + assertThat(mapper.errors, hasCountOf(1)); +} + @end