diff --git a/Code/CoreData/CoreData.h b/Code/CoreData/CoreData.h index 2e7f2b4f57..bcf92db187 100644 --- a/Code/CoreData/CoreData.h +++ b/Code/CoreData/CoreData.h @@ -26,8 +26,8 @@ #import "RKManagedObjectMapping.h" #import "RKManagedObjectMappingOperation.h" #import "RKManagedObjectMappingCache.h" -#import "RKInMemoryMappingCache.h" -#import "RKFetchRequestMappingCache.h" +#import "RKInMemoryManagedObjectCache.h" +#import "RKFetchRequestManagedObjectCache.h" #import "RKSearchableManagedObject.h" #import "RKSearchWord.h" @@ -35,3 +35,4 @@ #import "RKObjectMappingProvider+CoreData.h" #import "NSManagedObjectContext+RKAdditions.h" #import "NSManagedObject+RKAdditions.h" +#import "NSEntityDescription+RKAdditions.h" diff --git a/Code/CoreData/NSEntityDescription+RKAdditions.h b/Code/CoreData/NSEntityDescription+RKAdditions.h new file mode 100644 index 0000000000..a4805e581e --- /dev/null +++ b/Code/CoreData/NSEntityDescription+RKAdditions.h @@ -0,0 +1,39 @@ +// +// NSEntityDescription+RKAdditions.h +// RestKit +// +// Created by Blake Watters on 3/22/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import + +/** + The key for retrieving the name of the attribute that acts as + the primary key from the user info dictionary of the receiving NSEntityDescription. + + **Value**: @"primaryKeyAttribute" + */ +extern NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey; + +/** + Provides extensions to NSEntityDescription for various common tasks. + */ +@interface NSEntityDescription (RKAdditions) + +/** + The name of the attribute that acts as the primary key for the receiver. + + The primary key attribute can be configured in two ways: + 1. From within the Xcode Core Data editing view by + adding the desired attribute's name as the value for the + key `primaryKeyAttribute` to the user info dictionary. + 1. Programmatically, by retrieving the NSEntityDescription instance and + setting the property's value. + + Programmatically configured values take precedence over the user info + dictionary. + */ +@property(nonatomic, retain) NSString *primaryKeyAttribute; + +@end diff --git a/Code/CoreData/NSEntityDescription+RKAdditions.m b/Code/CoreData/NSEntityDescription+RKAdditions.m new file mode 100644 index 0000000000..29aa5a17d2 --- /dev/null +++ b/Code/CoreData/NSEntityDescription+RKAdditions.m @@ -0,0 +1,38 @@ +// +// NSEntityDescription+RKAdditions.m +// RestKit +// +// Created by Blake Watters on 3/22/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import +#import "NSEntityDescription+RKAdditions.h" + +NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey = @"primaryKeyAttribute"; +static char primaryKeyAttributeKey; + +@implementation NSEntityDescription (RKAdditions) + +- (NSString *)primaryKeyAttribute +{ + // Check for an associative object reference + NSString *primaryKeyAttribute = (NSString *) objc_getAssociatedObject(self, &primaryKeyAttributeKey); + + // Fall back to the userInfo dictionary + if (! primaryKeyAttribute) { + primaryKeyAttribute = [self.userInfo valueForKey:RKEntityDescriptionPrimaryKeyAttributeUserInfoKey]; + } + + return primaryKeyAttribute; +} + +- (void)setPrimaryKeyAttribute:(NSString *)primaryKeyAttribute +{ + objc_setAssociatedObject(self, + &primaryKeyAttributeKey, + primaryKeyAttribute, + OBJC_ASSOCIATION_RETAIN); +} + +@end diff --git a/Code/CoreData/NSManagedObject+ActiveRecord.h b/Code/CoreData/NSManagedObject+ActiveRecord.h index f5ae3c975a..c4603ed281 100644 --- a/Code/CoreData/NSManagedObject+ActiveRecord.h +++ b/Code/CoreData/NSManagedObject+ActiveRecord.h @@ -22,7 +22,8 @@ @end /** - Extensions for NSManage + Provides extensions to NSManagedObject implementing a low-ceremony querying + interface. */ @interface NSManagedObject (ActiveRecord) @@ -102,6 +103,25 @@ */ - (BOOL)isNew; +/** + Finds the instance of the receiver's entity with the given value for the primary key attribute + in the managed object context for the current thread. + + @param primaryKeyValue The value for the receiving entity's primary key attribute. + @return The object with the primary key attribute equal to the given value or nil. + */ ++ (id)findByPrimaryKey:(id)primaryKeyValue; + +/** + Finds the instance of the receiver's entity with the given value for the primary key attribute in + the given managed object context. + + @param primaryKeyValue The value for the receiving entity's primary key attribute. + @param context The managed object context to find the instance in. + @return The object with the primary key attribute equal to the given value or nil. + */ ++ (id)findByPrimaryKey:(id)primaryKeyValue inContext:(NSManagedObjectContext *)context; + //////////////////////////////////////////////////////////////////////////////////////////////////// + (NSManagedObjectContext*)currentContext; diff --git a/Code/CoreData/NSManagedObject+ActiveRecord.m b/Code/CoreData/NSManagedObject+ActiveRecord.m index f765697d85..172d330a53 100644 --- a/Code/CoreData/NSManagedObject+ActiveRecord.m +++ b/Code/CoreData/NSManagedObject+ActiveRecord.m @@ -13,6 +13,7 @@ #import "RKManagedObjectStore.h" #import "RKLog.h" #import "RKFixCategoryBug.h" +#import "NSEntityDescription+RKAdditions.h" // Set Logging Component #undef RKLogComponent @@ -136,6 +137,21 @@ - (BOOL)isNew { return [vals count] == 0; } ++ (id)findByPrimaryKey:(id)primaryKeyValue inContext:(NSManagedObjectContext *)context { + NSEntityDescription *entity = [self entityDescriptionInContext:context]; + NSString *primaryKeyAttribute = entity.primaryKeyAttribute; + if (! primaryKeyAttribute) { + RKLogWarning(@"Attempt to findByPrimaryKey for entity with nil primaryKeyAttribute. Set the primaryKeyAttribute and try again! %@", entity); + return nil; + } + + return [self findFirstByAttribute:primaryKeyAttribute withValue:primaryKeyValue inContext:context]; +} + ++ (id)findByPrimaryKey:(id)primaryKeyValue { + return [self findByPrimaryKey:primaryKeyValue inContext:[NSManagedObjectContext contextForCurrentThread]]; +} + #pragma mark - MagicalRecord Ported Methods + (NSManagedObjectContext*)currentContext; { diff --git a/Code/CoreData/NSManagedObject+RKAdditions.h b/Code/CoreData/NSManagedObject+RKAdditions.h index a276168d56..e8ac1112a1 100644 --- a/Code/CoreData/NSManagedObject+RKAdditions.h +++ b/Code/CoreData/NSManagedObject+RKAdditions.h @@ -10,6 +10,9 @@ @class RKManagedObjectStore, RKManagedObjectMapping; +/** + Provides extensions to NSManagedObject for various common tasks. + */ @interface NSManagedObject (RKAdditions) /** diff --git a/Code/CoreData/NSManagedObjectContext+RKAdditions.h b/Code/CoreData/NSManagedObjectContext+RKAdditions.h index 3557b655dc..7a8baa31fd 100644 --- a/Code/CoreData/NSManagedObjectContext+RKAdditions.h +++ b/Code/CoreData/NSManagedObjectContext+RKAdditions.h @@ -10,6 +10,9 @@ @class RKManagedObjectStore; +/** + Provides extensions to NSManagedObjectContext for various common tasks. + */ @interface NSManagedObjectContext (RKAdditions) /** diff --git a/Code/CoreData/RKFetchRequestManagedObjectCache.h b/Code/CoreData/RKFetchRequestManagedObjectCache.h new file mode 100644 index 0000000000..d4e007cf07 --- /dev/null +++ b/Code/CoreData/RKFetchRequestManagedObjectCache.h @@ -0,0 +1,19 @@ +// +// RKFetchRequestManagedObjectCache.h +// RestKit +// +// Created by Jeff Arena on 1/24/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import "RKManagedObjectMappingCache.h" + +/** + Provides a simple managed object cache strategy in which every request for an object + is satisfied by dispatching an NSFetchRequest against the Core Data persistent store. + Performance can be disappointing for data sets with a large amount of redundant data + being mapped and connected together, but the memory footprint stays flat. + */ +@interface RKFetchRequestManagedObjectCache : NSObject + +@end diff --git a/Code/CoreData/RKFetchRequestMappingCache.m b/Code/CoreData/RKFetchRequestManagedObjectCache.m similarity index 69% rename from Code/CoreData/RKFetchRequestMappingCache.m rename to Code/CoreData/RKFetchRequestManagedObjectCache.m index 975fda08c4..c9760ae3ac 100644 --- a/Code/CoreData/RKFetchRequestMappingCache.m +++ b/Code/CoreData/RKFetchRequestManagedObjectCache.m @@ -6,30 +6,31 @@ // Copyright (c) 2012 RestKit. All rights reserved. // -#import "RKFetchRequestMappingCache.h" +#import "RKFetchRequestManagedObjectCache.h" #import "NSManagedObject+ActiveRecord.h" +#import "NSEntityDescription+RKAdditions.h" #import "RKLog.h" // Set Logging Component #undef RKLogComponent #define RKLogComponent lcl_cRestKitCoreData -@implementation RKFetchRequestMappingCache +@implementation RKFetchRequestManagedObjectCache - (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping - andPrimaryKeyValue:(id)primaryKeyValue - inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext { + withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute + value:(id)primaryKeyValue + inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext +{ NSAssert(entity, @"Cannot find existing managed object without a target class"); - NSAssert(mapping, @"Cannot find existing managed object instance without mapping"); - NSAssert(mapping.primaryKeyAttribute, @"Cannot find existing managed object instance without mapping that defines a primaryKeyAttribute"); + NSAssert(primaryKeyAttribute, @"Cannot find existing managed object instance without mapping that defines a primaryKeyAttribute"); NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value"); NSAssert(managedObjectContext, @"Cannot find existing managed object with a context"); NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setEntity:entity]; [fetchRequest setFetchLimit:1]; - [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", mapping.primaryKeyAttribute, primaryKeyValue]]; + [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, primaryKeyValue]]; NSArray *objects = [NSManagedObject executeFetchRequest:fetchRequest]; RKLogDebug(@"Found objects '%@' using fetchRequest '%@'", objects, fetchRequest); diff --git a/Code/CoreData/RKFetchRequestMappingCache.h b/Code/CoreData/RKFetchRequestMappingCache.h deleted file mode 100644 index 5d408bc72e..0000000000 --- a/Code/CoreData/RKFetchRequestMappingCache.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// RKFetchRequestMappingCache.h -// RestKit -// -// Created by Jeff Arena on 1/24/12. -// Copyright (c) 2012 RestKit. All rights reserved. -// - -#import "RKManagedObjectMappingCache.h" - -@interface RKFetchRequestMappingCache : NSObject - -@end diff --git a/Code/CoreData/RKInMemoryEntityCache.h b/Code/CoreData/RKInMemoryEntityCache.h index 2f53a75bcd..1987c2b93d 100644 --- a/Code/CoreData/RKInMemoryEntityCache.h +++ b/Code/CoreData/RKInMemoryEntityCache.h @@ -7,62 +7,77 @@ // #import -#import "RKManagedObjectMapping.h" -@interface RKInMemoryEntityCache : NSObject { - NSMutableDictionary *_entityCache; -} +/** + Instances of RKInMemoryEntityCache provide an in-memory caching mechanism for + objects in a Core Data managed object context. Managed objects can be cached by + attribute for fast retrieval without repeatedly hitting the Core Data persistent store. + This can provide a substantial speed advantage over issuing fetch requests + in cases where repeated look-ups of the same data are performed using a small set + of attributes as the query key. Internally, the cache entries are maintained as + references to the NSManagedObjectID of corresponding cached objects. + */ +@interface RKInMemoryEntityCache : NSObject + +/** + The managed object context from which objects will be cached. + */ +@property(nonatomic, readonly) NSManagedObjectContext *managedObjectContext; + +/// @name Initializing the Cache + +/** + Initializes the receiver with a managed object context containing the entity instances to be cached. -@property (nonatomic, readonly) NSDictionary *entityCache; + @param context The managed object context containing objects to be cached. + @returns self, initialized with context. + */ +- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext; + +/// @name Cacheing Objects by Attribute /** + Retrieves all objects within the cache */ - (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping + byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext; /** */ - (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping - andPrimaryKeyValue:(id)primaryKeyValue + withAttribute:(NSString *)attributeName + value:(id)attributeValue inContext:(NSManagedObjectContext *)managedObjectContext; /** + Caches all instances of an entity in a given managed object context by the value */ - (void)cacheObjectsForEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping + byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext; /** */ - (void)cacheObject:(NSManagedObject *)managedObject - withMapping:(RKManagedObjectMapping *)mapping + byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext; /** */ - (void)cacheObject:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping - andPrimaryKeyValue:(id)primaryKeyValue + byAttribute:(NSString *)attributeName + value:(id)primaryKeyValue inContext:(NSManagedObjectContext *)managedObjectContext; /** */ - (void)expireCacheEntryForObject:(NSManagedObject *)managedObject - withMapping:(RKManagedObjectMapping *)mapping + byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext; /** */ -- (void)expireCacheEntryForEntity:(NSEntityDescription *)entity; - -/** - */ -- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity; - -/** - */ -- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext; +- (void)expireCacheEntriesForEntity:(NSEntityDescription *)entity; @end diff --git a/Code/CoreData/RKInMemoryEntityCache.m b/Code/CoreData/RKInMemoryEntityCache.m index feab26ddb7..d28bd354da 100644 --- a/Code/CoreData/RKInMemoryEntityCache.m +++ b/Code/CoreData/RKInMemoryEntityCache.m @@ -20,9 +20,20 @@ #undef RKLogComponent #define RKLogComponent lcl_cRestKitCoreData +@interface RKInMemoryEntityCache () +@property(nonatomic, retain) NSMutableArray *attributeCaches; + +@property(nonatomic, retain) NSMutableDictionary *entityCache; + +- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity; +- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext; +@end + @implementation RKInMemoryEntityCache @synthesize entityCache = _entityCache; +@synthesize managedObjectContext = _managedObjectContext; +@synthesize attributeCaches = _attributeCaches; - (id)init { self = [super init]; @@ -38,9 +49,22 @@ - (id)init { return self; } +- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext +{ + self = [self init]; + if (self) { + _managedObjectContext = [managedObjectContext retain]; + } + + return self; +} + - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [_entityCache release]; + [_managedObjectContext release]; + [_attributeCaches release]; + [super dealloc]; } @@ -52,36 +76,36 @@ - (void)didReceiveMemoryWarning { } - (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping + byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext { NSAssert(entity, @"Cannot retrieve cached objects without an entity"); - NSAssert(mapping, @"Cannot retrieve cached objects without a mapping"); + NSAssert(attributeName, @"Cannot retrieve cached objects without an attributeName"); NSAssert(managedObjectContext, @"Cannot retrieve cached objects without a managedObjectContext"); NSMutableDictionary *cachedObjectsForEntity = [_entityCache objectForKey:entity.name]; if (cachedObjectsForEntity == nil) { - [self cacheObjectsForEntity:entity withMapping:mapping inContext:managedObjectContext]; + [self cacheObjectsForEntity:entity byAttribute:attributeName inContext:managedObjectContext]; cachedObjectsForEntity = [_entityCache objectForKey:entity.name]; } return cachedObjectsForEntity; } - (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping - andPrimaryKeyValue:(id)primaryKeyValue + withAttribute:(NSString *)attributeName + value:(id)attributeValue inContext:(NSManagedObjectContext *)managedObjectContext { NSAssert(entity, @"Cannot retrieve a cached object without an entity"); - NSAssert(mapping, @"Cannot retrieve a cached object without a mapping"); - NSAssert(primaryKeyValue, @"Cannot retrieve a cached object without a primaryKeyValue"); + NSAssert(attributeName, @"Cannot retrieve a cached object without a mapping"); + NSAssert(attributeValue, @"Cannot retrieve a cached object without a primaryKeyValue"); NSAssert(managedObjectContext, @"Cannot retrieve a cached object without a managedObjectContext"); NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity - withMapping:mapping + byAttribute:attributeName inContext:managedObjectContext]; // NOTE: We coerce the primary key into a string (if possible) for convenience. Generally // primary keys are expressed either as a number of a string, so this lets us support either case interchangeably - id lookupValue = [primaryKeyValue respondsToSelector:@selector(stringValue)] ? [primaryKeyValue stringValue] : primaryKeyValue; + id lookupValue = [attributeValue respondsToSelector:@selector(stringValue)] ? [attributeValue stringValue] : attributeValue; NSManagedObjectID *objectID = [cachedObjectsForEntity objectForKey:lookupValue]; NSManagedObject *object = nil; if (objectID) { @@ -91,10 +115,10 @@ - (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity } - (void)cacheObjectsForEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping + byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext { NSAssert(entity, @"Cannot cache objects without an entity"); - NSAssert(mapping, @"Cannot cache objects without a mapping"); + NSAssert(attributeName, @"Cannot cache objects without an attributeName"); NSAssert(managedObjectContext, @"Cannot cache objects without a managedObjectContext"); NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; @@ -107,10 +131,10 @@ - (void)cacheObjectsForEntity:(NSEntityDescription *)entity RKLogInfo(@"Caching all %ld %@ objectsIDs to thread local storage", (long) [objectIds count], entity.name); NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; if ([objectIds count] > 0) { - BOOL coerceToString = [self shouldCoerceAttributeToString:mapping.primaryKeyAttribute forEntity:entity]; + BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity]; for (NSManagedObjectID* theObjectID in objectIds) { NSManagedObject* theObject = [self objectWithID:theObjectID inContext:managedObjectContext]; - id attributeValue = [theObject valueForKey:mapping.primaryKeyAttribute]; + id attributeValue = [theObject valueForKey:attributeName]; // Coerce to a string if possible attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue; if (attributeValue) { @@ -126,21 +150,21 @@ - (void)cacheObjectsForEntity:(NSEntityDescription *)entity object:managedObjectContext]; } -- (void)cacheObject:(NSManagedObject *)managedObject withMapping:(RKManagedObjectMapping *)mapping inContext:(NSManagedObjectContext *)managedObjectContext { +- (void)cacheObject:(NSManagedObject *)managedObject byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext { NSAssert(managedObject, @"Cannot cache an object without a managedObject"); - NSAssert(mapping, @"Cannot cache an object without a mapping"); + NSAssert(attributeName, @"Cannot cache an object without a mapping"); NSAssert(managedObjectContext, @"Cannot cache an object without a managedObjectContext"); NSManagedObjectID *objectID = [managedObject objectID]; if (objectID) { NSEntityDescription *entity = managedObject.entity; - BOOL coerceToString = [self shouldCoerceAttributeToString:mapping.primaryKeyAttribute forEntity:entity]; - id attributeValue = [managedObject valueForKey:mapping.primaryKeyAttribute]; + BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity]; + id attributeValue = [managedObject valueForKey:attributeName]; // Coerce to a string if possible attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue; if (attributeValue) { NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity - withMapping:mapping + byAttribute:attributeName inContext:managedObjectContext]; [cachedObjectsForEntity setObject:objectID forKey:attributeValue]; } @@ -152,19 +176,19 @@ - (void)cacheObject:(NSManagedObject *)managedObject withMapping:(RKManagedObjec object:managedObjectContext]; } -- (void)cacheObject:(NSEntityDescription *)entity withMapping:(RKManagedObjectMapping *)mapping andPrimaryKeyValue:(id)primaryKeyValue inContext:(NSManagedObjectContext *)managedObjectContext { +- (void)cacheObject:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)managedObjectContext { NSAssert(entity, @"Cannot cache an object without an entity"); - NSAssert(mapping, @"Cannot cache an object without a mapping"); + NSAssert(attributeName, @"Cannot cache an object without a mapping"); NSAssert(managedObjectContext, @"Cannot cache an object without a managedObjectContext"); // NOTE: We coerce the primary key into a string (if possible) for convenience. Generally // primary keys are expressed either as a number or a string, so this lets us support either case interchangeably - id lookupValue = [primaryKeyValue respondsToSelector:@selector(stringValue)] ? [primaryKeyValue stringValue] : primaryKeyValue; + id lookupValue = [attributeValue respondsToSelector:@selector(stringValue)] ? [attributeValue stringValue] : attributeValue; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setEntity:entity]; [fetchRequest setFetchLimit:1]; - [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", mapping.primaryKeyAttribute, lookupValue]]; + [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", attributeName, lookupValue]]; [fetchRequest setResultType:NSManagedObjectIDResultType]; NSArray *objectIds = [NSManagedObject executeFetchRequest:fetchRequest inContext:managedObjectContext]; @@ -175,7 +199,7 @@ - (void)cacheObject:(NSEntityDescription *)entity withMapping:(RKManagedObjectMa objectID = [objectIds objectAtIndex:0]; if (objectID && lookupValue) { NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity - withMapping:mapping + byAttribute:attributeName inContext:managedObjectContext]; [cachedObjectsForEntity setObject:objectID forKey:lookupValue]; } @@ -187,29 +211,29 @@ - (void)cacheObject:(NSEntityDescription *)entity withMapping:(RKManagedObjectMa object:managedObjectContext]; } -- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject withMapping:(RKManagedObjectMapping *)mapping inContext:(NSManagedObjectContext *)managedObjectContext { +- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext { NSAssert(managedObject, @"Cannot expire cache entry for an object without a managedObject"); - NSAssert(mapping, @"Cannot expire cache entry for an object without a mapping"); + NSAssert(attributeName, @"Cannot expire cache entry for an object without a mapping"); NSAssert(managedObjectContext, @"Cannot expire cache entry for an object without a managedObjectContext"); NSEntityDescription *entity = managedObject.entity; - BOOL coerceToString = [self shouldCoerceAttributeToString:mapping.primaryKeyAttribute forEntity:entity]; - id attributeValue = [managedObject valueForKey:mapping.primaryKeyAttribute]; + BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity]; + id attributeValue = [managedObject valueForKey:attributeName]; // Coerce to a string if possible attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue; if (attributeValue) { NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity - withMapping:mapping + byAttribute:attributeName inContext:managedObjectContext]; [cachedObjectsForEntity removeObjectForKey:attributeValue]; if ([cachedObjectsForEntity count] == 0) { - [self expireCacheEntryForEntity:entity]; + [self expireCacheEntriesForEntity:entity]; } } } -- (void)expireCacheEntryForEntity:(NSEntityDescription *)entity { +- (void)expireCacheEntriesForEntity:(NSEntityDescription *)entity { NSAssert(entity, @"Cannot expire cache entry for an entity without an entity"); RKLogTrace(@"About to expire cache for entity name=%@", entity.name); [_entityCache removeObjectForKey:entity.name]; @@ -228,11 +252,17 @@ - (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSMan NSAssert(managedObjectContext, @"Cannot fetch a managedObject with a nil managedObjectContext"); /* NOTE: - We use objectRegisteredForID: as opposed to objectWithID: as objectWithID: can return us a fault - that will raise an exception when fired. objectRegisteredForID: will return nil if the ID has been - deleted. existingObjectWithID:error: is also an acceptable approach. + We use existingObjectWithID: as opposed to objectWithID: as objectWithID: can return us a fault + that will raise an exception when fired. existingObjectWithID:error: will return nil if the ID has been + deleted. objectRegisteredForID: is also an acceptable approach. */ - return [managedObjectContext objectRegisteredForID:objectID]; + NSError *error = nil; + NSManagedObject *object = [managedObjectContext existingObjectWithID:objectID error:&error]; + if (! object && error) { + RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error); + } + + return object; } @@ -254,7 +284,7 @@ - (void)objectsDidChange:(NSNotification *)notification { } for (NSEntityDescription *entity in entitiesToExpire) { - [self expireCacheEntryForEntity:entity]; + [self expireCacheEntriesForEntity:entity]; } } diff --git a/Code/CoreData/RKInMemoryManagedObjectCache.h b/Code/CoreData/RKInMemoryManagedObjectCache.h new file mode 100644 index 0000000000..88b360255b --- /dev/null +++ b/Code/CoreData/RKInMemoryManagedObjectCache.h @@ -0,0 +1,19 @@ +// +// RKInMemoryManagedObjectCache.h +// RestKit +// +// Created by Jeff Arena on 1/24/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import "RKManagedObjectMappingCache.h" +#import "RKInMemoryEntityCache.h" + +/** + Provides a fast managed object cache where-in object instances are retained in + memory to avoid hitting the Core Data persistent store. Performance is greatly + increased over fetch request based strategy at the expense of memory consumption. + */ +@interface RKInMemoryManagedObjectCache : NSObject + +@end diff --git a/Code/CoreData/RKInMemoryManagedObjectCache.m b/Code/CoreData/RKInMemoryManagedObjectCache.m new file mode 100644 index 0000000000..360b6ae421 --- /dev/null +++ b/Code/CoreData/RKInMemoryManagedObjectCache.m @@ -0,0 +1,37 @@ +// +// RKInMemoryManagedObjectCache.m +// RestKit +// +// Created by Jeff Arena on 1/24/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import "RKInMemoryManagedObjectCache.h" +#import "RKLog.h" + +// Set Logging Component +#undef RKLogComponent +#define RKLogComponent lcl_cRestKitCoreData + +static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey = @"RKInMemoryObjectManagedObjectCacheThreadDictionaryKey"; + +@implementation RKInMemoryManagedObjectCache + +- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity + withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute + value:(id)primaryKeyValue + inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext { + NSAssert(entity, @"Cannot find existing managed object without a target class"); + NSAssert(primaryKeyAttribute, @"Cannot find existing managed object instance without mapping"); + NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value"); + NSAssert(managedObjectContext, @"Cannot find existing managed object with a context"); + RKInMemoryEntityCache *cache = [[[NSThread currentThread] threadDictionary] objectForKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey]; + if (! cache) { + cache = [[RKInMemoryEntityCache alloc] initWithManagedObjectContext:managedObjectContext]; + [[[NSThread currentThread] threadDictionary] setObject:cache forKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey]; + [cache release]; + } + return [cache cachedObjectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue inContext:managedObjectContext]; +} + +@end diff --git a/Code/CoreData/RKInMemoryMappingCache.h b/Code/CoreData/RKInMemoryMappingCache.h deleted file mode 100644 index 6ff25f0f99..0000000000 --- a/Code/CoreData/RKInMemoryMappingCache.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// RKInMemoryMappingCache.h -// RestKit -// -// Created by Jeff Arena on 1/24/12. -// Copyright (c) 2012 RestKit. All rights reserved. -// - -#import "RKManagedObjectMappingCache.h" -#import "RKInMemoryEntityCache.h" - -@interface RKInMemoryMappingCache : NSObject - -@property (nonatomic, readonly) RKInMemoryEntityCache *cache; - -@end diff --git a/Code/CoreData/RKInMemoryMappingCache.m b/Code/CoreData/RKInMemoryMappingCache.m deleted file mode 100644 index 82f3e1ce11..0000000000 --- a/Code/CoreData/RKInMemoryMappingCache.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// RKInMemoryMappingCache.m -// RestKit -// -// Created by Jeff Arena on 1/24/12. -// Copyright (c) 2012 RestKit. All rights reserved. -// - -#import "RKInMemoryMappingCache.h" -#import "RKLog.h" - -// Set Logging Component -#undef RKLogComponent -#define RKLogComponent lcl_cRestKitCoreData - -static NSString* const RKManagedObjectStoreThreadDictionaryEntityCacheKey = @"RKManagedObjectStoreThreadDictionaryEntityCacheKey"; - -@implementation RKInMemoryMappingCache - -@synthesize cache = _cache; - -- (id)init { - self = [super init]; - if (self) { - _cache = [[RKInMemoryEntityCache alloc] init]; - } - return self; -} - -- (void)dealloc { - [_cache release]; - _cache = nil; - [super dealloc]; -} - -- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping - andPrimaryKeyValue:(id)primaryKeyValue - inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext { - NSAssert(entity, @"Cannot find existing managed object without a target class"); - NSAssert(mapping, @"Cannot find existing managed object instance without mapping"); - NSAssert(mapping.primaryKeyAttribute, @"Cannot find existing managed object instance without mapping that defines a primaryKeyAttribute"); - NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value"); - NSAssert(managedObjectContext, @"Cannot find existing managed object with a context"); - return [_cache cachedObjectForEntity:entity withMapping:mapping andPrimaryKeyValue:primaryKeyValue inContext:managedObjectContext]; -} - -@end diff --git a/Code/CoreData/RKManagedObjectMapping.h b/Code/CoreData/RKManagedObjectMapping.h index b41a3788f2..1fe04dcc5f 100644 --- a/Code/CoreData/RKManagedObjectMapping.h +++ b/Code/CoreData/RKManagedObjectMapping.h @@ -56,9 +56,20 @@ @property (nonatomic, readonly) NSEntityDescription *entity; /** - The attribute containing the primary key value for the class. This is consulted by - RestKit to uniquely identify objects within the store using the primary key in your - remote backend system. + The name of the attribute on the destination entity that acts as the primary key for instances + of the entity in the remote backend system. Used to uniquely identify objects within the store + so that existing objects are updated rather than creating new ones. + + @warning Note that primaryKeyAttribute defaults to the primaryKeyAttribute configured + on the NSEntityDescription for the entity targetted by the receiving mapping. This provides + flexibility in cases where a single entity is the target of many mappings with differing + primary key definitions. + + If the primaryKeyAttribute is set on an RKManagedObjectMapping that targets an entity with a + nil primaryKeyAttribute, then the primaryKeyAttribute will be set on the entity as well for + convenience and backwards compatibility. This may change in the future. + + @see [NSEntityDescription primaryKeyAttribute] */ @property (nonatomic, retain) NSString *primaryKeyAttribute; diff --git a/Code/CoreData/RKManagedObjectMapping.m b/Code/CoreData/RKManagedObjectMapping.m index 6f9757acb0..6bbf6077c8 100644 --- a/Code/CoreData/RKManagedObjectMapping.m +++ b/Code/CoreData/RKManagedObjectMapping.m @@ -23,6 +23,7 @@ #import "RKManagedObjectStore.h" #import "RKDynamicObjectMappingMatcher.h" #import "RKObjectPropertyInspector+CoreData.h" +#import "NSEntityDescription+RKAdditions.h" #import "RKLog.h" // Set Logging Component @@ -62,6 +63,9 @@ - (id)initWithEntity:(NSEntityDescription*)entity inManagedObjectStore:(RKManage self.objectClass = NSClassFromString([entity managedObjectClassName]); _entity = [entity retain]; _objectStore = objectStore; + + [self addObserver:self forKeyPath:@"entity" options:NSKeyValueObservingOptionInitial context:nil]; + [self addObserver:self forKeyPath:@"primaryKeyAttribute" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; } return self; @@ -77,6 +81,9 @@ - (id)init { } - (void)dealloc { + [self removeObserver:self forKeyPath:@"entity"]; + [self removeObserver:self forKeyPath:@"primaryKeyAttribute"]; + [_entity release]; [_relationshipToPrimaryKeyMappings release]; [super dealloc]; @@ -158,9 +165,7 @@ - (id)mappableObjectForData:(id)mappableData { // If we have found the primary key attribute & value, try to find an existing instance to update if (primaryKeyAttribute && primaryKeyValue) { object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity - withMapping:self - andPrimaryKeyValue:primaryKeyValue - inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]]; + withPrimaryKeyAttribute:self.primaryKeyAttribute value:primaryKeyValue inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]]; } if (object == nil) { @@ -179,4 +184,19 @@ - (Class)classForProperty:(NSString*)propertyName { return propertyClass; } +/* + Allows the primaryKeyAttribute property on the NSEntityDescription to configure the mapping and vice-versa + */ +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:@"entity"]) { + if (! self.primaryKeyAttribute) { + self.primaryKeyAttribute = [self.entity primaryKeyAttribute]; + } + } else if ([keyPath isEqualToString:@"primaryKeyAttribute"]) { + if (! self.entity.primaryKeyAttribute) { + self.entity.primaryKeyAttribute = self.primaryKeyAttribute; + } + } +} @end diff --git a/Code/CoreData/RKManagedObjectMappingCache.h b/Code/CoreData/RKManagedObjectMappingCache.h index 26f5808958..2fa3a5bc25 100644 --- a/Code/CoreData/RKManagedObjectMappingCache.h +++ b/Code/CoreData/RKManagedObjectMappingCache.h @@ -1,5 +1,5 @@ // -// RKManagedObjectMappingCache.h +// RKManagedObjectCacheing.h // RestKit // // Created by Jeff Arena on 1/24/12. @@ -7,17 +7,29 @@ // #import -#import "RKManagedObjectMapping.h" -@protocol RKManagedObjectMappingCache +/** + Objects implementing the RKManagedObjectCacheing protocol can act as the cache + strategy for RestKit managed object stores. The managed object cache is consulted + when objects are retrieved from Core Data during object mapping operations and provide + an opportunity to accelerate the mapping process by trading memory for speed. + */ +@protocol RKManagedObjectCacheing /** - * Retrieves a model object from the object store given a Core Data entity and - * the primary key attribute and value for the desired object. + Retrieves a model object from the object store given a Core Data entity and + the primary key attribute and value for the desired object. + + @param entity The Core Data entity for the type of object to be retrieved from the cache. + @param primaryKeyAttribute The name of the attribute that acts as the primary key for the entity. + @param primaryKeyValue The value for the primary key attribute of the object to be retrieved from the cache. + @param mmanagedObjectContext The managed object context to be searched for a matching instance. + @return A managed object that is an instance of the given entity with a primary key and value matching + the specified parameters, or nil if no object was found. */ - (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity - withMapping:(RKManagedObjectMapping *)mapping - andPrimaryKeyValue:(id)primaryKeyValue + withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute + value:(id)primaryKeyValue inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext; @end diff --git a/Code/CoreData/RKManagedObjectStore.h b/Code/CoreData/RKManagedObjectStore.h index 2d0cad9b92..92c5209cbb 100644 --- a/Code/CoreData/RKManagedObjectStore.h +++ b/Code/CoreData/RKManagedObjectStore.h @@ -81,7 +81,7 @@ extern NSString* const RKManagedObjectStoreDidFailSaveNotification; /** */ -@property (nonatomic, retain) NSObject *cacheStrategy; +@property (nonatomic, retain) NSObject *cacheStrategy; /** * Initialize a new managed object store with a SQLite database with the filename specified diff --git a/Code/CoreData/RKManagedObjectStore.m b/Code/CoreData/RKManagedObjectStore.m index eaa6874443..48e39639ad 100644 --- a/Code/CoreData/RKManagedObjectStore.m +++ b/Code/CoreData/RKManagedObjectStore.m @@ -26,8 +26,8 @@ #import "RKObjectPropertyInspector+CoreData.h" #import "RKAlert.h" #import "RKDirectory.h" -#import "RKInMemoryMappingCache.h" -#import "RKFetchRequestMappingCache.h" +#import "RKInMemoryManagedObjectCache.h" +#import "RKFetchRequestManagedObjectCache.h" #import "NSBundle+RKAdditions.h" #import "NSManagedObjectContext+RKAdditions.h" @@ -118,7 +118,7 @@ - (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)ni self.primaryManagedObjectContext = [self newManagedObjectContext]; - _cacheStrategy = [[RKFetchRequestMappingCache alloc] init]; + _cacheStrategy = [[RKFetchRequestManagedObjectCache alloc] init]; // Ensure there is a search word observer [RKSearchWordObserver sharedObserver]; diff --git a/Code/ObjectMapping/RKDynamicObjectMapping.h b/Code/ObjectMapping/RKDynamicObjectMapping.h index 6d51b9af66..f303c75f2e 100644 --- a/Code/ObjectMapping/RKDynamicObjectMapping.h +++ b/Code/ObjectMapping/RKDynamicObjectMapping.h @@ -27,12 +27,12 @@ @protocol RKDynamicObjectMappingDelegate @required -- (RKObjectMapping*)objectMappingForData:(id)data; +- (RKObjectMapping *)objectMappingForData:(id)data; @end #ifdef NS_BLOCKS_AVAILABLE -typedef RKObjectMapping*(^RKDynamicObjectMappingDelegateBlock)(id); +typedef RKObjectMapping *(^RKDynamicObjectMappingDelegateBlock)(id); #endif /** diff --git a/Code/ObjectMapping/RKObjectManager.h b/Code/ObjectMapping/RKObjectManager.h index be8a2dce02..68f8193e4a 100644 --- a/Code/ObjectMapping/RKObjectManager.h +++ b/Code/ObjectMapping/RKObjectManager.h @@ -366,7 +366,7 @@ typedef enum { For example: - (void)loadObjectUsingBlockExample { - [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/monkeys.json" delegate:self block:^(RKObjectLoader* loader) { + [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/monkeys.json" usingBlock:^(RKObjectLoader* loader) { loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Monkey class]]; }]; } diff --git a/RestKit.xcodeproj/project.pbxproj b/RestKit.xcodeproj/project.pbxproj index b339a6d453..61441df609 100644 --- a/RestKit.xcodeproj/project.pbxproj +++ b/RestKit.xcodeproj/project.pbxproj @@ -20,6 +20,12 @@ 25055B9114EEF40000B9C4DD /* RKMappingTestExpectation.m in Sources */ = {isa = PBXBuildFile; fileRef = 25055B8E14EEF40000B9C4DD /* RKMappingTestExpectation.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 25055B9214EEF40000B9C4DD /* RKMappingTestExpectation.m in Sources */ = {isa = PBXBuildFile; fileRef = 25055B8E14EEF40000B9C4DD /* RKMappingTestExpectation.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 25055B9314EEFEC800B9C4DD /* libRestKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 25160D1614564E810060A5C5 /* libRestKit.a */; }; + 25079C6F151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25079C70151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25079C71151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */; }; + 25079C72151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */; }; + 25079C76151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */; }; + 25079C77151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */; }; 250CA67D147D8E8B0047D347 /* OCHamcrest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 250CA67B147D8E800047D347 /* OCHamcrest.framework */; }; 250CA67E147D8E8F0047D347 /* OCHamcrestIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 250CA67C147D8E800047D347 /* OCHamcrestIOS.framework */; }; 250CA680147D8F050047D347 /* OCHamcrest.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 250CA67B147D8E800047D347 /* OCHamcrest.framework */; }; @@ -632,6 +638,8 @@ 25CA7A9114EC5C2D00888FF8 /* RKTestFixture.m in Sources */ = {isa = PBXBuildFile; fileRef = 252EFB2114D9B35D004863C8 /* RKTestFixture.m */; }; 25CAAA9415254E7800CAE5D7 /* ArrayOfHumans.json in Resources */ = {isa = PBXBuildFile; fileRef = 25CAAA9315254E7800CAE5D7 /* ArrayOfHumans.json */; }; 25CAAA9515254E7800CAE5D7 /* ArrayOfHumans.json in Resources */ = {isa = PBXBuildFile; fileRef = 25CAAA9315254E7800CAE5D7 /* ArrayOfHumans.json */; }; + 25DB7508151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */; }; + 25DB7509151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */; }; 25E36E0215195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */; }; 25E36E0315195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */; }; 25EC1A2C14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.h in Headers */ = {isa = PBXBuildFile; fileRef = 25EC1A2A14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -642,14 +650,14 @@ 25EC1A3614F72AF100C3CF3F /* RKInMemoryEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */; }; 25EC1A3714F72B0100C3CF3F /* RKInMemoryEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25EC1A3814F72B0200C3CF3F /* RKInMemoryEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25EC1A3914F72B0900C3CF3F /* RKFetchRequestMappingCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3814CF168C00CE7BCE /* RKFetchRequestMappingCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25EC1A3A14F72B0A00C3CF3F /* RKFetchRequestMappingCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3814CF168C00CE7BCE /* RKFetchRequestMappingCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25EC1A3B14F72B1300C3CF3F /* RKFetchRequestMappingCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3914CF168C00CE7BCE /* RKFetchRequestMappingCache.m */; }; - 25EC1A3C14F72B1400C3CF3F /* RKFetchRequestMappingCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3914CF168C00CE7BCE /* RKFetchRequestMappingCache.m */; }; - 25EC1A3D14F72B2800C3CF3F /* RKInMemoryMappingCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3C14CF19F200CE7BCE /* RKInMemoryMappingCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25EC1A3E14F72B2900C3CF3F /* RKInMemoryMappingCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3C14CF19F200CE7BCE /* RKInMemoryMappingCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 25EC1A3F14F72B3100C3CF3F /* RKInMemoryMappingCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3D14CF19F200CE7BCE /* RKInMemoryMappingCache.m */; }; - 25EC1A4014F72B3300C3CF3F /* RKInMemoryMappingCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3D14CF19F200CE7BCE /* RKInMemoryMappingCache.m */; }; + 25EC1A3914F72B0900C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25EC1A3A14F72B0A00C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25EC1A3B14F72B1300C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */; }; + 25EC1A3C14F72B1400C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */; }; + 25EC1A3D14F72B2800C3CF3F /* RKInMemoryManagedObjectCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25EC1A3E14F72B2900C3CF3F /* RKInMemoryManagedObjectCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25EC1A3F14F72B3100C3CF3F /* RKInMemoryManagedObjectCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */; }; + 25EC1A4014F72B3300C3CF3F /* RKInMemoryManagedObjectCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */; }; 25EC1A4114F72C7200C3CF3F /* RKObjectMappingProvider+CoreData.h in Headers */ = {isa = PBXBuildFile; fileRef = 73DA8E1A14D1BA960054DD73 /* RKObjectMappingProvider+CoreData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25EC1A4214F72C7300C3CF3F /* RKObjectMappingProvider+CoreData.h in Headers */ = {isa = PBXBuildFile; fileRef = 73DA8E1A14D1BA960054DD73 /* RKObjectMappingProvider+CoreData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25EC1A4314F72D0D00C3CF3F /* RKObjectMappingProvider+CoreData.m in Sources */ = {isa = PBXBuildFile; fileRef = 73DA8E1B14D1BA960054DD73 /* RKObjectMappingProvider+CoreData.m */; }; @@ -742,6 +750,9 @@ 25055B8314EEF32A00B9C4DD /* RKTestFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RKTestFactory.m; path = Testing/RKTestFactory.m; sourceTree = ""; }; 25055B8D14EEF40000B9C4DD /* RKMappingTestExpectation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RKMappingTestExpectation.h; path = Testing/RKMappingTestExpectation.h; sourceTree = ""; }; 25055B8E14EEF40000B9C4DD /* RKMappingTestExpectation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RKMappingTestExpectation.m; path = Testing/RKMappingTestExpectation.m; sourceTree = ""; }; + 25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEntityDescription+RKAdditions.h"; sourceTree = ""; }; + 25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEntityDescription+RKAdditions.m"; sourceTree = ""; }; + 25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEntityDescription+RKAdditionsTest.m"; sourceTree = ""; }; 250CA67B147D8E800047D347 /* OCHamcrest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrest.framework; sourceTree = ""; }; 250CA67C147D8E800047D347 /* OCHamcrestIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrestIOS.framework; sourceTree = ""; }; 250DF22814C5190E0001DEFA /* RKOrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKOrderedDictionary.h; sourceTree = ""; }; @@ -1123,6 +1134,7 @@ 25B6EA0714CF947D00B1E881 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 25CA7A8E14EC570100888FF8 /* RKObjectMappingDefinition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectMappingDefinition.m; sourceTree = ""; }; 25CAAA9315254E7800CAE5D7 /* ArrayOfHumans.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ArrayOfHumans.json; sourceTree = ""; }; + 25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+ActiveRecordTest.m"; sourceTree = ""; }; 25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestMappingCacheTest.m; sourceTree = ""; }; 25EC1A2A14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RKObjectManager+RKTableController.h"; sourceTree = ""; }; 25EC1A2B14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RKObjectManager+RKTableController.m"; sourceTree = ""; }; @@ -1162,10 +1174,10 @@ 49D275AB14C9F3020090845D /* RKISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKISO8601DateFormatter.h; sourceTree = ""; }; 49D275AC14C9F3020090845D /* RKISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKISO8601DateFormatter.m; sourceTree = ""; }; 7394DF3514CF157A00CE7BCE /* RKManagedObjectMappingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKManagedObjectMappingCache.h; sourceTree = ""; }; - 7394DF3814CF168C00CE7BCE /* RKFetchRequestMappingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKFetchRequestMappingCache.h; sourceTree = ""; }; - 7394DF3914CF168C00CE7BCE /* RKFetchRequestMappingCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestMappingCache.m; sourceTree = ""; }; - 7394DF3C14CF19F200CE7BCE /* RKInMemoryMappingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryMappingCache.h; sourceTree = ""; }; - 7394DF3D14CF19F200CE7BCE /* RKInMemoryMappingCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryMappingCache.m; sourceTree = ""; }; + 7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKFetchRequestManagedObjectCache.h; sourceTree = ""; }; + 7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestManagedObjectCache.m; sourceTree = ""; }; + 7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryManagedObjectCache.h; sourceTree = ""; }; + 7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryManagedObjectCache.m; sourceTree = ""; }; 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryEntityCache.h; sourceTree = ""; }; 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryEntityCache.m; sourceTree = ""; }; 73D3907114CA19F90093E3D6 /* parent.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = parent.json; sourceTree = ""; }; @@ -1326,12 +1338,12 @@ 25160D46145650490060A5C5 /* CoreData.h */, 25160D47145650490060A5C5 /* NSManagedObject+ActiveRecord.h */, 25160D48145650490060A5C5 /* NSManagedObject+ActiveRecord.m */, - 7394DF3814CF168C00CE7BCE /* RKFetchRequestMappingCache.h */, - 7394DF3914CF168C00CE7BCE /* RKFetchRequestMappingCache.m */, + 7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */, + 7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */, 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */, 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */, - 7394DF3C14CF19F200CE7BCE /* RKInMemoryMappingCache.h */, - 7394DF3D14CF19F200CE7BCE /* RKInMemoryMappingCache.m */, + 7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */, + 7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */, 25160D4A145650490060A5C5 /* RKManagedObjectLoader.h */, 25160D4B145650490060A5C5 /* RKManagedObjectLoader.m */, 25160D4C145650490060A5C5 /* RKManagedObjectMapping.h */, @@ -1353,6 +1365,8 @@ 257ABAAF15112DD400CCAA76 /* NSManagedObjectContext+RKAdditions.m */, 257ABAB41511371C00CCAA76 /* NSManagedObject+RKAdditions.h */, 257ABAB51511371D00CCAA76 /* NSManagedObject+RKAdditions.m */, + 25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */, + 25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */, ); path = CoreData; sourceTree = ""; @@ -1661,6 +1675,8 @@ 25160FCB1456F2330060A5C5 /* RKManagedObjectStoreTest.m */, 25160FCC1456F2330060A5C5 /* RKManagedObjectThreadSafeInvocationTest.m */, 25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */, + 25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */, + 25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */, ); name = CoreData; path = Logic/CoreData; @@ -2191,8 +2207,8 @@ 25055B8F14EEF40000B9C4DD /* RKMappingTestExpectation.h in Headers */, 25EC1A2C14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.h in Headers */, 25EC1A3714F72B0100C3CF3F /* RKInMemoryEntityCache.h in Headers */, - 25EC1A3914F72B0900C3CF3F /* RKFetchRequestMappingCache.h in Headers */, - 25EC1A3D14F72B2800C3CF3F /* RKInMemoryMappingCache.h in Headers */, + 25EC1A3914F72B0900C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */, + 25EC1A3D14F72B2800C3CF3F /* RKInMemoryManagedObjectCache.h in Headers */, 25EC1A4114F72C7200C3CF3F /* RKObjectMappingProvider+CoreData.h in Headers */, 25EC1A4714F7394100C3CF3F /* RKObjectMappingProviderContextEntry.h in Headers */, 25EC1A6314F7402A00C3CF3F /* RKManagedObjectMappingCache.h in Headers */, @@ -2201,6 +2217,7 @@ 25EC1B3914F84B5D00C3CF3F /* UIImage+RKAdditions.h in Headers */, 257ABAB015112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.h in Headers */, 257ABAB61511371E00CCAA76 /* NSManagedObject+RKAdditions.h in Headers */, + 25079C6F151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2311,8 +2328,8 @@ 25055B9014EEF40000B9C4DD /* RKMappingTestExpectation.h in Headers */, 25EC1A2D14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.h in Headers */, 25EC1A3814F72B0200C3CF3F /* RKInMemoryEntityCache.h in Headers */, - 25EC1A3A14F72B0A00C3CF3F /* RKFetchRequestMappingCache.h in Headers */, - 25EC1A3E14F72B2900C3CF3F /* RKInMemoryMappingCache.h in Headers */, + 25EC1A3A14F72B0A00C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */, + 25EC1A3E14F72B2900C3CF3F /* RKInMemoryManagedObjectCache.h in Headers */, 25EC1A4214F72C7300C3CF3F /* RKObjectMappingProvider+CoreData.h in Headers */, 25EC1A4814F7394200C3CF3F /* RKObjectMappingProviderContextEntry.h in Headers */, 25EC1A6514F7402A00C3CF3F /* RKManagedObjectMappingCache.h in Headers */, @@ -2321,6 +2338,7 @@ 25EC1B3A14F84B5D00C3CF3F /* UIImage+RKAdditions.h in Headers */, 257ABAB115112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.h in Headers */, 257ABAB71511371E00CCAA76 /* NSManagedObject+RKAdditions.h in Headers */, + 25079C70151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2711,8 +2729,8 @@ 25055B9114EEF40000B9C4DD /* RKMappingTestExpectation.m in Sources */, 25EC1A2E14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.m in Sources */, 25EC1A3414F72AF000C3CF3F /* RKInMemoryEntityCache.m in Sources */, - 25EC1A3B14F72B1300C3CF3F /* RKFetchRequestMappingCache.m in Sources */, - 25EC1A3F14F72B3100C3CF3F /* RKInMemoryMappingCache.m in Sources */, + 25EC1A3B14F72B1300C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */, + 25EC1A3F14F72B3100C3CF3F /* RKInMemoryManagedObjectCache.m in Sources */, 25EC1A4314F72D0D00C3CF3F /* RKObjectMappingProvider+CoreData.m in Sources */, 25EC1A4514F7393D00C3CF3F /* RKObjectMappingProviderContextEntry.m in Sources */, 25EC1ABE14F8019F00C3CF3F /* RKRefreshGestureRecognizer.m in Sources */, @@ -2720,6 +2738,7 @@ 25EC1B3B14F84B5D00C3CF3F /* UIImage+RKAdditions.m in Sources */, 257ABAB215112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.m in Sources */, 257ABAB81511371E00CCAA76 /* NSManagedObject+RKAdditions.m in Sources */, + 25079C71151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2801,6 +2820,8 @@ 252EFB0D14D98F76004863C8 /* RKMutableBlockDictionaryTest.m in Sources */, 25EC1A4A14F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m in Sources */, 25E36E0215195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */, + 25079C76151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */, + 25DB7508151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2889,14 +2910,15 @@ 25055B9214EEF40000B9C4DD /* RKMappingTestExpectation.m in Sources */, 25EC1A2F14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.m in Sources */, 25EC1A3614F72AF100C3CF3F /* RKInMemoryEntityCache.m in Sources */, - 25EC1A3C14F72B1400C3CF3F /* RKFetchRequestMappingCache.m in Sources */, - 25EC1A4014F72B3300C3CF3F /* RKInMemoryMappingCache.m in Sources */, + 25EC1A3C14F72B1400C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */, + 25EC1A4014F72B3300C3CF3F /* RKInMemoryManagedObjectCache.m in Sources */, 25EC1A4614F7393E00C3CF3F /* RKObjectMappingProviderContextEntry.m in Sources */, 25EC1ABF14F8019F00C3CF3F /* RKRefreshGestureRecognizer.m in Sources */, 25EC1AC314F8019F00C3CF3F /* RKRefreshTriggerView.m in Sources */, 25EC1B3C14F84B5D00C3CF3F /* UIImage+RKAdditions.m in Sources */, 257ABAB315112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.m in Sources */, 257ABAB91511371E00CCAA76 /* NSManagedObject+RKAdditions.m in Sources */, + 25079C72151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2978,6 +3000,8 @@ 252EFB0E14D98F76004863C8 /* RKMutableBlockDictionaryTest.m in Sources */, 25EC1A4B14F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m in Sources */, 25E36E0315195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */, + 25079C77151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */, + 25DB7509151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/Fixtures/JSON/ArrayOfHumans.json b/Tests/Fixtures/JSON/ArrayOfHumans.json index a1fc5a8c0a..cbc34c1c0f 100644 --- a/Tests/Fixtures/JSON/ArrayOfHumans.json +++ b/Tests/Fixtures/JSON/ArrayOfHumans.json @@ -2,6 +2,7 @@ { "human": { "id": 201, + "name": "Blake", "human_id": 1, "cats": [ { @@ -22,6 +23,7 @@ { "human": { "id": 202, + "name": "Sarah", "human_id": 1, "cats": [ { diff --git a/Tests/Logic/CoreData/NSEntityDescription+RKAdditionsTest.m b/Tests/Logic/CoreData/NSEntityDescription+RKAdditionsTest.m new file mode 100644 index 0000000000..dc826622ac --- /dev/null +++ b/Tests/Logic/CoreData/NSEntityDescription+RKAdditionsTest.m @@ -0,0 +1,49 @@ +// +// NSEntityDescription+RKAdditionsTest.m +// RestKit +// +// Created by Blake Watters on 3/22/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import "RKTestEnvironment.h" +#import "NSEntityDescription+RKAdditions.h" + +@interface NSEntityDescription_RKAdditionsTest : RKTestCase + +@end + +@implementation NSEntityDescription_RKAdditionsTest + +- (void)testRetrievalOfPrimaryKeyFromXcdatamodel +{ + RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKCat" inManagedObjectContext:objectStore.primaryManagedObjectContext]; + assertThat(entity.primaryKeyAttribute, is(equalTo(@"railsID"))); +} + +- (void)testRetrievalOfUnconfiguredPrimaryKeyAttributeReturnsNil +{ + RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKHuman" inManagedObjectContext:objectStore.primaryManagedObjectContext]; + assertThat(entity.primaryKeyAttribute, is(nilValue())); +} + +- (void)testSettingPrimaryKeyAttributeProgramatically +{ + RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKHouse" inManagedObjectContext:objectStore.primaryManagedObjectContext]; + entity.primaryKeyAttribute = @"houseID"; + assertThat(entity.primaryKeyAttribute, is(equalTo(@"houseID"))); +} + +- (void)testSettingExistingPrimaryKeyAttributeProgramatically +{ + RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKCat" inManagedObjectContext:objectStore.primaryManagedObjectContext]; + assertThat(entity.primaryKeyAttribute, is(equalTo(@"railsID"))); + entity.primaryKeyAttribute = @"catID"; + assertThat(entity.primaryKeyAttribute, is(equalTo(@"catID"))); +} + +@end diff --git a/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m b/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m new file mode 100644 index 0000000000..7f41ef1930 --- /dev/null +++ b/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m @@ -0,0 +1,51 @@ +// +// NSManagedObject+ActiveRecordTest.m +// RestKit +// +// Created by Blake Watters on 3/22/12. +// Copyright (c) 2012 RestKit. All rights reserved. +// + +#import "RKTestEnvironment.h" +#import "NSEntityDescription+RKAdditions.h" +#import "RKHuman.h" + +@interface NSManagedObject_ActiveRecordTest : SenTestCase + +@end + +@implementation NSManagedObject_ActiveRecordTest + +- (void)testFindByPrimaryKey +{ + RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [RKHuman entityDescription]; + entity.primaryKeyAttribute = @"railsID"; + + RKHuman *human = [RKHuman createEntity]; + human.railsID = [NSNumber numberWithInt:12345]; + [store save:nil]; + + RKHuman *foundHuman = [RKHuman findByPrimaryKey:[NSNumber numberWithInt:12345] inContext:store.primaryManagedObjectContext]; + assertThat(foundHuman, is(equalTo(human))); +} + +- (void)testFindByPrimaryKeyInContext +{ + RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; + NSManagedObjectContext *context = [[RKTestFactory managedObjectStore] newManagedObjectContext]; + NSEntityDescription *entity = [RKHuman entityDescription]; + entity.primaryKeyAttribute = @"railsID"; + + RKHuman *human = [RKHuman createInContext:context]; + human.railsID = [NSNumber numberWithInt:12345]; + [context save:nil]; + + RKHuman *foundHuman = [RKHuman findByPrimaryKey:[NSNumber numberWithInt:12345] inContext:store.primaryManagedObjectContext]; + assertThat(foundHuman, is(nilValue())); + + foundHuman = [RKHuman findByPrimaryKey:[NSNumber numberWithInt:12345] inContext:context]; + assertThat(foundHuman, is(equalTo(human))); +} + +@end diff --git a/Tests/Logic/CoreData/RKFetchRequestMappingCacheTest.m b/Tests/Logic/CoreData/RKFetchRequestMappingCacheTest.m index 6354d56c1c..fd454edce4 100644 --- a/Tests/Logic/CoreData/RKFetchRequestMappingCacheTest.m +++ b/Tests/Logic/CoreData/RKFetchRequestMappingCacheTest.m @@ -20,7 +20,7 @@ - (void)testFetchRequestMappingCacheReturnsObjectsWithNumericPrimaryKey { // RKCat entity. Integer prinmary key. RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; - RKFetchRequestMappingCache *cache = [RKFetchRequestMappingCache new]; + RKFetchRequestManagedObjectCache *cache = [RKFetchRequestManagedObjectCache new]; NSEntityDescription *entity = [RKCat entityDescription]; RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForClass:[RKCat class] inManagedObjectStore:objectStore]; mapping.primaryKeyAttribute = @"railsID"; @@ -31,8 +31,8 @@ - (void)testFetchRequestMappingCacheReturnsObjectsWithNumericPrimaryKey [objectStore.primaryManagedObjectContext save:nil]; NSManagedObject *cachedObject = [cache findInstanceOfEntity:entity - withMapping:mapping - andPrimaryKeyValue:[NSNumber numberWithInt:123456] + withPrimaryKeyAttribute:mapping.primaryKeyAttribute + value:[NSNumber numberWithInt:123456] inManagedObjectContext:objectStore.primaryManagedObjectContext]; assertThat(cachedObject, is(equalTo(reginald))); } @@ -41,7 +41,7 @@ - (void)testFetchRequestMappingCacheReturnsObjectsWithStringPrimaryKey { // RKEvent entity. String primary key RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; - RKFetchRequestMappingCache *cache = [RKFetchRequestMappingCache new]; + RKFetchRequestManagedObjectCache *cache = [RKFetchRequestManagedObjectCache new]; NSEntityDescription *entity = [RKEvent entityDescription]; RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForClass:[RKEvent class] inManagedObjectStore:objectStore]; mapping.primaryKeyAttribute = @"eventID"; @@ -49,10 +49,10 @@ - (void)testFetchRequestMappingCacheReturnsObjectsWithStringPrimaryKey RKEvent *birthday = [RKEvent createInContext:objectStore.primaryManagedObjectContext]; birthday.eventID = @"e-1234-a8-b12"; [objectStore.primaryManagedObjectContext save:nil]; - - NSManagedObject *cachedObject = [cache findInstanceOfEntity:entity - withMapping:mapping - andPrimaryKeyValue:@"e-1234-a8-b12" + + NSManagedObject *cachedObject = [cache findInstanceOfEntity:entity + withPrimaryKeyAttribute:mapping.primaryKeyAttribute + value:@"e-1234-a8-b12" inManagedObjectContext:objectStore.primaryManagedObjectContext]; assertThat(cachedObject, is(equalTo(birthday))); } diff --git a/Tests/Logic/CoreData/RKInMemoryEntityCacheTest.m b/Tests/Logic/CoreData/RKInMemoryEntityCacheTest.m index f9742af1b2..da30d36d3a 100644 --- a/Tests/Logic/CoreData/RKInMemoryEntityCacheTest.m +++ b/Tests/Logic/CoreData/RKInMemoryEntityCacheTest.m @@ -21,12 +21,26 @@ #import "RKTestEnvironment.h" #import "RKHuman.h" +@interface RKInMemoryEntityCache () +@property(nonatomic, retain) NSMutableDictionary *entityCache; + +- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity; +- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext; +@end + @interface RKInMemoryEntityCacheTest : RKTestCase @end @implementation RKInMemoryEntityCacheTest +- (void)testInitializationWithManagedObjectContext +{ + RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; + RKInMemoryEntityCache *cache = [[RKInMemoryEntityCache alloc] initWithManagedObjectContext:objectStore.primaryManagedObjectContext]; + assertThat(cache.managedObjectContext, is(equalTo(objectStore.primaryManagedObjectContext))); +} + - (void)testShouldCoercePrimaryKeysToStringsForLookup { RKManagedObjectStore* objectStore = [RKTestFactory managedObjectStore]; RKHuman* human = [RKHuman createEntity]; @@ -43,12 +57,9 @@ - (void)testShouldCacheAllObjectsForEntityWhenAccessingEntityCache { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); } @@ -59,13 +70,10 @@ - (void)testShouldCacheAllObjectsForEntityWhenAsked { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; - [entityCache cacheObjectsForEntity:human.entity withMapping:mapping inContext:objectStore.primaryManagedObjectContext]; + [entityCache cacheObjectsForEntity:human.entity byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); } @@ -76,13 +84,11 @@ - (void)testShouldRetrieveObjectsProperlyFromTheEntityCache { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSManagedObject *cachedInstance = [entityCache cachedObjectForEntity:human.entity - withMapping:mapping - andPrimaryKeyValue:[NSNumber numberWithInt:1234] inContext:objectStore.primaryManagedObjectContext]; + withAttribute:@"railsID" + value:[NSNumber numberWithInt:1234] + inContext:objectStore.primaryManagedObjectContext]; assertThat(cachedInstance, is(equalTo(human))); } @@ -92,22 +98,19 @@ - (void)testShouldCacheAnIndividualObjectWhenAsked { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); RKHuman* newHuman = [RKHuman createEntity]; newHuman.railsID = [NSNumber numberWithInt:5678]; - [entityCache cacheObject:newHuman withMapping:mapping inContext:objectStore.primaryManagedObjectContext]; - [entityCache cacheObjectsForEntity:human.entity withMapping:mapping inContext:objectStore.primaryManagedObjectContext]; + [entityCache cacheObject:newHuman byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; + [entityCache cacheObjectsForEntity:human.entity byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(2))); } @@ -118,12 +121,9 @@ - (void)testShouldCacheAnIndividualObjectByPrimaryKeyValueWhenAsked { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); @@ -132,13 +132,12 @@ - (void)testShouldCacheAnIndividualObjectByPrimaryKeyValueWhenAsked { [objectStore save:nil]; [entityCache cacheObject:newHuman.entity - withMapping:mapping - andPrimaryKeyValue:[NSNumber numberWithInt:5678] + byAttribute:@"railsID" + value:[NSNumber numberWithInt:5678] inContext:objectStore.primaryManagedObjectContext]; - [entityCache cacheObjectsForEntity:human.entity withMapping:mapping inContext:objectStore.primaryManagedObjectContext]; - humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping - inContext:objectStore.primaryManagedObjectContext]; + [entityCache cacheObjectsForEntity:human.entity byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; + + humanCache = [entityCache cachedObjectsForEntity:human.entity byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(2))); } @@ -148,16 +147,13 @@ - (void)testShouldExpireACacheEntryForAnObjectWhenAsked { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); - [entityCache expireCacheEntryForObject:human withMapping:mapping inContext:objectStore.primaryManagedObjectContext]; + [entityCache expireCacheEntryForObject:human byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([entityCache.entityCache count], is(equalToInt(0))); } @@ -167,16 +163,13 @@ - (void)testShouldExpireEntityCacheWhenAsked { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); - [entityCache expireCacheEntryForEntity:human.entity]; + [entityCache expireCacheEntriesForEntity:human.entity]; assertThatInteger([entityCache.entityCache count], is(equalToInt(0))); } @@ -187,12 +180,9 @@ - (void)testShouldExpireEntityCacheInResponseToMemoryWarning { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); @@ -207,12 +197,9 @@ - (void)testShouldAddInstancesOfInsertedObjectsToCache { human.railsID = [NSNumber numberWithInt:1234]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); @@ -221,7 +208,7 @@ - (void)testShouldAddInstancesOfInsertedObjectsToCache { [objectStore save:nil]; humanCache = [entityCache cachedObjectsForEntity:human.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(2))); } @@ -235,12 +222,9 @@ - (void)testShouldRemoveInstancesOfDeletedObjectsToCache { humanTwo.railsID = [NSNumber numberWithInt:5678]; [objectStore save:nil]; - RKManagedObjectMapping* mapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; - mapping.primaryKeyAttribute = @"railsID"; - RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:humanOne.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(2))); @@ -248,9 +232,65 @@ - (void)testShouldRemoveInstancesOfDeletedObjectsToCache { [objectStore save:nil]; humanCache = [entityCache cachedObjectsForEntity:humanOne.entity - withMapping:mapping + byAttribute:@"railsID" inContext:objectStore.primaryManagedObjectContext]; assertThatInteger([humanCache count], is(equalToInt(1))); } +- (void)testThatDeletionOfObjectsDoesNotPermitCreationOfDuplicates { + RKManagedObjectStore* objectStore = [RKTestFactory managedObjectStore]; + RKHuman* humanOne = [RKHuman createEntity]; + humanOne.railsID = [NSNumber numberWithInt:1234]; + + RKHuman* humanTwo = [RKHuman createEntity]; + humanTwo.railsID = [NSNumber numberWithInt:5678]; + [objectStore save:nil]; + + RKInMemoryEntityCache *entityCache = [[[RKInMemoryEntityCache alloc] init] autorelease]; + NSMutableDictionary *humanCache = [entityCache cachedObjectsForEntity:humanOne.entity + byAttribute:@"railsID" + inContext:objectStore.primaryManagedObjectContext]; + assertThatInteger([humanCache count], is(equalToInt(2))); + + [humanTwo deleteEntity]; + [objectStore save:nil]; + + RKHuman *existingReference = (RKHuman *)[entityCache cachedObjectForEntity:[RKHuman entity] withAttribute:@"railsID" value:[NSNumber numberWithInt:1234] inContext:objectStore.primaryManagedObjectContext]; + + assertThat(existingReference, is(notNilValue())); + assertThat(existingReference, is(equalTo(humanOne))); +} + +- (void)testThatRepeatedInvocationsOfLoadObjectDoesNotDuplicateObjects { + RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore]; + objectStore.cacheStrategy = [RKInMemoryManagedObjectCache new]; + RKObjectManager *objectManager = [RKTestFactory objectManager]; + objectManager.objectStore = objectStore; + RKManagedObjectMapping *humanMapping = [RKManagedObjectMapping mappingForClass:[RKHuman class] inManagedObjectStore:objectStore]; + humanMapping.primaryKeyAttribute = @"name"; + [humanMapping mapKeyPath:@"id" toAttribute:@"railsID"]; + [humanMapping mapAttributes:@"name", nil]; + [objectManager.mappingProvider setObjectMapping:humanMapping forKeyPath:@"human"]; + + for (NSUInteger i = 0; i < 5; i++) { + RKTestResponseLoader *responseLoader = [RKTestResponseLoader responseLoader]; + [objectManager loadObjectsAtResourcePath:@"/JSON/ArrayOfHumans.json" delegate:responseLoader]; + [responseLoader waitForResponse]; + for (RKHuman *object in [RKHuman allObjects]) { + if ([object.railsID intValue] == 201) { + [objectStore.managedObjectContextForCurrentThread deleteObject:object]; + [objectStore.managedObjectContextForCurrentThread save:nil]; + } + } + + [objectStore save:nil]; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.2]]; + } + + NSUInteger count = [RKHuman count:nil]; + assertThatInt(count, is(equalToInt(1))); + NSArray *humans = [RKHuman allObjects]; + [humans makeObjectsPerformSelector:@selector(railsID)]; +} + @end diff --git a/Tests/Logic/CoreData/RKManagedObjectMappingTest.m b/Tests/Logic/CoreData/RKManagedObjectMappingTest.m index ea4ea99034..1777fba122 100644 --- a/Tests/Logic/CoreData/RKManagedObjectMappingTest.m +++ b/Tests/Logic/CoreData/RKManagedObjectMappingTest.m @@ -24,24 +24,14 @@ #import "RKMappableObject.h" #import "RKChild.h" #import "RKParent.h" +#import "NSEntityDescription+RKAdditions.h" -@interface RKManagedObjectMappingTest : RKTestCase { - NSAutoreleasePool *_autoreleasePool; -} +@interface RKManagedObjectMappingTest : RKTestCase @end - @implementation RKManagedObjectMappingTest -//- (void)setUp { -// _autoreleasePool = [NSAutoreleasePool new]; -//} -// -//- (void)tearDown { -// [_autoreleasePool drain]; -//} - - (void)testShouldReturnTheDefaultValueForACoreDataAttribute { // Load Core Data RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; @@ -150,12 +140,12 @@ - (void)testShouldMapACollectionOfObjectsWithDynamicKeys { id mockCacheStrategy = [OCMockObject partialMockForObject:objectStore.cacheStrategy]; [[[mockCacheStrategy expect] andForwardToRealObject] findInstanceOfEntity:OCMOCK_ANY - withMapping:mapping - andPrimaryKeyValue:@"blake" + withPrimaryKeyAttribute:mapping.primaryKeyAttribute + value:@"blake" inManagedObjectContext:objectStore.primaryManagedObjectContext]; [[[mockCacheStrategy expect] andForwardToRealObject] findInstanceOfEntity:mapping.entity - withMapping:mapping - andPrimaryKeyValue:@"rachit" + withPrimaryKeyAttribute:mapping.primaryKeyAttribute + value:@"rachit" inManagedObjectContext:objectStore.primaryManagedObjectContext]; id userInfo = [RKTestFixture parsedObjectWithContentsOfFixture:@"DynamicKeys.json"]; RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:userInfo mappingProvider:provider]; @@ -200,4 +190,20 @@ - (void)testShouldIncludeTransformableAttributesInPropertyNamesAndTypes { assertThat([propertyNamesAndTypes objectForKey:@"favoriteColors"], is(notNilValue())); } +- (void)testThatAssigningAnEntityWithANonNilPrimaryKeyAttributeSetsTheDefaultValueForTheMapping { + RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKCat" inManagedObjectContext:store.primaryManagedObjectContext]; + RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForEntity:entity inManagedObjectStore:store]; + assertThat(mapping.primaryKeyAttribute, is(equalTo(@"railsID"))); +} + +- (void)testThatAssigningAPrimaryKeyAttributeToAMappingWhoseEntityHasANilPrimaryKeyAttributeAssignsItToTheEntity { + RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; + NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKCloud" inManagedObjectContext:store.primaryManagedObjectContext]; + RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForEntity:entity inManagedObjectStore:store]; + assertThat(mapping.primaryKeyAttribute, is(nilValue())); + mapping.primaryKeyAttribute = @"cloudID"; + assertThat(entity.primaryKeyAttribute, is(equalTo(@"cloudID"))); +} + @end diff --git a/Tests/Models/Data Model.xcdatamodel/elements b/Tests/Models/Data Model.xcdatamodel/elements index f4b144805e..df91e13b88 100644 Binary files a/Tests/Models/Data Model.xcdatamodel/elements and b/Tests/Models/Data Model.xcdatamodel/elements differ diff --git a/Tests/Models/Data Model.xcdatamodel/layout b/Tests/Models/Data Model.xcdatamodel/layout index 94b692e27c..d953ff4497 100644 Binary files a/Tests/Models/Data Model.xcdatamodel/layout and b/Tests/Models/Data Model.xcdatamodel/layout differ