mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-01-12 22:51:50 +08:00
First functional cut at new Core Data cacheing implementation. Tests clean, needs polishing.
This commit is contained in:
61
Code/CoreData/RKEntityByAttributeCache.h
Normal file
61
Code/CoreData/RKEntityByAttributeCache.h
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// RKEntityByAttributeCache.h
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/1/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
// RKManagedObjectContext
|
||||
// Maybe RKManagedObjectContextCache | RKEntityCache | RKEntityByAttributeCache
|
||||
// TODO: Better name... RKEntityAttributeCache ??
|
||||
@interface RKEntityByAttributeCache : NSObject
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
/// @name Creating a Cache
|
||||
///-----------------------------------------------------------------------------
|
||||
|
||||
- (id)initWithEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName managedObjectContext:(NSManagedObjectContext *)context;
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
/// @name Getting Cache Identity
|
||||
///-----------------------------------------------------------------------------
|
||||
|
||||
@property (nonatomic, readonly) NSEntityDescription *entity;
|
||||
@property (nonatomic, readonly) NSString *attribute;
|
||||
@property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
|
||||
@property (nonatomic, assign) BOOL monitorsContextForChanges;
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
/// @name Loading and Flushing the Cache
|
||||
///-----------------------------------------------------------------------------
|
||||
|
||||
- (void)load;
|
||||
- (void)flush;
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
/// @name Inspecting Cache State
|
||||
///-----------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)isLoaded;
|
||||
|
||||
- (NSUInteger)count;
|
||||
- (NSUInteger)countWithAttributeValue:(id)attributeValue;
|
||||
|
||||
- (BOOL)containsObject:(NSManagedObject *)object;
|
||||
- (BOOL)containsObjectWithAttributeValue:(id)attributeValue;
|
||||
|
||||
// Retrieve the object with the value for the attribute
|
||||
- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue;
|
||||
- (NSSet *)objectsWithAttributeValue:(id)attributeValue;
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
/// @name Managing Cached Objects
|
||||
///-----------------------------------------------------------------------------
|
||||
|
||||
- (void)addObject:(NSManagedObject *)object;
|
||||
- (void)removeObject:(NSManagedObject *)object;
|
||||
|
||||
@end
|
||||
270
Code/CoreData/RKEntityByAttributeCache.m
Normal file
270
Code/CoreData/RKEntityByAttributeCache.m
Normal file
@@ -0,0 +1,270 @@
|
||||
//
|
||||
// RKEntityByAttributeCache.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/1/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#import "RKEntityByAttributeCache.h"
|
||||
#import "RKLog.h"
|
||||
#import "RKObjectPropertyInspector.h"
|
||||
#import "RKObjectPropertyInspector+CoreData.h"
|
||||
|
||||
// Set Logging Component
|
||||
#undef RKLogComponent
|
||||
#define RKLogComponent lcl_cRestKitCoreData
|
||||
|
||||
@interface RKEntityByAttributeCache ()
|
||||
@property (nonatomic, retain) NSMutableDictionary *attributeValuesToObjectIDs;
|
||||
@end
|
||||
|
||||
@implementation RKEntityByAttributeCache
|
||||
|
||||
@synthesize entity = _entity;
|
||||
@synthesize attribute = _attribute;
|
||||
@synthesize managedObjectContext = _managedObjectContext;
|
||||
@synthesize attributeValuesToObjectIDs = _attributeValuesToObjectIDs;
|
||||
@synthesize monitorsContextForChanges = _monitorsContextForChanges;
|
||||
|
||||
- (id)initWithEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName managedObjectContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
self = [self init];
|
||||
if (self) {
|
||||
_entity = [entity retain];
|
||||
_attribute = [attributeName retain];
|
||||
_managedObjectContext = [context retain];
|
||||
_monitorsContextForChanges = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(managedObjectContextDidChange:)
|
||||
name:NSManagedObjectContextObjectsDidChangeNotification
|
||||
object:context];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(managedObjectContextDidSave:)
|
||||
name:NSManagedObjectContextDidSaveNotification
|
||||
object:context];
|
||||
#if TARGET_OS_IPHONE
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveMemoryWarning:)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
[_entity release];
|
||||
[_attribute release];
|
||||
[_managedObjectContext release];
|
||||
[_attributeValuesToObjectIDs release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSUInteger)count
|
||||
{
|
||||
return [self.attributeValuesToObjectIDs count];
|
||||
}
|
||||
|
||||
- (NSUInteger)countWithAttributeValue:(id)attributeValue
|
||||
{
|
||||
return [[self objectsWithAttributeValue:attributeValue] count];
|
||||
}
|
||||
|
||||
- (BOOL)shouldCoerceAttributeToString:(NSString *)attributeValue
|
||||
{
|
||||
if ([attributeValue isKindOfClass:[NSString class]] || [attributeValue isEqual:[NSNull null]]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
Class attributeType = [[RKObjectPropertyInspector sharedInspector] typeForProperty:self.attribute ofEntity:self.entity];
|
||||
return [attributeType instancesRespondToSelector:@selector(stringValue)];
|
||||
}
|
||||
|
||||
- (void)load
|
||||
{
|
||||
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
|
||||
[fetchRequest setEntity:self.entity];
|
||||
[fetchRequest setResultType:NSManagedObjectIDResultType];
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *objectIDs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (error) {
|
||||
RKLogError(@"Failed to load entity cache: %@", error);
|
||||
return;
|
||||
}
|
||||
[fetchRequest release];
|
||||
|
||||
self.attributeValuesToObjectIDs = [NSMutableDictionary dictionaryWithCapacity:[objectIDs count]];
|
||||
for (NSManagedObjectID* objectID in objectIDs) {
|
||||
NSError *error = nil;
|
||||
NSManagedObject *object = [self.managedObjectContext existingObjectWithID:objectID error:&error];
|
||||
if (! object && error) {
|
||||
RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error);
|
||||
}
|
||||
|
||||
[self addObject:object];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)flush
|
||||
{
|
||||
self.attributeValuesToObjectIDs = nil;
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[self flush];
|
||||
[self load];
|
||||
}
|
||||
|
||||
- (BOOL)isLoaded
|
||||
{
|
||||
return (self.attributeValuesToObjectIDs != nil);
|
||||
}
|
||||
|
||||
- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue
|
||||
{
|
||||
return [[self objectsWithAttributeValue:attributeValue] anyObject];
|
||||
}
|
||||
|
||||
- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID {
|
||||
/*
|
||||
NOTE:
|
||||
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.
|
||||
*/
|
||||
NSError *error = nil;
|
||||
NSManagedObject *object = [self.managedObjectContext existingObjectWithID:objectID error:&error];
|
||||
if (! object && error) {
|
||||
RKLogError(@"Failed to retrieve managed object with ID %@. Error %@\n%@", objectID, [error localizedDescription], [error userInfo]);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
- (NSSet *)objectsWithAttributeValue:(id)attributeValue
|
||||
{
|
||||
attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
|
||||
NSMutableSet *set = [self.attributeValuesToObjectIDs valueForKey:attributeValue];
|
||||
if (set) {
|
||||
NSSet *objectIDs = [NSSet setWithSet:set];
|
||||
NSMutableSet *objects = [NSMutableSet setWithCapacity:[objectIDs count]];
|
||||
for (NSManagedObjectID *objectID in objectIDs) {
|
||||
NSManagedObject *object = [self objectWithID:objectID];
|
||||
if (object) [objects addObject:object];
|
||||
}
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
return [NSSet set];
|
||||
}
|
||||
|
||||
- (void)addObject:(NSManagedObject *)object
|
||||
{
|
||||
NSAssert([object.entity isEqual:self.entity], @"Cannot add object with entity '%@' to cache with entity of '%@'", [[object entity] name], [self.entity name]);
|
||||
id attributeValue = [object valueForKey:self.attribute];
|
||||
// Coerce to a string if possible
|
||||
attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
|
||||
if (attributeValue) {
|
||||
NSManagedObjectID *objectID = [object objectID];
|
||||
BOOL isTemporary = [objectID isTemporaryID];
|
||||
NSMutableSet *set = [self.attributeValuesToObjectIDs valueForKey:attributeValue];
|
||||
if (set) {
|
||||
[set addObject:objectID];
|
||||
} else {
|
||||
set = [NSMutableSet setWithObject:objectID];
|
||||
}
|
||||
|
||||
if (nil == self.attributeValuesToObjectIDs) self.attributeValuesToObjectIDs = [NSMutableDictionary dictionary];
|
||||
[self.attributeValuesToObjectIDs setValue:set forKey:attributeValue];
|
||||
} else {
|
||||
RKLogWarning(@"Unable to add object with nil value for attribute '%@': %@", self.attribute, object);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObject:(NSManagedObject *)object
|
||||
{
|
||||
NSAssert([object.entity isEqual:self.entity], @"Cannot remove object with entity '%@' from cache with entity of '%@'", [[object entity] name], [self.entity name]);
|
||||
id attributeValue = [object valueForKey:self.attribute];
|
||||
// Coerce to a string if possible
|
||||
attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
|
||||
if (attributeValue) {
|
||||
NSManagedObjectID *objectID = [object objectID];
|
||||
NSMutableSet *set = [self.attributeValuesToObjectIDs valueForKey:attributeValue];
|
||||
if (set) {
|
||||
[set removeObject:objectID];
|
||||
}
|
||||
} else {
|
||||
RKLogWarning(@"Unable to remove object with nil value for attribute '%@': %@", self.attribute, object);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)containsObjectWithAttributeValue:(id)attributeValue
|
||||
{
|
||||
// Coerce to a string if possible
|
||||
attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
|
||||
return [[self objectsWithAttributeValue:attributeValue] count] > 0;
|
||||
}
|
||||
|
||||
- (BOOL)containsObject:(NSManagedObject *)object
|
||||
{
|
||||
if (! [object.entity isEqual:self.entity]) return NO;
|
||||
id attributeValue = [object valueForKey:self.attribute];
|
||||
// Coerce to a string if possible
|
||||
attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
|
||||
return [[self objectsWithAttributeValue:attributeValue] containsObject:object];
|
||||
}
|
||||
|
||||
- (void)managedObjectContextDidChange:(NSNotification *)notification
|
||||
{
|
||||
if (self.monitorsContextForChanges == NO) return;
|
||||
|
||||
NSDictionary *userInfo = notification.userInfo;
|
||||
NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
|
||||
NSSet *updatedObjects = [userInfo objectForKey:NSUpdatedObjectsKey];
|
||||
NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
|
||||
RKLogTrace(@"insertedObjects=%@, updatedObjects=%@, deletedObjects=%@", insertedObjects, updatedObjects, deletedObjects);
|
||||
|
||||
NSMutableSet *objectsToAdd = [NSMutableSet setWithSet:insertedObjects];
|
||||
[objectsToAdd unionSet:updatedObjects];
|
||||
|
||||
for (NSManagedObject *object in objectsToAdd) {
|
||||
if ([object.entity isEqual:self.entity]) {
|
||||
[self addObject:object];
|
||||
}
|
||||
}
|
||||
|
||||
for (NSManagedObject *object in deletedObjects) {
|
||||
if ([object.entity isEqual:self.entity]) {
|
||||
[self removeObject:object];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)managedObjectContextDidSave:(NSNotification *)notification
|
||||
{
|
||||
// After the MOC has been saved, we flush to ensure any temporary
|
||||
// objectID references are converted into permanent ID's on the next load.
|
||||
[self flush];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning:(NSNotification *)notification
|
||||
{
|
||||
[self flush];
|
||||
}
|
||||
|
||||
@end
|
||||
91
Code/CoreData/RKEntityCache.h
Normal file
91
Code/CoreData/RKEntityCache.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// RKEntityCache.h
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/2/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class RKEntityByAttributeCache;
|
||||
|
||||
/**
|
||||
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 RKEntityCache : NSObject
|
||||
|
||||
/// @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 *)context;
|
||||
|
||||
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
|
||||
|
||||
/// @name Cacheing Objects by Attribute
|
||||
|
||||
/**
|
||||
Caches all instances of an entity using the value for an attribute as the cache key.
|
||||
|
||||
@param entity The entity to cache all instances of.
|
||||
@param attributeName The attribute to cache the instances by.
|
||||
*/
|
||||
- (void)cacheObjectsForEntity:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName;
|
||||
|
||||
/**
|
||||
Returns a Boolean value indicating if all instances of an entity have been cached by a given attribute name.
|
||||
|
||||
@param entity The entity to check the cache status of.
|
||||
@param attributeName The attribute to check the cache status with.
|
||||
@return YES if the cache has been loaded with instances with the given attribute, else NO.
|
||||
*/
|
||||
- (BOOL)isEntity:(NSEntityDescription *)entity cachedByAttribute:(NSString *)attributeName;
|
||||
|
||||
/**
|
||||
Retrieves the first cached instance of a given entity where the specified attribute matches the given value.
|
||||
|
||||
@param entity The entity to search the cache for instances of.
|
||||
@param attributeName The attribute to search the cache for matches with.
|
||||
@param attributeValue The value of the attribute to return a match for.
|
||||
@return A matching managed object instance or nil.
|
||||
@raise NSInvalidArgumentException Raised if instances of the entity and attribute have not been cached.
|
||||
*/
|
||||
- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue;
|
||||
|
||||
/**
|
||||
Retrieves the all cached instances of a given entity where the specified attribute matches the given value.
|
||||
|
||||
@param entity The entity to search the cache for instances of.
|
||||
@param attributeName The attribute to search the cache for matches with.
|
||||
@param attributeValue The value of the attribute to return a match for.
|
||||
@return All matching managed object instances or nil.
|
||||
@raise NSInvalidArgumentException Raised if instances of the entity and attribute have not been cached.
|
||||
*/
|
||||
- (NSSet *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue;
|
||||
|
||||
// @name Accessing Underlying Caches
|
||||
- (RKEntityByAttributeCache *)attributeCacheForEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName;
|
||||
- (NSSet *)attributeCachesForEntity:(NSEntityDescription *)entity;
|
||||
|
||||
// @name Managing the Cache
|
||||
|
||||
/**
|
||||
Empties the cache by releasing all cached objects.
|
||||
*/
|
||||
- (void)flush;
|
||||
|
||||
- (void)addObject:(NSManagedObject *)object;
|
||||
- (void)removeObject:(NSManagedObject *)object;
|
||||
|
||||
@end
|
||||
122
Code/CoreData/RKEntityCache.m
Normal file
122
Code/CoreData/RKEntityCache.m
Normal file
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// RKEntityCache.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/2/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RKEntityCache.h"
|
||||
#import "RKEntityByAttributeCache.h"
|
||||
|
||||
@interface RKEntityCache ()
|
||||
@property (nonatomic, retain) NSMutableSet *attributeCaches;
|
||||
@end
|
||||
|
||||
@implementation RKEntityCache
|
||||
|
||||
@synthesize managedObjectContext = _managedObjectContext;
|
||||
@synthesize attributeCaches = _attributeCaches;
|
||||
|
||||
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
self = [self init];
|
||||
if (self) {
|
||||
_managedObjectContext = [context retain];
|
||||
_attributeCaches = [[NSMutableSet alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_managedObjectContext release];
|
||||
[_attributeCaches release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)cacheObjectsForEntity:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName
|
||||
{
|
||||
RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
|
||||
if (attributeCache && !attributeCache.isLoaded) {
|
||||
[attributeCache load];
|
||||
} else {
|
||||
attributeCache = [[RKEntityByAttributeCache alloc] initWithEntity:entity attribute:attributeName managedObjectContext:self.managedObjectContext];
|
||||
[attributeCache load];
|
||||
[self.attributeCaches addObject:attributeCache];
|
||||
[attributeCache release];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isEntity:(NSEntityDescription *)entity cachedByAttribute:(NSString *)attributeName
|
||||
{
|
||||
RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
|
||||
return (attributeCache && attributeCache.isLoaded);
|
||||
}
|
||||
|
||||
- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue
|
||||
{
|
||||
RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
|
||||
if (attributeCache) {
|
||||
return [attributeCache objectWithAttributeValue:attributeValue];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSSet *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue
|
||||
{
|
||||
RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
|
||||
if (attributeCache) {
|
||||
return [attributeCache objectsWithAttributeValue:attributeValue];
|
||||
}
|
||||
|
||||
return [NSSet set];
|
||||
}
|
||||
|
||||
- (RKEntityByAttributeCache *)attributeCacheForEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName
|
||||
{
|
||||
for (RKEntityByAttributeCache *cache in self.attributeCaches) {
|
||||
if ([cache.entity isEqual:entity] && [cache.attribute isEqualToString:attributeName]) {
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSSet *)attributeCachesForEntity:(NSEntityDescription *)entity
|
||||
{
|
||||
NSMutableSet *set = [NSMutableSet set];
|
||||
for (RKEntityByAttributeCache *cache in self.attributeCaches) {
|
||||
if ([cache.entity isEqual:entity]) {
|
||||
[set addObject:cache];
|
||||
}
|
||||
}
|
||||
|
||||
return [NSSet setWithSet:set];
|
||||
}
|
||||
|
||||
- (void)flush
|
||||
{
|
||||
[self.attributeCaches makeObjectsPerformSelector:@selector(flush)];
|
||||
}
|
||||
|
||||
- (void)addObject:(NSManagedObject *)object
|
||||
{
|
||||
NSSet *attributeCaches = [self attributeCachesForEntity:object.entity];
|
||||
for (RKEntityByAttributeCache *cache in attributeCaches) {
|
||||
[cache addObject:object];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObject:(NSManagedObject *)object
|
||||
{
|
||||
NSSet *attributeCaches = [self attributeCachesForEntity:object.entity];
|
||||
for (RKEntityByAttributeCache *cache in attributeCaches) {
|
||||
[cache removeObject:object];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
#import "RKManagedObjectCaching.h"
|
||||
#import "RKInMemoryEntityCache.h"
|
||||
|
||||
/**
|
||||
Provides a fast managed object cache where-in object instances are retained in
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "RKInMemoryManagedObjectCache.h"
|
||||
#import "RKEntityCache.h"
|
||||
#import "RKLog.h"
|
||||
|
||||
// Set Logging Component
|
||||
@@ -30,14 +31,23 @@ static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey =
|
||||
contextDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
|
||||
[[[NSThread currentThread] threadDictionary] setObject:contextDictionary forKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey];
|
||||
}
|
||||
NSNumber *hashNumber = [NSNumber numberWithUnsignedInteger:[managedObjectContext hash]];
|
||||
RKInMemoryEntityCache *cache = [contextDictionary objectForKey:hashNumber];
|
||||
if (! cache) {
|
||||
cache = [[RKInMemoryEntityCache alloc] initWithManagedObjectContext:managedObjectContext];
|
||||
[contextDictionary setObject:cache forKey:hashNumber];
|
||||
[cache release];
|
||||
NSNumber *hashNumber = [NSNumber numberWithUnsignedInteger:[managedObjectContext hash]];
|
||||
RKEntityCache *entityCache = [contextDictionary objectForKey:hashNumber];
|
||||
if (! entityCache) {
|
||||
RKLogInfo(@"Creating thread-local entity cache for managed object context: %@", managedObjectContext);
|
||||
entityCache = [[RKEntityCache alloc] initWithManagedObjectContext:managedObjectContext];
|
||||
[contextDictionary setObject:entityCache forKey:hashNumber];
|
||||
[entityCache release];
|
||||
}
|
||||
return [cache cachedObjectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue inContext:managedObjectContext];
|
||||
|
||||
if (! [entityCache isEntity:entity cachedByAttribute:primaryKeyAttribute]) {
|
||||
RKLogInfo(@"Cacheing instances of Entity '%@' by attribute '%@'", entity.name, primaryKeyAttribute);
|
||||
[entityCache cacheObjectsForEntity:entity byAttribute:primaryKeyAttribute];
|
||||
RKEntityByAttributeCache *attributeCache = [entityCache attributeCacheForEntity:entity attribute:primaryKeyAttribute];
|
||||
RKLogTrace(@"Cached %d objects", [attributeCache count]);
|
||||
}
|
||||
|
||||
return [entityCache objectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
@protocol RKManagedObjectCaching
|
||||
|
||||
@required
|
||||
|
||||
/**
|
||||
Retrieves a model object from the object store given a Core Data entity and
|
||||
the primary key attribute and value for the desired object.
|
||||
@@ -32,4 +34,10 @@
|
||||
value:(id)primaryKeyValue
|
||||
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
|
||||
|
||||
@optional
|
||||
|
||||
- (void)didFetchObject:(NSManagedObject *)object;
|
||||
- (void)didCreateObject:(NSManagedObject *)object;
|
||||
- (void)didDeleteObject:(NSManagedObject *)object;
|
||||
|
||||
@end
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
}
|
||||
|
||||
// If we have found the primary key attribute & value, try to find an existing instance to update
|
||||
if (primaryKeyAttribute && primaryKeyValue) {
|
||||
if (primaryKeyAttribute && primaryKeyValue && NO == [primaryKeyValue isEqual:[NSNull null]]) {
|
||||
object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity
|
||||
withPrimaryKeyAttribute:self.primaryKeyAttribute value:primaryKeyValue inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]];
|
||||
}
|
||||
|
||||
1
Gemfile
1
Gemfile
@@ -7,3 +7,4 @@ gem "thin", "~> 1.3.1"
|
||||
gem 'xcoder', :git => "git://github.com/rayh/xcoder.git"
|
||||
gem 'restkit', :git => 'git://github.com/RestKit/RestKit-Gem.git'
|
||||
gem 'ruby-debug19'
|
||||
gem 'faker', '1.0.1'
|
||||
|
||||
@@ -32,6 +32,9 @@ GEM
|
||||
columnize (0.3.6)
|
||||
daemons (1.1.4)
|
||||
eventmachine (0.12.10)
|
||||
faker (1.0.1)
|
||||
i18n (~> 0.4)
|
||||
i18n (0.6.0)
|
||||
json (1.6.6)
|
||||
linecache19 (0.5.12)
|
||||
ruby_core_source (>= 0.1.4)
|
||||
@@ -65,6 +68,7 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
bundler (~> 1.1.0)
|
||||
faker (= 1.0.1)
|
||||
rake (~> 0.9.0)
|
||||
restkit!
|
||||
ruby-debug19
|
||||
|
||||
22
Rakefile
22
Rakefile
@@ -181,3 +181,25 @@ desc "Validate a branch is ready for merging by checking for common issues"
|
||||
task :validate => [:build, 'docs:check', 'uispec:all'] do
|
||||
puts "Project state validated successfully. Proceed with merge."
|
||||
end
|
||||
|
||||
namespace :payload do
|
||||
task :generate do
|
||||
require 'json'
|
||||
require 'faker'
|
||||
|
||||
ids = (1..25).to_a
|
||||
child_ids = (50..100).to_a
|
||||
child_counts = (10..25).to_a
|
||||
hash = ids.inject({'parents' => []}) do |hash, parent_id|
|
||||
child_count = child_counts.sample
|
||||
children = (0..child_count).collect do
|
||||
{'name' => Faker::Name.name, 'childID' => child_ids.sample}
|
||||
end
|
||||
parent = {'parentID' => parent_id, 'name' => Faker::Name.name, 'children' => children}
|
||||
hash['parents'] << parent
|
||||
hash
|
||||
end
|
||||
File.open('payload.json', 'w+') { |f| f << hash.to_json }
|
||||
puts "Generated payload at: payload.json"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -523,10 +523,24 @@
|
||||
259C3027151280A1003066A2 /* grayArrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 25EC1AE314F8022600C3CF3F /* grayArrow@2x.png */; };
|
||||
259C3028151280A1003066A2 /* whiteArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 25EC1AE414F8022600C3CF3F /* whiteArrow.png */; };
|
||||
259C3029151280A1003066A2 /* whiteArrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 25EC1AE514F8022600C3CF3F /* whiteArrow@2x.png */; };
|
||||
259D983C154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */ = {isa = PBXBuildFile; fileRef = 259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */; };
|
||||
259D983D154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */ = {isa = PBXBuildFile; fileRef = 259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */; };
|
||||
259D9847154F8744008C90F5 /* RKBenchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D9845154F8744008C90F5 /* RKBenchmark.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
259D9848154F8744008C90F5 /* RKBenchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D9845154F8744008C90F5 /* RKBenchmark.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
259D9849154F8744008C90F5 /* RKBenchmark.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D9846154F8744008C90F5 /* RKBenchmark.m */; };
|
||||
259D984A154F8744008C90F5 /* RKBenchmark.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D9846154F8744008C90F5 /* RKBenchmark.m */; };
|
||||
259D98541550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */; };
|
||||
259D98551550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */; };
|
||||
259D98561550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */; };
|
||||
259D98571550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */; };
|
||||
259D985A1550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98591550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m */; };
|
||||
259D985B1550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98591550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m */; };
|
||||
259D985E155218E5008C90F5 /* RKEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D985C155218E4008C90F5 /* RKEntityCache.h */; };
|
||||
259D985F155218E5008C90F5 /* RKEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D985C155218E4008C90F5 /* RKEntityCache.h */; };
|
||||
259D9860155218E5008C90F5 /* RKEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D985D155218E4008C90F5 /* RKEntityCache.m */; };
|
||||
259D9861155218E5008C90F5 /* RKEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D985D155218E4008C90F5 /* RKEntityCache.m */; };
|
||||
259D986415521B20008C90F5 /* RKEntityCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D986315521B1F008C90F5 /* RKEntityCacheTest.m */; };
|
||||
259D986515521B20008C90F5 /* RKEntityCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D986315521B1F008C90F5 /* RKEntityCacheTest.m */; };
|
||||
25A2476E153E667E003240B6 /* RKCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A2476D153E667E003240B6 /* RKCacheTest.m */; };
|
||||
25A2476F153E667E003240B6 /* RKCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A2476D153E667E003240B6 /* RKCacheTest.m */; };
|
||||
25A34245147D8AAA0009758D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25A34244147D8AAA0009758D /* Security.framework */; };
|
||||
@@ -1049,8 +1063,16 @@
|
||||
257ABAB41511371C00CCAA76 /* NSManagedObject+RKAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+RKAdditions.h"; sourceTree = "<group>"; };
|
||||
257ABAB51511371D00CCAA76 /* NSManagedObject+RKAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+RKAdditions.m"; sourceTree = "<group>"; };
|
||||
259C301615128079003066A2 /* RestKitResources.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RestKitResources.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = benchmark_parents_and_children.json; sourceTree = "<group>"; };
|
||||
259D9845154F8744008C90F5 /* RKBenchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RKBenchmark.h; path = Testing/RKBenchmark.h; sourceTree = "<group>"; };
|
||||
259D9846154F8744008C90F5 /* RKBenchmark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RKBenchmark.m; path = Testing/RKBenchmark.m; sourceTree = "<group>"; };
|
||||
259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKEntityByAttributeCache.h; sourceTree = "<group>"; };
|
||||
259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityByAttributeCache.m; sourceTree = "<group>"; };
|
||||
259D98581550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryEntityAttributeCacheTest.h; sourceTree = "<group>"; };
|
||||
259D98591550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryEntityAttributeCacheTest.m; sourceTree = "<group>"; };
|
||||
259D985C155218E4008C90F5 /* RKEntityCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKEntityCache.h; sourceTree = "<group>"; };
|
||||
259D985D155218E4008C90F5 /* RKEntityCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityCache.m; sourceTree = "<group>"; };
|
||||
259D986315521B1F008C90F5 /* RKEntityCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityCacheTest.m; sourceTree = "<group>"; };
|
||||
25A2476D153E667E003240B6 /* RKCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKCacheTest.m; sourceTree = "<group>"; };
|
||||
25A34244147D8AAA0009758D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
|
||||
25B408241491CDDB00F21111 /* RKDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKDirectory.h; sourceTree = "<group>"; };
|
||||
@@ -1381,6 +1403,10 @@
|
||||
257ABAB51511371D00CCAA76 /* NSManagedObject+RKAdditions.m */,
|
||||
25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */,
|
||||
25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */,
|
||||
259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */,
|
||||
259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */,
|
||||
259D985C155218E4008C90F5 /* RKEntityCache.h */,
|
||||
259D985D155218E4008C90F5 /* RKEntityCache.m */,
|
||||
);
|
||||
path = CoreData;
|
||||
sourceTree = "<group>";
|
||||
@@ -1685,6 +1711,9 @@
|
||||
25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */,
|
||||
25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */,
|
||||
25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */,
|
||||
259D98581550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.h */,
|
||||
259D98591550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m */,
|
||||
259D986315521B1F008C90F5 /* RKEntityCacheTest.m */,
|
||||
);
|
||||
name = CoreData;
|
||||
path = Logic/CoreData;
|
||||
@@ -1712,6 +1741,7 @@
|
||||
25160FD01456F2330060A5C5 /* JSON */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */,
|
||||
252EFB2714DA0689004863C8 /* NakedEvents.json */,
|
||||
25160FD11456F2330060A5C5 /* ArrayOfNestedDictionaries.json */,
|
||||
25160FD21456F2330060A5C5 /* ArrayOfResults.json */,
|
||||
@@ -2234,6 +2264,8 @@
|
||||
25079C6F151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */,
|
||||
252A202D153471380078F8AD /* NSArray+RKAdditions.h in Headers */,
|
||||
259D9847154F8744008C90F5 /* RKBenchmark.h in Headers */,
|
||||
259D98541550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */,
|
||||
259D985E155218E5008C90F5 /* RKEntityCache.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2356,6 +2388,8 @@
|
||||
25079C70151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */,
|
||||
252A20311534714D0078F8AD /* NSArray+RKAdditions.h in Headers */,
|
||||
259D9848154F8744008C90F5 /* RKBenchmark.h in Headers */,
|
||||
259D98551550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */,
|
||||
259D985F155218E5008C90F5 /* RKEntityCache.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2526,6 +2560,7 @@
|
||||
252EFB2814DA0689004863C8 /* NakedEvents.json in Resources */,
|
||||
25CAAA9415254E7800CAE5D7 /* ArrayOfHumans.json in Resources */,
|
||||
25119FB6154A34B400C6BC58 /* parents_and_children.json in Resources */,
|
||||
259D983C154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2587,6 +2622,7 @@
|
||||
252EFB2914DA0689004863C8 /* NakedEvents.json in Resources */,
|
||||
25CAAA9515254E7800CAE5D7 /* ArrayOfHumans.json in Resources */,
|
||||
25119FB7154A34B400C6BC58 /* parents_and_children.json in Resources */,
|
||||
259D983D154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2759,6 +2795,8 @@
|
||||
25079C71151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m in Sources */,
|
||||
252A202E153471380078F8AD /* NSArray+RKAdditions.m in Sources */,
|
||||
259D9849154F8744008C90F5 /* RKBenchmark.m in Sources */,
|
||||
259D98561550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */,
|
||||
259D9860155218E5008C90F5 /* RKEntityCache.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2845,6 +2883,8 @@
|
||||
252A2034153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */,
|
||||
2501405315366000004E0466 /* RKObjectiveCppTest.mm in Sources */,
|
||||
25A2476E153E667E003240B6 /* RKCacheTest.m in Sources */,
|
||||
259D985A1550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m in Sources */,
|
||||
259D986415521B20008C90F5 /* RKEntityCacheTest.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2944,6 +2984,8 @@
|
||||
250B849E152B6F63002581F9 /* RKObjectMappingProvider+CoreData.m in Sources */,
|
||||
252A2030153471470078F8AD /* NSArray+RKAdditions.m in Sources */,
|
||||
259D984A154F8744008C90F5 /* RKBenchmark.m in Sources */,
|
||||
259D98571550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */,
|
||||
259D9861155218E5008C90F5 /* RKEntityCache.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3030,6 +3072,8 @@
|
||||
252A2035153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */,
|
||||
2501405415366000004E0466 /* RKObjectiveCppTest.mm in Sources */,
|
||||
25A2476F153E667E003240B6 /* RKCacheTest.m in Sources */,
|
||||
259D985B1550C6BE008C90F5 /* RKInMemoryEntityAttributeCacheTest.m in Sources */,
|
||||
259D986515521B20008C90F5 /* RKEntityCacheTest.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
2062
Tests/Fixtures/JSON/benchmark_parents_and_children.json
Normal file
2062
Tests/Fixtures/JSON/benchmark_parents_and_children.json
Normal file
File diff suppressed because it is too large
Load Diff
155
Tests/Logic/CoreData/RKEntityCacheTest.m
Normal file
155
Tests/Logic/CoreData/RKEntityCacheTest.m
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// RKEntityCacheTest.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/2/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RKTestEnvironment.h"
|
||||
#import "NSEntityDescription+RKAdditions.h"
|
||||
#import "RKEntityCache.h"
|
||||
#import "RKEntityByAttributeCache.h"
|
||||
#import "RKHuman.h"
|
||||
|
||||
@interface RKEntityCacheTest : RKTestCase
|
||||
@property (nonatomic, retain) RKManagedObjectStore *objectStore;
|
||||
@property (nonatomic, retain) RKEntityCache *cache;
|
||||
@property (nonatomic, retain) NSEntityDescription *entity;
|
||||
@end
|
||||
|
||||
@implementation RKEntityCacheTest
|
||||
|
||||
@synthesize objectStore = _objectStore;
|
||||
@synthesize cache = _cache;
|
||||
@synthesize entity = _entity;
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[RKTestFactory setUp];
|
||||
|
||||
self.objectStore = [RKTestFactory managedObjectStore];
|
||||
_cache = [[RKEntityCache alloc] initWithManagedObjectContext:self.objectStore.primaryManagedObjectContext];
|
||||
self.entity = [RKHuman entityDescriptionInContext:self.objectStore.primaryManagedObjectContext];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
self.objectStore = nil;
|
||||
self.cache = nil;
|
||||
|
||||
[RKTestFactory tearDown];
|
||||
}
|
||||
|
||||
- (void)testInitializationSetsManagedObjectContext
|
||||
{
|
||||
assertThat(_cache.managedObjectContext, is(equalTo(self.objectStore.primaryManagedObjectContext)));
|
||||
}
|
||||
|
||||
- (void)testIsEntityCachedByAttribute
|
||||
{
|
||||
assertThatBool([_cache isEntity:self.entity cachedByAttribute:@"railsID"], is(equalToBool(NO)));
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
assertThatBool([_cache isEntity:self.entity cachedByAttribute:@"railsID"], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfUnderlyingEntityAttributeCache
|
||||
{
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
RKEntityByAttributeCache *attributeCache = [_cache attributeCacheForEntity:self.entity attribute:@"railsID"];
|
||||
assertThat(attributeCache, is(notNilValue()));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfUnderlyingEntityAttributeCaches
|
||||
{
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
NSSet *caches = [_cache attributeCachesForEntity:self.entity];
|
||||
assertThat(caches, is(notNilValue()));
|
||||
assertThatInteger([caches count], is(equalToInteger(1)));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfObjectForEntityWithAttributeValue
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
NSError *error = nil;
|
||||
[self.objectStore save:&error];
|
||||
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
NSManagedObject *fetchedObject = [self.cache objectForEntity:self.entity withAttribute:@"railsID" value:[NSNumber numberWithInteger:12345]];
|
||||
assertThat(fetchedObject, is(notNilValue()));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfObjectsForEntityWithAttributeValue
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
NSError *error = nil;
|
||||
[self.objectStore save:&error];
|
||||
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
NSSet *objects = [self.cache objectsForEntity:self.entity withAttribute:@"railsID" value:[NSNumber numberWithInteger:12345]];
|
||||
assertThat(objects, hasCountOf(2));
|
||||
assertThat(objects, containsInAnyOrder(human1, human2, nil));
|
||||
}
|
||||
|
||||
- (void)testThatFlushEmptiesAllUnderlyingAttributeCaches
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
human1.name = @"Blake";
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
human2.name = @"Sarah";
|
||||
|
||||
[self.objectStore save:nil];
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"name"];
|
||||
|
||||
NSSet *objects = [self.cache objectsForEntity:self.entity withAttribute:@"railsID" value:[NSNumber numberWithInteger:12345]];
|
||||
assertThat(objects, hasCountOf(2));
|
||||
assertThat(objects, containsInAnyOrder(human1, human2, nil));
|
||||
|
||||
objects = [self.cache objectsForEntity:self.entity withAttribute:@"name" value:@"Blake"];
|
||||
assertThat(objects, hasCountOf(1));
|
||||
assertThat(objects, contains(human1, nil));
|
||||
|
||||
[self.cache flush];
|
||||
objects = [self.cache objectsForEntity:self.entity withAttribute:@"railsID" value:[NSNumber numberWithInteger:12345]];
|
||||
assertThat(objects, is(empty()));
|
||||
objects = [self.cache objectsForEntity:self.entity withAttribute:@"name" value:@"Blake"];
|
||||
assertThat(objects, is(empty()));
|
||||
}
|
||||
|
||||
- (void)testAddingObjectAddsToEachUnderlyingEntityAttributeCaches
|
||||
{
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"railsID"];
|
||||
[_cache cacheObjectsForEntity:self.entity byAttribute:@"name"];
|
||||
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
human1.name = @"Blake";
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
human2.name = @"Sarah";
|
||||
|
||||
[_cache addObject:human1];
|
||||
[_cache addObject:human2];
|
||||
|
||||
NSSet *objects = [self.cache objectsForEntity:self.entity withAttribute:@"railsID" value:[NSNumber numberWithInteger:12345]];
|
||||
assertThat(objects, hasCountOf(2));
|
||||
assertThat(objects, containsInAnyOrder(human1, human2, nil));
|
||||
|
||||
objects = [self.cache objectsForEntity:self.entity withAttribute:@"name" value:@"Blake"];
|
||||
assertThat(objects, hasCountOf(1));
|
||||
assertThat(objects, contains(human1, nil));
|
||||
}
|
||||
|
||||
- (void)testRemovingObjectRemovesFromUnderlyingEntityAttributeCaches
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
13
Tests/Logic/CoreData/RKInMemoryEntityAttributeCacheTest.h
Normal file
13
Tests/Logic/CoreData/RKInMemoryEntityAttributeCacheTest.h
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RKInMemoryEntityAttributeCacheTest.h
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/1/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface RKInMemoryEntityAttributeCacheTest : NSObject
|
||||
|
||||
@end
|
||||
345
Tests/Logic/CoreData/RKInMemoryEntityAttributeCacheTest.m
Normal file
345
Tests/Logic/CoreData/RKInMemoryEntityAttributeCacheTest.m
Normal file
@@ -0,0 +1,345 @@
|
||||
//
|
||||
// RKInMemoryEntityAttributeCacheTest.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/1/12.
|
||||
// Copyright (c) 2012 RestKit. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RKTestEnvironment.h"
|
||||
#import "NSEntityDescription+RKAdditions.h"
|
||||
#import "RKEntityByAttributeCache.h"
|
||||
#import "RKHuman.h"
|
||||
#import "RKChild.h"
|
||||
|
||||
@interface RKInMemoryEntityAttributeCacheTest : RKTestCase
|
||||
@property (nonatomic, retain) RKManagedObjectStore *objectStore;
|
||||
@property (nonatomic, retain) RKEntityByAttributeCache *cache;
|
||||
@end
|
||||
|
||||
@implementation RKInMemoryEntityAttributeCacheTest
|
||||
|
||||
@synthesize objectStore = _objectStore;
|
||||
@synthesize cache = _cache;
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[RKTestFactory setUp];
|
||||
self.objectStore = [RKTestFactory managedObjectStore];
|
||||
|
||||
NSEntityDescription *entity = [RKHuman entityDescriptionInContext:self.objectStore.primaryManagedObjectContext];
|
||||
self.cache = [[RKEntityByAttributeCache alloc] initWithEntity:entity
|
||||
attribute:@"railsID"
|
||||
managedObjectContext:self.objectStore.primaryManagedObjectContext];
|
||||
// Disable cache monitoring. Tested in specific cases.
|
||||
self.cache.monitorsContextForChanges = NO;
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
self.objectStore = nil;
|
||||
[RKTestFactory tearDown];
|
||||
}
|
||||
|
||||
#pragma mark - Identity Tests
|
||||
|
||||
- (void)testEntityIsAssigned
|
||||
{
|
||||
NSEntityDescription *entity = [RKHuman entityDescriptionInContext:self.objectStore.primaryManagedObjectContext];
|
||||
assertThat(self.cache.entity, is(equalTo(entity)));
|
||||
}
|
||||
|
||||
- (void)testManagedObjectContextIsAssigned
|
||||
{
|
||||
NSManagedObjectContext *context = self.objectStore.primaryManagedObjectContext;
|
||||
assertThat(self.cache.managedObjectContext, is(equalTo(context)));
|
||||
}
|
||||
|
||||
- (void)testAttributeNameIsAssigned
|
||||
{
|
||||
assertThat(self.cache.attribute, is(equalTo(@"railsID")));
|
||||
}
|
||||
|
||||
#pragma mark - Loading and Flushing
|
||||
|
||||
- (void)testLoadSetsLoadedToYes
|
||||
{
|
||||
[self.cache load];
|
||||
assertThatBool(self.cache.isLoaded, is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testLoadSetsCountAppropriately
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
NSError *error = nil;
|
||||
[self.objectStore save:&error];
|
||||
|
||||
assertThat(error, is(nilValue()));
|
||||
[self.cache load];
|
||||
assertThatInteger([self.cache count], is(equalToInteger(1)));
|
||||
}
|
||||
|
||||
- (void)testFlushCacheRemovesObjects
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
[self.cache flush];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(NO)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testFlushCacheReturnsCountToZero
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
[self.cache flush];
|
||||
assertThatInteger([self.cache count], is(equalToInteger(0)));
|
||||
}
|
||||
|
||||
#pragma mark - Retrieving Objects
|
||||
|
||||
- (void)testRetrievalByNumericValue
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
[self.cache load];
|
||||
|
||||
NSManagedObject *object = [self.cache objectWithAttributeValue:[NSNumber numberWithInteger:12345]];
|
||||
assertThat(object, is(equalTo(human)));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfNumericPropertyByStringValue
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
[self.cache load];
|
||||
|
||||
NSManagedObject *object = [self.cache objectWithAttributeValue:@"12345"];
|
||||
assertThat(object, is(equalTo(human)));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfObjectsWithAttributeValue
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
|
||||
NSSet *set = [self.cache objectsWithAttributeValue:[NSNumber numberWithInt:12345]];
|
||||
assertThat(set, hasCountOf(2));
|
||||
assertThat([set anyObject], is(instanceOf([NSManagedObject class])));
|
||||
}
|
||||
|
||||
- (void)testAddingObjectToCache
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human];
|
||||
assertThatBool([self.cache containsObject:human], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testAddingObjectWithDuplicateAttributeValue
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testRemovingObjectFromCache
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human];
|
||||
assertThatBool([self.cache containsObject:human], is(equalToBool(YES)));
|
||||
[self.cache removeObject:human];
|
||||
assertThatBool([self.cache containsObject:human], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testRemovingObjectWithExistingAttributeValue
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(YES)));
|
||||
[self.cache removeObject:human1];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(NO)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
#pragma mark - Inspecting Cache State
|
||||
|
||||
- (void)testContainsObjectReturnsNoForDifferingEntities
|
||||
{
|
||||
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKCloud" inManagedObjectContext:self.objectStore.primaryManagedObjectContext];
|
||||
NSManagedObject *cloud = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.objectStore.primaryManagedObjectContext];
|
||||
assertThatBool([self.cache containsObject:cloud], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testContainsObjectReturnsNoForSubEntities
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKChild *child = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
child.railsID = [NSNumber numberWithInteger:12345];
|
||||
|
||||
[self.cache addObject:human];
|
||||
assertThatBool([self.cache containsObject:human], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:child], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testContainsObjectWithAttributeValue
|
||||
{
|
||||
RKHuman *human = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human];
|
||||
assertThatBool([self.cache containsObjectWithAttributeValue:[NSNumber numberWithInteger:12345]], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testCountWithAttributeValue
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
assertThatInteger([self.cache countWithAttributeValue:[NSNumber numberWithInteger:12345]], is(equalToInteger(2)));
|
||||
}
|
||||
|
||||
- (void)testThatUnloadedCacheReturnsCountOfZero
|
||||
{
|
||||
assertThatInteger([self.cache count], is(equalToInteger(0)));
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle Events
|
||||
|
||||
- (void)testManagedObjectContextProcessPendingChangesAddsNewObjectsToCache
|
||||
{
|
||||
self.cache.monitorsContextForChanges = YES;
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testManagedObjectContextProcessPendingChangesIgnoresObjectsOfDifferentEntityTypes
|
||||
{
|
||||
self.cache.monitorsContextForChanges = YES;
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
|
||||
NSEntityDescription *entity = [NSEntityDescription entityForName:@"RKCloud" inManagedObjectContext:self.objectStore.primaryManagedObjectContext];
|
||||
NSManagedObject *cloud = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:self.objectStore.primaryManagedObjectContext];
|
||||
[cloud setValue:@"Cumulus" forKey:@"name"];
|
||||
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:cloud], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testManagedObjectContextProcessPendingChangesAddsUpdatedObjectsToCache
|
||||
{
|
||||
self.cache.monitorsContextForChanges = YES;
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
[self.cache removeObject:human1];
|
||||
human1.name = @"Modified Name";
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(NO)));
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testManagedObjectContextProcessPendingChangesRemovesExistingObjectsFromCache
|
||||
{
|
||||
self.cache.monitorsContextForChanges = YES;
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
[self.objectStore.primaryManagedObjectContext deleteObject:human1];
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
- (void)testCacheIsFlushedOnMemoryWarning
|
||||
{
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore save:nil];
|
||||
|
||||
[self.cache addObject:human1];
|
||||
[self.cache addObject:human2];
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(YES)));
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:self];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)testCreatingProcessingAndDeletingObjectsWorksAsExpected {
|
||||
self.cache.monitorsContextForChanges = YES;
|
||||
|
||||
RKHuman *human1 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human1.railsID = [NSNumber numberWithInteger:12345];
|
||||
RKHuman *human2 = [RKHuman createInContext:self.objectStore.primaryManagedObjectContext];
|
||||
human2.railsID = [NSNumber numberWithInteger:12345];
|
||||
[self.objectStore.primaryManagedObjectContext processPendingChanges];
|
||||
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(YES)));
|
||||
[self.objectStore.primaryManagedObjectContext deleteObject:human2];
|
||||
|
||||
// Save and reload the cache. This will result in the cached temporary
|
||||
// object ID's being released during the cache flush.
|
||||
[self.objectStore.primaryManagedObjectContext save:nil];
|
||||
[self.cache load];
|
||||
|
||||
assertThatBool([self.cache containsObject:human1], is(equalToBool(YES)));
|
||||
assertThatBool([self.cache containsObject:human2], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -19,6 +19,7 @@
|
||||
//
|
||||
|
||||
#import "RKTestEnvironment.h"
|
||||
#import "RKInMemoryEntityCache.h"
|
||||
#import "RKHuman.h"
|
||||
|
||||
@interface RKInMemoryEntityCache ()
|
||||
@@ -262,6 +263,7 @@
|
||||
}
|
||||
|
||||
- (void)testThatRepeatedInvocationsOfLoadObjectDoesNotDuplicateObjects {
|
||||
RKLogConfigureByName("RestKit/CoreData", RKLogLevelTrace);
|
||||
RKManagedObjectStore *objectStore = [RKTestFactory managedObjectStore];
|
||||
objectStore.cacheStrategy = [RKInMemoryManagedObjectCache new];
|
||||
RKObjectManager *objectManager = [RKTestFactory objectManager];
|
||||
@@ -274,12 +276,12 @@
|
||||
|
||||
for (NSUInteger i = 0; i < 5; i++) {
|
||||
RKTestResponseLoader *responseLoader = [RKTestResponseLoader responseLoader];
|
||||
responseLoader.timeout = 1000;
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#import "RKHuman.h"
|
||||
#import "RKChild.h"
|
||||
#import "RKParent.h"
|
||||
#import "RKBenchmark.h"
|
||||
|
||||
@interface RKManagedObjectMappingOperationTest : RKTestCase {
|
||||
|
||||
@@ -352,6 +353,7 @@
|
||||
// NOTE: This may be fragile. Reverse order seems to trigger them to be mapped parent first. NSDictionary
|
||||
// keys are not guaranteed to return in any particular order
|
||||
[mappingProvider setMapping:parentMapping forKeyPath:@"parents"];
|
||||
[mappingProvider setMapping:childMapping forKeyPath:@"children"];
|
||||
|
||||
NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"ConnectingParents.json"];
|
||||
RKObjectMapper *mapper = [RKObjectMapper mapperWithObject:JSON mappingProvider:mappingProvider];
|
||||
@@ -418,4 +420,74 @@
|
||||
assertThatInteger(childrenCount, is(equalToInteger(4)));
|
||||
}
|
||||
|
||||
- (void)testMappingAPayloadContainingRepeatedObjectsPerformsAcceptablyWithFetchRequestMappingCache {
|
||||
RKManagedObjectStore *store = [RKTestFactory managedObjectStore];
|
||||
store.cacheStrategy = [RKFetchRequestManagedObjectCache new];
|
||||
|
||||
RKManagedObjectMapping* childMapping = [RKManagedObjectMapping mappingForClass:[RKChild class] inManagedObjectStore:store];
|
||||
childMapping.primaryKeyAttribute = @"childID";
|
||||
[childMapping mapAttributes:@"name", @"childID", nil];
|
||||
|
||||
RKManagedObjectMapping* parentMapping = [RKManagedObjectMapping mappingForClass:[RKParent class] inManagedObjectStore:store];
|
||||
[parentMapping mapAttributes:@"parentID", @"name", nil];
|
||||
parentMapping.primaryKeyAttribute = @"parentID";
|
||||
[parentMapping mapRelationship:@"children" withMapping:childMapping];
|
||||
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider new];
|
||||
// NOTE: This may be fragile. Reverse order seems to trigger them to be mapped parent first. NSDictionary
|
||||
// keys are not guaranteed to return in any particular order
|
||||
[mappingProvider setObjectMapping:parentMapping forKeyPath:@"parents"];
|
||||
|
||||
NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"benchmark_parents_and_children.json"];
|
||||
RKObjectMapper *mapper = [RKObjectMapper mapperWithObject:JSON mappingProvider:mappingProvider];
|
||||
|
||||
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelOff);
|
||||
RKLogConfigureByName("RestKit/CoreData", RKLogLevelOff);
|
||||
|
||||
[RKBenchmark report:@"Mapping with Fetch Request Cache" executionBlock:^{
|
||||
for (NSUInteger i=0; i<50; i++) {
|
||||
[mapper performMapping];
|
||||
}
|
||||
}];
|
||||
NSUInteger parentCount = [RKParent count:nil];
|
||||
NSUInteger childrenCount = [RKChild count:nil];
|
||||
assertThatInteger(parentCount, is(equalToInteger(25)));
|
||||
assertThatInteger(childrenCount, is(equalToInteger(51)));
|
||||
}
|
||||
|
||||
- (void)testMappingAPayloadContainingRepeatedObjectsPerformsAcceptablyWithInMemoryMappingCache {
|
||||
RKManagedObjectStore *store = [RKTestFactory managedObjectStore];
|
||||
store.cacheStrategy = [RKInMemoryManagedObjectCache new];
|
||||
|
||||
RKManagedObjectMapping* childMapping = [RKManagedObjectMapping mappingForClass:[RKChild class] inManagedObjectStore:store];
|
||||
childMapping.primaryKeyAttribute = @"childID";
|
||||
[childMapping mapAttributes:@"name", @"childID", nil];
|
||||
|
||||
RKManagedObjectMapping* parentMapping = [RKManagedObjectMapping mappingForClass:[RKParent class] inManagedObjectStore:store];
|
||||
[parentMapping mapAttributes:@"parentID", @"name", nil];
|
||||
parentMapping.primaryKeyAttribute = @"parentID";
|
||||
[parentMapping mapRelationship:@"children" withMapping:childMapping];
|
||||
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider new];
|
||||
// NOTE: This may be fragile. Reverse order seems to trigger them to be mapped parent first. NSDictionary
|
||||
// keys are not guaranteed to return in any particular order
|
||||
[mappingProvider setObjectMapping:parentMapping forKeyPath:@"parents"];
|
||||
|
||||
NSDictionary *JSON = [RKTestFixture parsedObjectWithContentsOfFixture:@"benchmark_parents_and_children.json"];
|
||||
RKObjectMapper *mapper = [RKObjectMapper mapperWithObject:JSON mappingProvider:mappingProvider];
|
||||
|
||||
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelOff);
|
||||
RKLogConfigureByName("RestKit/CoreData", RKLogLevelOff);
|
||||
|
||||
[RKBenchmark report:@"Mapping with In Memory Cache" executionBlock:^{
|
||||
for (NSUInteger i=0; i<50; i++) {
|
||||
[mapper performMapping];
|
||||
}
|
||||
}];
|
||||
NSUInteger parentCount = [RKParent count:nil];
|
||||
NSUInteger childrenCount = [RKChild count:nil];
|
||||
assertThatInteger(parentCount, is(equalToInteger(25)));
|
||||
assertThatInteger(childrenCount, is(equalToInteger(51)));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,16 @@
|
||||
|
||||
@implementation RKManagedObjectMappingTest
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[RKTestFactory setUp];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[RKTestFactory tearDown];
|
||||
}
|
||||
|
||||
- (void)testShouldReturnTheDefaultValueForACoreDataAttribute {
|
||||
// Load Core Data
|
||||
RKManagedObjectStore *store = [RKTestFactory managedObjectStore];
|
||||
@@ -220,13 +230,14 @@
|
||||
mapping.primaryKeyAttribute = @"railsID";
|
||||
[mapping addAttributeMapping:[RKObjectAttributeMapping mappingFromKeyPath:@"id" toKeyPath:@"railsID"]];
|
||||
|
||||
RKHuman* human = [RKHuman object];
|
||||
RKHuman* human = [RKHuman createInContext:store.primaryManagedObjectContext];
|
||||
human.railsID = [NSNumber numberWithInt:123];
|
||||
[store save:nil];
|
||||
assertThatBool([RKHuman hasAtLeastOneEntity], is(equalToBool(YES)));
|
||||
|
||||
NSDictionary* data = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:123] forKey:@"id"];
|
||||
id object = [mapping mappableObjectForData:data];
|
||||
NSManagedObject *object = [mapping mappableObjectForData:data];
|
||||
assertThat([object managedObjectContext], is(equalTo(store.primaryManagedObjectContext)));
|
||||
assertThat(object, isNot(nilValue()));
|
||||
assertThat(object, is(equalTo(human)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user