Skip to content

Commit

Permalink
fixed predicate with substitution variables (#2430)
Browse files Browse the repository at this point in the history
  • Loading branch information
valeriomazzeo committed Jun 9, 2016
1 parent a858454 commit 533c955
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 8 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
@@ -1,5 +1,9 @@
osx_image: xcode7.3
language: objective-c
rvm: 2.2.3
rvm: 2.2.4
cache:
- bundler
- cocoapods
install:
- bundle install
- bundle exec pod install
Expand Down
32 changes: 25 additions & 7 deletions Code/CoreData/RKFetchRequestManagedObjectCache.m
Expand Up @@ -43,15 +43,31 @@
return [keyFragments componentsJoinedByString:@":"];
}

// NOTE: We build a dynamic format string here because `NSCompoundPredicate` does not support use of substiution variables
static NSPredicate *RKPredicateWithSubsitutionVariablesForAttributeValues(NSDictionary *attributeValues)
// NOTE: We make sure to convert the attribute values to compatible names that can be replaced correctly by `predicateWithSubstitutionVariables`
static NSString *RKAttributePlaceholderForAttributeName(NSString *attributeName)
{
return [[attributeName componentsSeparatedByCharactersInSet:[NSCharacterSet punctuationCharacterSet]] componentsJoinedByString:@"_"];
}

static NSDictionary *RKSubstitutionVariablesForAttributeValues(NSDictionary *attributeValues)
{
NSMutableDictionary *placeholders = [[NSMutableDictionary alloc] initWithCapacity:attributeValues.count];
[attributeValues enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
placeholders[RKAttributePlaceholderForAttributeName(key)] = value;
}];

return [NSDictionary dictionaryWithDictionary:placeholders];
}

// NOTE: We build a dynamic format string here because `NSCompoundPredicate` does not support use of substitution variables
static NSPredicate *RKPredicateWithSubstitutionVariablesForAttributeValues(NSDictionary *attributeValues)
{
NSArray *attributeNames = [attributeValues allKeys];
NSMutableArray *formatFragments = [NSMutableArray arrayWithCapacity:[attributeNames count]];
[attributeValues enumerateKeysAndObjectsUsingBlock:^(NSString *attributeName, id value, BOOL *stop) {
NSString *formatFragment = RKObjectIsCollection(value)
? [NSString stringWithFormat:@"%@ IN $%@", attributeName, attributeName]
: [NSString stringWithFormat:@"%@ = $%@", attributeName, attributeName];
? [NSString stringWithFormat:@"%@ IN $%@", attributeName, RKAttributePlaceholderForAttributeName(attributeName)]
: [NSString stringWithFormat:@"%@ = $%@", attributeName, RKAttributePlaceholderForAttributeName(attributeName)];
[formatFragments addObject:formatFragment];
}];

Expand Down Expand Up @@ -103,16 +119,18 @@ - (NSSet *)managedObjectsWithEntity:(NSEntityDescription *)entity
dispatch_sync(self.cacheQueue, ^{
substitutionPredicate = (self.predicateCache)[predicateCacheKey];
});


NSDictionary *substitutionVariables = RKSubstitutionVariablesForAttributeValues(attributeValues);

if (! substitutionPredicate) {
substitutionPredicate = RKPredicateWithSubsitutionVariablesForAttributeValues(attributeValues);
substitutionPredicate = RKPredicateWithSubstitutionVariablesForAttributeValues(attributeValues);
dispatch_barrier_async(self.cacheQueue, ^{
(self.predicateCache)[predicateCacheKey] = substitutionPredicate;
});
}

NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[entity name]];
fetchRequest.predicate = [substitutionPredicate predicateWithSubstitutionVariables:attributeValues];
fetchRequest.predicate = [substitutionPredicate predicateWithSubstitutionVariables:substitutionVariables];
__block NSError *error = nil;
__block NSArray *objects = nil;
[managedObjectContext performBlockAndWait:^{
Expand Down
23 changes: 23 additions & 0 deletions Tests/Logic/CoreData/RKFetchRequestMappingCacheTest.m
Expand Up @@ -9,6 +9,8 @@
#import "RKTestEnvironment.h"
#import "RKCat.h"
#import "RKEvent.h"
#import "RKHouse.h"
#import "RKHuman.h"

@interface RKFetchRequestMappingCacheTest : RKTestCase

Expand Down Expand Up @@ -67,6 +69,27 @@ - (void)testFetchRequestMappingCacheReturnsObjectsWithStringPrimaryKey
expect(managedObjects).to.equal(birthdays);
}

- (void)testFetchRequestMappingCacheReturnsObjectsWithStringUsingDotNotation
{
// RKEvent entity. String primary key
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
RKFetchRequestManagedObjectCache *cache = [RKFetchRequestManagedObjectCache new];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
RKHouse *house = [NSEntityDescription insertNewObjectForEntityForName:@"House" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
house.city = @"myCity";
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
human.house = house;
house.owner = human;

[managedObjectStore.persistentStoreManagedObjectContext save:nil];

NSSet *managedObjects = [cache managedObjectsWithEntity:entity
attributeValues:@{ @"house.city": @"myCity" }
inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
NSSet *humans = [NSSet setWithObject:human];
expect(managedObjects).to.equal(humans);
}

- (void)testThatCacheCanHandleSwitchingBetweenSingularAndPluralAttributeValues
{
// RKEvent entity. String primary key
Expand Down

0 comments on commit 533c955

Please sign in to comment.