mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-22 20:18:53 +08:00
Fix issue in which managed objects that are the children of non-managed objects were refetched and assigned as a collection rather than as a singular value. fixes #1118
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#import "RKRequestOperationSubclass.h"
|
||||
#import "NSManagedObjectContext+RKAdditions.h"
|
||||
#import "NSManagedObject+RKAdditions.h"
|
||||
#import "RKObjectUtilities.h"
|
||||
|
||||
// Graph visitor
|
||||
#import "RKResponseDescriptor.h"
|
||||
@@ -267,19 +268,6 @@ NSSet *RKSetByRemovingSubkeypathsFromSet(NSSet *setOfKeyPaths)
|
||||
}];
|
||||
}
|
||||
|
||||
static void RKSetMappedValueForKeyPathInDictionary(id value, id rootKey, NSString *keyPath, NSMutableDictionary *dictionary)
|
||||
{
|
||||
NSCParameterAssert(value);
|
||||
NSCParameterAssert(rootKey);
|
||||
NSCParameterAssert(dictionary);
|
||||
if (keyPath && ![keyPath isEqual:[NSNull null]]) {
|
||||
id valueAtRootKey = [dictionary objectForKey:rootKey];
|
||||
[valueAtRootKey setValue:value forKeyPath:keyPath];
|
||||
} else {
|
||||
[dictionary setObject:value forKey:rootKey];
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition: Must be called from within the correct context
|
||||
static NSManagedObject *RKRefetchManagedObjectInContext(NSManagedObject *managedObject, NSManagedObjectContext *managedObjectContext)
|
||||
{
|
||||
@@ -295,6 +283,41 @@ static NSManagedObject *RKRefetchManagedObjectInContext(NSManagedObject *managed
|
||||
return refetchedObject;
|
||||
}
|
||||
|
||||
static id RKRefetchedValueInManagedObjectContext(id value, NSManagedObjectContext *managedObjectContext)
|
||||
{
|
||||
if (! value) {
|
||||
return value;
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
BOOL isMutable = [value isKindOfClass:[NSMutableArray class]];
|
||||
NSMutableArray *newValue = [[NSMutableArray alloc] initWithCapacity:[value count]];
|
||||
for (__strong id object in value) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) object = RKRefetchManagedObjectInContext(object, managedObjectContext);
|
||||
if (object) [newValue addObject:object];
|
||||
}
|
||||
value = (isMutable) ? newValue : [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSSet class]]) {
|
||||
BOOL isMutable = [value isKindOfClass:[NSMutableSet class]];
|
||||
NSMutableSet *newValue = [[NSMutableSet alloc] initWithCapacity:[value count]];
|
||||
for (__strong id object in value) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) object = RKRefetchManagedObjectInContext(object, managedObjectContext);
|
||||
if (object) [newValue addObject:object];
|
||||
}
|
||||
value = (isMutable) ? newValue : [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSOrderedSet class]]) {
|
||||
BOOL isMutable = [value isKindOfClass:[NSMutableOrderedSet class]];
|
||||
NSMutableOrderedSet *newValue = [NSMutableOrderedSet orderedSet];
|
||||
[(NSOrderedSet *)value enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) object = RKRefetchManagedObjectInContext(object, managedObjectContext);
|
||||
if (object) [newValue setObject:object atIndex:index];
|
||||
}];
|
||||
value = (isMutable) ? newValue : [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSManagedObject class]]) {
|
||||
value = RKRefetchManagedObjectInContext(value, managedObjectContext);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Finds the key paths for all entity mappings in the graph whose parent objects are not other managed objects
|
||||
static NSDictionary *RKDictionaryFromDictionaryWithManagedObjectsInVisitationsRefetchedInContext(NSDictionary *dictionaryOfManagedObjects, NSArray *visitations, NSManagedObjectContext *managedObjectContext)
|
||||
{
|
||||
@@ -309,41 +332,29 @@ static NSDictionary *RKDictionaryFromDictionaryWithManagedObjectsInVisitationsRe
|
||||
// If keyPaths contains null, then the root object is a managed object and we only need to refetch it
|
||||
NSSet *nonNestedKeyPaths = ([keyPaths containsObject:[NSNull null]]) ? [NSSet setWithObject:[NSNull null]] : RKSetByRemovingSubkeypathsFromSet(keyPaths);
|
||||
|
||||
NSDictionary *mappingResultsAtRootKey = [dictionaryOfManagedObjects objectForKey:rootKey];
|
||||
NSDictionary *mappingResultsAtRootKey = [newDictionary objectForKey:rootKey];
|
||||
for (NSString *keyPath in nonNestedKeyPaths) {
|
||||
id value = [keyPath isEqual:[NSNull null]] ? mappingResultsAtRootKey : [mappingResultsAtRootKey valueForKeyPath:keyPath];
|
||||
if (! value) {
|
||||
continue;
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
BOOL isMutable = [value isKindOfClass:[NSMutableArray class]];
|
||||
NSMutableArray *newValue = [[NSMutableArray alloc] initWithCapacity:[value count]];
|
||||
for (__strong id object in value) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) object = RKRefetchManagedObjectInContext(object, managedObjectContext);
|
||||
if (object) [newValue addObject:object];
|
||||
id value = nil;
|
||||
if ([keyPath isEqual:[NSNull null]]) {
|
||||
value = RKRefetchedValueInManagedObjectContext(mappingResultsAtRootKey, managedObjectContext);
|
||||
if (value) [newDictionary setObject:value forKey:rootKey];
|
||||
} else {
|
||||
NSMutableArray *keyPathComponents = [[keyPath componentsSeparatedByString:@"."] mutableCopy];
|
||||
NSString *destinationKey = [keyPathComponents lastObject];
|
||||
[keyPathComponents removeLastObject];
|
||||
id sourceObject = [keyPathComponents count] ? [mappingResultsAtRootKey valueForKeyPath:[keyPathComponents componentsJoinedByString:@"."]] : mappingResultsAtRootKey;
|
||||
if (RKObjectIsCollection(sourceObject)) {
|
||||
// This is a to-many relationship, we want to refetch each item at the keyPath
|
||||
for (id nestedObject in sourceObject) {
|
||||
// Refetch this object. Set it on the destination.
|
||||
NSManagedObject *managedObject = [nestedObject valueForKey:destinationKey];
|
||||
[nestedObject setValue:RKRefetchedValueInManagedObjectContext(managedObject, managedObjectContext) forKey:destinationKey];
|
||||
}
|
||||
} else {
|
||||
// This is a singular relationship. We want to refetch the object and set it directly.
|
||||
id valueToRefetch = [sourceObject valueForKey:destinationKey];
|
||||
[sourceObject setValue:RKRefetchedValueInManagedObjectContext(valueToRefetch, managedObjectContext) forKey:destinationKey];
|
||||
}
|
||||
value = (isMutable) ? newValue : [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSSet class]]) {
|
||||
BOOL isMutable = [value isKindOfClass:[NSMutableSet class]];
|
||||
NSMutableSet *newValue = [[NSMutableSet alloc] initWithCapacity:[value count]];
|
||||
for (__strong id object in value) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) object = RKRefetchManagedObjectInContext(object, managedObjectContext);
|
||||
if (object) [newValue addObject:object];
|
||||
}
|
||||
value = (isMutable) ? newValue : [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSOrderedSet class]]) {
|
||||
BOOL isMutable = [value isKindOfClass:[NSMutableOrderedSet class]];
|
||||
NSMutableOrderedSet *newValue = [NSMutableOrderedSet orderedSet];
|
||||
[(NSOrderedSet *)value enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) object = RKRefetchManagedObjectInContext(object, managedObjectContext);
|
||||
if (object) [newValue setObject:object atIndex:index];
|
||||
}];
|
||||
value = (isMutable) ? newValue : [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSManagedObject class]]) {
|
||||
value = RKRefetchManagedObjectInContext(value, managedObjectContext);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
RKSetMappedValueForKeyPathInDictionary(value, rootKey, keyPath, newDictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ NSSet *RKSetByRemovingSubkeypathsFromSet(NSSet *setOfKeyPaths);
|
||||
expect(managedObjectRequestOperation.mappingResult).notTo.beNil();
|
||||
NSArray *managedObjectContexts = [[managedObjectRequestOperation.mappingResult array] valueForKeyPath:@"@distinctUnionOfObjects.managedObjectContext"];
|
||||
expect([managedObjectContexts count]).to.equal(1);
|
||||
expect(managedObjectContexts[0]).to.equal(managedObjectStore.mainQueueManagedObjectContext);
|
||||
expect(managedObjectContexts).to.equal([NSArray arrayWithObject:managedObjectStore.mainQueueManagedObjectContext]);
|
||||
}
|
||||
|
||||
// 304 'Not Modified'
|
||||
@@ -830,7 +830,7 @@ NSSet *RKSetByRemovingSubkeypathsFromSet(NSSet *setOfKeyPaths);
|
||||
RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
|
||||
[entityMapping addAttributeMappingsFromArray:@[ @"name" ]];
|
||||
[userMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"favorite_cat" toKeyPath:@"bestFriend" withMapping:entityMapping]];
|
||||
[itemMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"items" toKeyPath:@"hasMany" withMapping:userMapping]];
|
||||
[itemMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"items.human" toKeyPath:@"hasMany" withMapping:userMapping]];
|
||||
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:itemMapping pathPattern:nil keyPath:@"result" statusCodes:[NSIndexSet indexSetWithIndex:200]];
|
||||
|
||||
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/JSON/humans/has_many_with_to_one_relationship.json" relativeToURL:[RKTestFactory baseURL]]];
|
||||
@@ -840,7 +840,9 @@ NSSet *RKSetByRemovingSubkeypathsFromSet(NSSet *setOfKeyPaths);
|
||||
expect(managedObjectRequestOperation.error).to.beNil();
|
||||
RKMappableObject *result = [managedObjectRequestOperation.mappingResult.array lastObject];
|
||||
RKTestUser *user = (RKTestUser *)result.hasMany.anyObject;
|
||||
expect(user.bestFriend.class).to.equal(RKHuman.class);
|
||||
NSLog(@"Examining result = %@, with result.hasMany = %@, user = %@ and user.bestFriend(%@) = %@", result, result.hasMany, user, [user.bestFriend class], user.bestFriend);
|
||||
expect(user.bestFriend).to.beInstanceOf([RKHuman class]);
|
||||
expect([user.bestFriend managedObjectContext]).to.equal(managedObjectStore.persistentStoreManagedObjectContext);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user