Integrated primaryKey extension to NSEntityDescription and refactored cache strategy

classes to eliminate issues with duplicated objects. closes #611, #612, #613, #618

* NSEntityDescription is now aware of the primaryKeyAttribute. Can be configured via
Interface Builder within Xcode or programatically.
* Added findByPrimaryKey: interface to the Core Data extensions.
* Relaxed dependencies on RKManagedObjectMapping across the system now that primaryKey is
available without a reference to the mapping.
This commit is contained in:
Blake Watters
2012-03-22 16:31:39 -04:00
parent 1b324ccc80
commit a545c3942b
32 changed files with 650 additions and 271 deletions

View File

@@ -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"

View File

@@ -0,0 +1,39 @@
//
// NSEntityDescription+RKAdditions.h
// RestKit
//
// Created by Blake Watters on 3/22/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import <CoreData/CoreData.h>
/**
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

View File

@@ -0,0 +1,38 @@
//
// NSEntityDescription+RKAdditions.m
// RestKit
//
// Created by Blake Watters on 3/22/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import <objc/runtime.h>
#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

View File

@@ -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;

View File

@@ -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 @@ RK_FIX_CATEGORY_BUG(NSManagedObject_ActiveRecord)
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; {

View File

@@ -10,6 +10,9 @@
@class RKManagedObjectStore, RKManagedObjectMapping;
/**
Provides extensions to NSManagedObject for various common tasks.
*/
@interface NSManagedObject (RKAdditions)
/**

View File

@@ -10,6 +10,9 @@
@class RKManagedObjectStore;
/**
Provides extensions to NSManagedObjectContext for various common tasks.
*/
@interface NSManagedObjectContext (RKAdditions)
/**

View File

@@ -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 <RKManagedObjectCacheing>
@end

View File

@@ -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);

View File

@@ -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 <RKManagedObjectMappingCache>
@end

View File

@@ -7,62 +7,77 @@
//
#import <CoreData/CoreData.h>
#import "RKManagedObjectMapping.h"
@interface RKInMemoryEntityCache : NSObject {
NSMutableDictionary *_entityCache;
}
@property (nonatomic, readonly) NSDictionary *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.
@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

View File

@@ -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 @@
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 @@
}
- (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 @@
}
- (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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
for (NSEntityDescription *entity in entitiesToExpire) {
[self expireCacheEntryForEntity:entity];
[self expireCacheEntriesForEntity:entity];
}
}

View File

@@ -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 <RKManagedObjectCacheing>
@end

View File

@@ -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

View File

@@ -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 <RKManagedObjectMappingCache>
@property (nonatomic, readonly) RKInMemoryEntityCache *cache;
@end

View File

@@ -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

View File

@@ -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;

View File

@@ -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 @@
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 @@
}
- (void)dealloc {
[self removeObserver:self forKeyPath:@"entity"];
[self removeObserver:self forKeyPath:@"primaryKeyAttribute"];
[_entity release];
[_relationshipToPrimaryKeyMappings release];
[super dealloc];
@@ -158,9 +165,7 @@
// 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 @@
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

View File

@@ -1,5 +1,5 @@
//
// RKManagedObjectMappingCache.h
// RKManagedObjectCacheing.h
// RestKit
//
// Created by Jeff Arena on 1/24/12.
@@ -7,17 +7,29 @@
//
#import <CoreData/CoreData.h>
#import "RKManagedObjectMapping.h"
@protocol RKManagedObjectMappingCache
/**
* Retrieves a model object from the object store given a Core Data entity and
* the primary key attribute and value for the desired object.
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.
@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

View File

@@ -81,7 +81,7 @@ extern NSString* const RKManagedObjectStoreDidFailSaveNotification;
/**
*/
@property (nonatomic, retain) NSObject<RKManagedObjectMappingCache> *cacheStrategy;
@property (nonatomic, retain) NSObject<RKManagedObjectCacheing> *cacheStrategy;
/**
* Initialize a new managed object store with a SQLite database with the filename specified

View File

@@ -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 @@ static RKManagedObjectStore *defaultObjectStore = nil;
self.primaryManagedObjectContext = [self newManagedObjectContext];
_cacheStrategy = [[RKFetchRequestMappingCache alloc] init];
_cacheStrategy = [[RKFetchRequestManagedObjectCache alloc] init];
// Ensure there is a search word observer
[RKSearchWordObserver sharedObserver];

View File

@@ -27,12 +27,12 @@
@protocol RKDynamicObjectMappingDelegate <NSObject>
@required
- (RKObjectMapping*)objectMappingForData:(id)data;
- (RKObjectMapping *)objectMappingForData:(id)data;
@end
#ifdef NS_BLOCKS_AVAILABLE
typedef RKObjectMapping*(^RKDynamicObjectMappingDelegateBlock)(id);
typedef RKObjectMapping *(^RKDynamicObjectMappingDelegateBlock)(id);
#endif
/**

View File

@@ -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]];
}];
}

View File

@@ -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 = "<group>"; };
25055B8D14EEF40000B9C4DD /* RKMappingTestExpectation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RKMappingTestExpectation.h; path = Testing/RKMappingTestExpectation.h; sourceTree = "<group>"; };
25055B8E14EEF40000B9C4DD /* RKMappingTestExpectation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RKMappingTestExpectation.m; path = Testing/RKMappingTestExpectation.m; sourceTree = "<group>"; };
25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEntityDescription+RKAdditions.h"; sourceTree = "<group>"; };
25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEntityDescription+RKAdditions.m"; sourceTree = "<group>"; };
25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSEntityDescription+RKAdditionsTest.m"; sourceTree = "<group>"; };
250CA67B147D8E800047D347 /* OCHamcrest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrest.framework; sourceTree = "<group>"; };
250CA67C147D8E800047D347 /* OCHamcrestIOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OCHamcrestIOS.framework; sourceTree = "<group>"; };
250DF22814C5190E0001DEFA /* RKOrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKOrderedDictionary.h; sourceTree = "<group>"; };
@@ -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 = "<group>"; };
25CAAA9315254E7800CAE5D7 /* ArrayOfHumans.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ArrayOfHumans.json; sourceTree = "<group>"; };
25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+ActiveRecordTest.m"; sourceTree = "<group>"; };
25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestMappingCacheTest.m; sourceTree = "<group>"; };
25EC1A2A14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RKObjectManager+RKTableController.h"; sourceTree = "<group>"; };
25EC1A2B14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RKObjectManager+RKTableController.m"; sourceTree = "<group>"; };
@@ -1162,10 +1174,10 @@
49D275AB14C9F3020090845D /* RKISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKISO8601DateFormatter.h; sourceTree = "<group>"; };
49D275AC14C9F3020090845D /* RKISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKISO8601DateFormatter.m; sourceTree = "<group>"; };
7394DF3514CF157A00CE7BCE /* RKManagedObjectMappingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKManagedObjectMappingCache.h; sourceTree = "<group>"; };
7394DF3814CF168C00CE7BCE /* RKFetchRequestMappingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKFetchRequestMappingCache.h; sourceTree = "<group>"; };
7394DF3914CF168C00CE7BCE /* RKFetchRequestMappingCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestMappingCache.m; sourceTree = "<group>"; };
7394DF3C14CF19F200CE7BCE /* RKInMemoryMappingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryMappingCache.h; sourceTree = "<group>"; };
7394DF3D14CF19F200CE7BCE /* RKInMemoryMappingCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryMappingCache.m; sourceTree = "<group>"; };
7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKFetchRequestManagedObjectCache.h; sourceTree = "<group>"; };
7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestManagedObjectCache.m; sourceTree = "<group>"; };
7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryManagedObjectCache.h; sourceTree = "<group>"; };
7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryManagedObjectCache.m; sourceTree = "<group>"; };
7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryEntityCache.h; sourceTree = "<group>"; };
7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryEntityCache.m; sourceTree = "<group>"; };
73D3907114CA19F90093E3D6 /* parent.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = parent.json; sourceTree = "<group>"; };
@@ -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 = "<group>";
@@ -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;
};

View File

@@ -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": [
{

View File

@@ -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

View File

@@ -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

View File

@@ -20,7 +20,7 @@
{
// 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 @@
[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 @@
{
// 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 @@
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)));
}

View File

@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
[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 @@
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 @@
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 @@
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 @@
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 @@
[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 @@
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 @@
[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

View File

@@ -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 @@
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 @@
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