Fix issues with incorrect determination of the appropriate object request operation. fixes #1054, #1056

* Expands test coverage for the `appropriateObjectRequestOperationWithObject:method:path:parameters:`
* Uses an object graph visitor to completely navigate the mapping graph, ensuring that an `RKEntityMapping` appearing at any nesting level will be correctly handled
This commit is contained in:
Blake Watters
2012-12-07 12:40:02 -05:00
parent 0210424c6c
commit 997158e9e6
3 changed files with 144 additions and 10 deletions

View File

@@ -32,6 +32,7 @@
#import "RKMappingErrors.h"
#import "RKPaginator.h"
#import "RKDynamicMapping.h"
#import "RKRelationshipMapping.h"
#if !__has_feature(objc_arc)
#error RestKit must be built with ARC.
@@ -82,24 +83,85 @@ static RKRequestDescriptor *RKRequestDescriptorFromArrayMatchingObject(NSArray *
}
/**
Returns `YES` if the given array of `RKResponseDescriptor` objects contains an `RKEntityMapping`.
Visits all mappings accessible via relationships or dynamic mapping in an object graph starting from a given mapping.
*/
@interface RKMappingGraphVisitor : NSObject
@property (nonatomic, readonly) NSSet *mappings;
- (id)initWithMapping:(RKMapping *)mapping;
@end
@interface RKMappingGraphVisitor ()
@property (nonatomic, readwrite) NSMutableSet *mutableMappings;
@end
@implementation RKMappingGraphVisitor
- (id)initWithMapping:(RKMapping *)mapping
{
self = [super init];
if (self) {
self.mutableMappings = [NSMutableSet set];
[self visitMapping:mapping];
}
return self;
}
- (NSSet *)mappings
{
return self.mutableMappings;
}
- (void)visitMapping:(RKMapping *)mapping
{
if ([self.mappings containsObject:mapping]) return;
[self.mutableMappings addObject:mapping];
if ([mapping isKindOfClass:[RKDynamicMapping class]]) {
RKDynamicMapping *dynamicMapping = (RKDynamicMapping *)mapping;
for (RKMapping *nestedMapping in dynamicMapping.objectMappings) {
[self visitMapping:nestedMapping];
}
} else if ([mapping isKindOfClass:[RKObjectMapping class]]) {
RKObjectMapping *objectMapping = (RKObjectMapping *)mapping;
for (RKRelationshipMapping *relationshipMapping in objectMapping.relationshipMappings) {
[self visitMapping:relationshipMapping.mapping];
}
}
}
@end
/**
Returns `YES` if the given array of `RKResponseDescriptor` objects contains an `RKEntityMapping` anywhere in its object graph.
@param responseDescriptor An array of `RKResponseDescriptor` objects.
@return `YES` if the `mapping` property of any of the response descriptor objects in the given array is an instance of `RKEntityMapping`, else `NO`.
*/
static BOOL RKDoesArrayOfResponseDescriptorsContainEntityMapping(NSArray *responseDescriptors)
{
// Visit all mappings accessible from the object graphs of all response descriptors
NSMutableSet *accessibleMappings = [NSMutableSet set];
for (RKResponseDescriptor *responseDescriptor in responseDescriptors) {
if ([responseDescriptor.mapping isKindOfClass:[RKEntityMapping class]]) {
if (! [accessibleMappings containsObject:responseDescriptor.mapping]) {
RKMappingGraphVisitor *graphVisitor = [[RKMappingGraphVisitor alloc] initWithMapping:responseDescriptor.mapping];
[accessibleMappings unionSet:graphVisitor.mappings];
}
}
// Enumerate all mappings and search for an `RKEntityMapping`
for (RKMapping *mapping in accessibleMappings) {
if ([mapping isKindOfClass:[RKEntityMapping class]]) {
return YES;
}
if ([responseDescriptor.mapping isKindOfClass:[RKDynamicMapping class]]) {
RKDynamicMapping *dynamicMapping = (RKDynamicMapping *)responseDescriptor.mapping;
for (RKMapping *mapping in dynamicMapping.objectMappings) {
if ([mapping isKindOfClass:[RKEntityMapping class]]) {
return YES;
}
if ([mapping isKindOfClass:[RKDynamicMapping class]]) {
RKDynamicMapping *dynamicMapping = (RKDynamicMapping *)mapping;
if ([dynamicMapping.objectMappings count] == 0) {
// Likely means that there is a representation block, assume `YES`
return YES;
}
}
}