diff --git a/Code/Network/RKClient.m b/Code/Network/RKClient.m index 5e4a65ef..44b7613c 100644 --- a/Code/Network/RKClient.m +++ b/Code/Network/RKClient.m @@ -179,8 +179,12 @@ NSString* RKMakePathWithObject(NSString* path, id object) { [_baseURLReachabilityObserver release]; _baseURLReachabilityObserver = nil; - NSURL* URL = [NSURL URLWithString:baseURL]; - _baseURLReachabilityObserver = [[RKReachabilityObserver reachabilityObserverWithHostName:[URL host]] retain]; + + // Don't crash if baseURL is nil'd out (i.e. dealloc) + if (baseURL) { + NSURL* URL = [NSURL URLWithString:baseURL]; + _baseURLReachabilityObserver = [[RKReachabilityObserver reachabilityObserverWithHostName:[URL host]] retain]; + } } - (RKRequest*)requestWithResourcePath:(NSString*)resourcePath delegate:(id)delegate { diff --git a/Code/ObjectMapping/RKObjectLoader.m b/Code/ObjectMapping/RKObjectLoader.m index f245b010..9849b26d 100644 --- a/Code/ObjectMapping/RKObjectLoader.m +++ b/Code/ObjectMapping/RKObjectLoader.m @@ -34,7 +34,7 @@ } - (id)initWithResourcePath:(NSString*)resourcePath client:(RKClient*)client mapper:(RKObjectMapper*)mapper delegate:(NSObject*)delegate { - if (self = [self initWithURL:[client URLForResourcePath:resourcePath] delegate:delegate]) { + if ((self = [self initWithURL:[client URLForResourcePath:resourcePath] delegate:delegate])) { _mapper = [mapper retain]; self.managedObjectStore = nil; _targetObjectID = nil; diff --git a/Code/ObjectMapping/RKObjectMapper.h b/Code/ObjectMapping/RKObjectMapper.h index e3443673..74829885 100644 --- a/Code/ObjectMapping/RKObjectMapper.h +++ b/Code/ObjectMapping/RKObjectMapper.h @@ -151,6 +151,11 @@ typedef enum { */ - (id)mapFromString:(NSString *)string toClass:(Class)class keyPath:(NSString*)keyPath; +/** + * Map a dictionary of elements to an instance of a particular class + */ +- (id)mapObjectFromDictionary:(NSDictionary*)dictionary toClass:(Class)class; + /** * Map an array of object dictionary representations to instances of a particular * object class diff --git a/Code/ObjectMapping/RKObjectMapper.m b/Code/ObjectMapping/RKObjectMapper.m index b25f2f23..f84c6401 100644 --- a/Code/ObjectMapping/RKObjectMapper.m +++ b/Code/ObjectMapping/RKObjectMapper.m @@ -125,13 +125,19 @@ static const NSString* kRKModelMapperMappingFormatParserKey = @"RKMappingFormatP return error; } +// Primary entry point for RKObjectLoader - (id)mapFromString:(NSString*)string toClass:(Class)class keyPath:(NSString*)keyPath { id object = [self parseString:string]; if (keyPath) { object = [object valueForKeyPath:keyPath]; } + if ([object isKindOfClass:[NSDictionary class]]) { - return [self mapObjectFromDictionary:(NSDictionary*)object]; + if (class) { + return [self mapObjectFromDictionary:(NSDictionary*)object toClass:class]; + } else { + return [self mapObjectFromDictionary:(NSDictionary*)object]; + } } else if ([object isKindOfClass:[NSArray class]]) { if (class) { return [self mapObjectsFromArrayOfDictionaries:(NSArray*)object toClass:class]; @@ -193,8 +199,12 @@ static const NSString* kRKModelMapperMappingFormatParserKey = @"RKMappingFormatP } } -// TODO: Can I make this support keyPath?? +- (id)mapObjectFromDictionary:(NSDictionary*)dictionary toClass:(Class)class { + return [self createOrUpdateInstanceOfModelClass:class fromElements:dictionary]; +} + - (id)mapObjectFromDictionary:(NSDictionary*)dictionary { + // TODO: Makes assumptions about the structure of the JSON... NSString* elementName = [[dictionary allKeys] objectAtIndex:0]; Class class = [_elementToClassMappings objectForKey:elementName]; NSDictionary* elements = [dictionary objectForKey:elementName]; @@ -365,15 +375,24 @@ static const NSString* kRKModelMapperMappingFormatParserKey = @"RKMappingFormatP NSLog(@"Caught exception:%@ when trying valueForKeyPath with path:%@ for elements:%@", e, elementKeyPath, elements); } - if ([relationshipElements isKindOfClass:[NSArray class]] || [relationshipElements isKindOfClass:[NSSet class]]) { + // TODO: Need to send NSSet or NSArray depending on what the property type is... + Class collectionClass = [self typeClassForProperty:propertyName ofClass:[object class]]; +// if ([relationshipElements isKindOfClass:[NSArray class]] || [relationshipElements isKindOfClass:[NSSet class]]) { + if ([collectionClass isSubclassOfClass:[NSSet class]] || [collectionClass isSubclassOfClass:[NSArray class]]) { // NOTE: The last part of the keyPath contains the elementName for the mapped destination class of our children NSArray* componentsOfKeyPath = [elementKeyPath componentsSeparatedByString:@"."]; Class class = [_elementToClassMappings objectForKey:[componentsOfKeyPath objectAtIndex:[componentsOfKeyPath count] - 1]]; - NSMutableSet* children = [NSMutableSet setWithCapacity:[relationshipElements count]]; + id children = nil; + if ([collectionClass isSubclassOfClass:[NSSet class]]) { + children = [NSMutableSet setWithCapacity:[relationshipElements count]]; + } else if ([collectionClass isSubclassOfClass:[NSArray class]]) { + children = [NSMutableArray arrayWithCapacity:[relationshipElements count]]; + } + for (NSDictionary* childElements in relationshipElements) { id child = [self createOrUpdateInstanceOfModelClass:class fromElements:childElements]; if (child) { - [children addObject:child]; + [(NSMutableArray*)children addObject:child]; } } diff --git a/RestKit.xcodeproj/project.pbxproj b/RestKit.xcodeproj/project.pbxproj index 18359193..ce7225db 100644 --- a/RestKit.xcodeproj/project.pbxproj +++ b/RestKit.xcodeproj/project.pbxproj @@ -1662,7 +1662,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "osascript Specs/Support/set_ip_address.scpt"; + shellScript = "# Disabled - Xcode 4\n#osascript Specs/Support/set_ip_address.scpt"; }; 25956980126DF159004BAC4C /* Protect Copied Headers */ = { isa = PBXShellScriptBuildPhase; diff --git a/Specs/RKModelMapperSpec.m b/Specs/RKModelMapperSpec.m index 0875291a..e77290ed 100644 --- a/Specs/RKModelMapperSpec.m +++ b/Specs/RKModelMapperSpec.m @@ -12,6 +12,50 @@ #import "RKMappableObject.h" #import "RKMappableAssociation.h" #import "RKObjectMapperSpecModel.h" +#import "RKObject.h" +#import "NSDictionary+RKAdditions.h" + +@interface RKObjectMapperSpecUser : RKObject { +@private + NSNumber* userID; + NSString* encryptedPassword; + NSString* name; + NSString* salt; + NSDate* createdAt; + NSDate* updatedAt; + NSString* image; + NSString* email; +} + +@property (nonatomic, retain) NSNumber* userID; +@property (nonatomic, retain) NSString* encryptedPassword; +@property (nonatomic, retain) NSString* name; +@property (nonatomic, retain) NSString* salt; +@property (nonatomic, retain) NSDate* createdAt; +@property (nonatomic, retain) NSDate* updatedAt; +@property (nonatomic, retain) NSString* image; +@property (nonatomic, retain) NSString* email; + +@end + +@implementation RKObjectMapperSpecUser + +@synthesize userID, encryptedPassword, name, salt, createdAt, updatedAt, image, email; + ++ (NSDictionary*)elementToPropertyMappings { + return [NSDictionary dictionaryWithKeysAndObjects: + @"id", @"userID", + @"encrypted_password", @"encryptedPassword", + @"name", @"name", + @"salt", @"salt", + @"created_at", @"createdAt", + @"updated_at", @"updatedAt", + @"image", @"image", + @"email", @"email", + nil]; +} + +@end @interface RKObjectMapperSpec : NSObject @@ -22,6 +66,29 @@ @implementation RKObjectMapperSpec +- (NSString*)userJSON { + return @"{\"user\":" + @"{\"encrypted_password\":\"68dad82a867c4a61719fec594c119188ed35cd3b7d42eed1647e46d85f2ffdd8\",\"name\":\"mimi\"," + @"\"salt\":\"809c79c24cbebfe3f9feea2e9bf98255e5af0f0b8514b6c0d71dcc63fa083688\",\"created_at\":\"2011-02-16T23:04:22Z\"," + @"\"updated_at\":\"2011-02-16T23:04:22Z\",\"id\":" + @"10,\"image\":null,\"email\":\"mimi@mimi.com\"}}"; +} + +- (void)itShouldMapWhenGivenAClassAndElements { + RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; + mapper.format = RKMappingFormatJSON; + RKObjectMapperSpecUser* user = [mapper mapFromString:[self userJSON] toClass:[RKObjectMapperSpecUser class] keyPath:@"user"]; + [expectThat(user.name) should:be(@"mimi")]; +} + +- (void)itShouldMapWhenGivenRegisteredElementsAndASingleObject { + RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; + [mapper registerClass:[RKObjectMapperSpecUser class] forElementNamed:@"user"]; + mapper.format = RKMappingFormatJSON; + RKObjectMapperSpecUser* user = [mapper mapFromString:[self userJSON] toClass:nil keyPath:nil]; + [expectThat(user.name) should:be(@"mimi")]; +} + - (void)itShouldMapFromJSON { RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; mapper.format = RKMappingFormatJSON; @@ -114,7 +181,7 @@ //} - (void)itShouldNotUpdateNilPropertyToNil { - RKObjectMapperSpecModel* model = [[RKObjectMapperSpecModel alloc] autorelease]; + RKObjectMapperSpecModel* model = [[[RKObjectMapperSpecModel alloc] init] autorelease]; RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; [mapper updateModel:model ifNewPropertyValue:nil forPropertyNamed:@"name"]; @@ -122,7 +189,7 @@ } - (void)itShouldBeAbleToSetNonNilPropertiesToNil { - RKObjectMapperSpecModel* model = [[RKObjectMapperSpecModel alloc] autorelease]; + RKObjectMapperSpecModel* model = [[[RKObjectMapperSpecModel alloc] init] autorelease]; model.age = [NSNumber numberWithInt:0]; RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; [mapper updateModel:model ifNewPropertyValue:nil forPropertyNamed:@"age"]; @@ -131,7 +198,7 @@ } - (void)itShouldBeAbleToSetNilPropertiesToNonNil { - RKObjectMapperSpecModel* model = [[RKObjectMapperSpecModel alloc] autorelease]; + RKObjectMapperSpecModel* model = [[[RKObjectMapperSpecModel alloc] init] autorelease]; RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; [mapper updateModel:model ifNewPropertyValue:[NSNumber numberWithInt:0] forPropertyNamed:@"age"]; @@ -139,7 +206,7 @@ } - (void)itShouldBeAbleToSetNonNilNSStringPropertiesToNonNil { - RKObjectMapperSpecModel* model = [[RKObjectMapperSpecModel alloc] autorelease]; + RKObjectMapperSpecModel* model = [[[RKObjectMapperSpecModel alloc] init] autorelease]; RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; model.name = @"Bob"; @@ -148,16 +215,17 @@ } - (void)itShouldBeAbleToSetNonNilNSNumberPropertiesToNonNil { - RKObjectMapperSpecModel* model = [[RKObjectMapperSpecModel alloc] autorelease]; + RKObjectMapperSpecModel* model = [[[RKObjectMapperSpecModel alloc] init] autorelease]; RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; model.age = [NSNumber numberWithInt:16]; [mapper updateModel:model ifNewPropertyValue:[NSNumber numberWithInt:17] forPropertyNamed:@"age"]; - [expectThat(model.age) should:be([NSNumber numberWithInt:17])]; + NSNumber* expectedAge = [NSNumber numberWithInt:17]; + [expectThat(model.age) should:be(17)]; } - (void)itShouldBeAbleToSetNonNilNSDatePropertiesToNonNil { - RKObjectMapperSpecModel* model = [[RKObjectMapperSpecModel alloc] autorelease]; + RKObjectMapperSpecModel* model = [[[RKObjectMapperSpecModel alloc] init] autorelease]; RKObjectMapper* mapper = [[RKObjectMapper alloc] init]; model.createdAt = [NSDate date]; diff --git a/Specs/Support/set_ip_address.scpt b/Specs/Support/set_ip_address.scpt index 13bf8114..6f46c43c 100644 Binary files a/Specs/Support/set_ip_address.scpt and b/Specs/Support/set_ip_address.scpt differ