mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-01-12 22:51:50 +08:00
Reimplement predicate caching within the RKFetchRequestManagedObjectCache to avoid nasty crash due to malformed predicates. fixes #1141
This commit is contained in:
@@ -17,13 +17,19 @@
|
||||
#define RKLogComponent RKlcl_cRestKitCoreData
|
||||
|
||||
/*
|
||||
NOTE: At the moment this cache key assume that the structure of the values for each key in the `attributeValues` in constant
|
||||
i.e. if you have `userID`, it will always be a single value, or `userIDs` will always be an array.
|
||||
It will need to be reimplemented if changes in attribute values occur during the life of a single cache
|
||||
This function computes a cache key given a dictionary of attribute values. Each attribute name is used as a fragment within the aggregate cache key. A suffix qualifier is appended that differentiates singular vs. collection attribute values so that '==' and 'IN' predicates are computed appropriately.
|
||||
*/
|
||||
static NSString *RKPredicateCacheKeyForAttributes(NSArray *attributeNames)
|
||||
static NSString *RKPredicateCacheKeyForAttributeValues(NSDictionary *attributesValues)
|
||||
{
|
||||
return [[attributeNames sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] componentsJoinedByString:@":"];
|
||||
NSArray *sortedKeys = [[attributesValues allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||
NSMutableArray *keyFragments = [NSMutableArray array];
|
||||
for (NSString *attributeName in sortedKeys) {
|
||||
id value = [attributesValues objectForKey:attributeName];
|
||||
char suffix = ([value respondsToSelector:@selector(count)]) ? '+' : '.';
|
||||
NSString *attributeKey = [NSString stringWithFormat:@"%@%c", attributeName, suffix];
|
||||
[keyFragments addObject:attributeKey];
|
||||
}
|
||||
return [keyFragments componentsJoinedByString:@":"];
|
||||
}
|
||||
|
||||
// NOTE: We build a dynamic format string here because `NSCompoundPredicate` does not support use of substiution variables
|
||||
@@ -65,7 +71,7 @@ static NSPredicate *RKPredicateWithSubsitutionVariablesForAttributeValues(NSDict
|
||||
NSAssert(attributeValues, @"Cannot retrieve cached objects without attribute values to identify them with.");
|
||||
NSAssert(managedObjectContext, @"Cannot find existing managed object with a nil context");
|
||||
|
||||
NSString *predicateCacheKey = RKPredicateCacheKeyForAttributes([attributeValues allKeys]);
|
||||
NSString *predicateCacheKey = RKPredicateCacheKeyForAttributeValues(attributeValues);
|
||||
NSPredicate *substitutionPredicate = [self.predicateCache objectForKey:predicateCacheKey];
|
||||
if (! substitutionPredicate) {
|
||||
substitutionPredicate = RKPredicateWithSubsitutionVariablesForAttributeValues(attributeValues);
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
|
||||
@implementation RKFetchRequestMappingCacheTest
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[RKTestFactory setUp];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[RKTestFactory tearDown];
|
||||
}
|
||||
|
||||
- (void)testFetchRequestMappingCacheReturnsObjectsWithNumericPrimaryKey
|
||||
{
|
||||
// RKCat entity. Integer prinmary key.
|
||||
@@ -57,4 +67,43 @@
|
||||
expect(managedObjects).to.equal(birthdays);
|
||||
}
|
||||
|
||||
- (void)testThatCacheCanHandleSwitchingBetweenSingularAndPluralAttributeValues
|
||||
{
|
||||
// RKEvent entity. String primary key
|
||||
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
|
||||
RKFetchRequestManagedObjectCache *cache = [RKFetchRequestManagedObjectCache new];
|
||||
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Event" inManagedObjectStore:managedObjectStore];
|
||||
mapping.identificationAttributes = @[ @"eventID" ];
|
||||
|
||||
RKEvent *event1 = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
event1.eventID = @"e-1234-a8-b12";
|
||||
|
||||
RKEvent *event2 = [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
event2.eventID = @"ff-1234-a8-b12";
|
||||
|
||||
[managedObjectStore.persistentStoreManagedObjectContext save:nil];
|
||||
|
||||
NSSet *managedObjects = [cache managedObjectsWithEntity:entity
|
||||
attributeValues:@{ @"eventID": @[ event1.eventID, event2.eventID ] }
|
||||
inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
NSSet *events = [NSSet setWithObjects:event1, event2, nil];
|
||||
expect(managedObjects).to.haveCountOf(2);
|
||||
expect(managedObjects).to.equal(events);
|
||||
|
||||
managedObjects = [cache managedObjectsWithEntity:entity
|
||||
attributeValues:@{ @"eventID": event1.eventID }
|
||||
inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
events = [NSSet setWithObject:event1];
|
||||
expect(managedObjects).to.haveCountOf(1);
|
||||
expect(managedObjects).to.equal(events);
|
||||
|
||||
managedObjects = [cache managedObjectsWithEntity:entity
|
||||
attributeValues:@{ @"eventID": @[ event1.eventID ] }
|
||||
inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
events = [NSSet setWithObject:event1];
|
||||
expect(managedObjects).to.haveCountOf(1);
|
||||
expect(managedObjects).to.equal(events);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user