Merge branch 'bugfix/inappropriate-objectrequestoperations' into development

This commit is contained in:
Blake Watters
2012-12-07 12:40:18 -05:00
4 changed files with 148 additions and 3 deletions

View File

@@ -31,6 +31,8 @@
#import "RKPathMatcher.h"
#import "RKMappingErrors.h"
#import "RKPaginator.h"
#import "RKDynamicMapping.h"
#import "RKRelationshipMapping.h"
#if !__has_feature(objc_arc)
#error RestKit must be built with ARC.
@@ -81,17 +83,87 @@ 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 ([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;
}
}
}
return NO;

View File

@@ -24,6 +24,7 @@
// Cloned from AFStringFromIndexSet -- method should be non-static for reuse
static NSString *RKStringFromIndexSet(NSIndexSet *indexSet) {
NSCParameterAssert(indexSet);
NSMutableString *string = [NSMutableString string];
NSRange range = NSMakeRange([indexSet firstIndex], 1);
@@ -80,7 +81,7 @@ static NSString *RKStringFromIndexSet(NSIndexSet *indexSet) {
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p pathPattern=%@ keyPath=%@ statusCodes=%@ : %@>",
NSStringFromClass([self class]), self, self.pathPattern, self.keyPath, RKStringFromIndexSet(self.statusCodes), self.mapping];
NSStringFromClass([self class]), self, self.pathPattern, self.keyPath, self.statusCodes ? RKStringFromIndexSet(self.statusCodes) : self.statusCodes, self.mapping];
}
- (BOOL)matchesPath:(NSString *)path

View File

@@ -25,3 +25,4 @@
#import "RKObjectParameterization.h"
#import "RKMappingResult.h"
#import "RKMapperOperation.h"
#import "RKDynamicMapping.h"

View File

@@ -25,6 +25,7 @@
#import "RKHuman.h"
#import "RKCat.h"
#import "RKObjectMapperTestModel.h"
#import "RKDynamicMapping.h"
@interface RKSubclassedTestModel : RKObjectMapperTestModel
@end
@@ -526,6 +527,76 @@
expect(dictionary).to.equal(@{ @"subclassed": @{ @"age": @(30) } });
}
- (void)testThatResponseDescriptorWithUnmanagedMappingTriggersCreationOfObjectRequestOperation
{
RKObjectMapping *vanillaMapping = [RKObjectMapping requestMapping];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:vanillaMapping pathPattern:nil keyPath:nil statusCodes:nil];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]];
manager.managedObjectStore = [RKTestFactory managedObjectStore];
[manager addResponseDescriptor:responseDescriptor];
RKObjectRequestOperation *objectRequestOperation = [manager appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:@"/something" parameters:nil];
expect(objectRequestOperation).to.beInstanceOf([RKObjectRequestOperation class]);
}
- (void)testThatResponseDescriptorWithDynamicMappingContainingEntityMappingsTriggersCreationOfManagedObjectRequestOperation
{
RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:_objectManager.managedObjectStore];
RKDynamicMapping *dynamicMapping = [RKDynamicMapping new];
[dynamicMapping setObjectMapping:humanMapping whenValueOfKeyPath:@"whatever" isEqualTo:@"whatever"];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping pathPattern:nil keyPath:nil statusCodes:nil];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]];
manager.managedObjectStore = [RKTestFactory managedObjectStore];
[manager addResponseDescriptor:responseDescriptor];
RKObjectRequestOperation *objectRequestOperation = [manager appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:@"/something" parameters:nil];
expect(objectRequestOperation).to.beInstanceOf([RKManagedObjectRequestOperation class]);
}
- (void)testThatResponseDescriptorWithDynamicMappingUsingABlockTriggersCreationOfManagedObjectRequestOperation
{
RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:_objectManager.managedObjectStore];
RKDynamicMapping *dynamicMapping = [RKDynamicMapping new];
[dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
return humanMapping;
}];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping pathPattern:nil keyPath:nil statusCodes:nil];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]];
manager.managedObjectStore = [RKTestFactory managedObjectStore];
[manager addResponseDescriptor:responseDescriptor];
RKObjectRequestOperation *objectRequestOperation = [manager appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:@"/something" parameters:nil];
expect(objectRequestOperation).to.beInstanceOf([RKManagedObjectRequestOperation class]);
}
- (void)testThatResponseDescriptorWithUnmanagedMappingContainingRelationshipMappingWithEntityMappingsTriggersCreationOfManagedObjectRequestOperation
{
RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:_objectManager.managedObjectStore];
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[objectMapping addRelationshipMappingWithSourceKeyPath:@"relationship" mapping:humanMapping];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:objectMapping pathPattern:nil keyPath:nil statusCodes:nil];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]];
manager.managedObjectStore = [RKTestFactory managedObjectStore];
[manager addResponseDescriptor:responseDescriptor];
RKObjectRequestOperation *objectRequestOperation = [manager appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:@"/something" parameters:nil];
expect(objectRequestOperation).to.beInstanceOf([RKManagedObjectRequestOperation class]);
}
- (void)testThatResponseDescriptorWithUnmanagedMappingContainingRelationshipMappingWithEntityMappingsDeepWithinObjectGraphTriggersCreationOfManagedObjectRequestOperation
{
RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:_objectManager.managedObjectStore];
RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[objectMapping addRelationshipMappingWithSourceKeyPath:@"relationship" mapping:humanMapping];
RKObjectMapping *objectMapping2 = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[objectMapping2 addRelationshipMappingWithSourceKeyPath:@"relationship" mapping:objectMapping];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:objectMapping2 pathPattern:nil keyPath:nil statusCodes:nil];
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://restkit.org"]];
manager.managedObjectStore = [RKTestFactory managedObjectStore];
[manager addResponseDescriptor:responseDescriptor];
RKObjectRequestOperation *objectRequestOperation = [manager appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:@"/something" parameters:nil];
expect(objectRequestOperation).to.beInstanceOf([RKManagedObjectRequestOperation class]);
}
// Test with relationship 2 levels deep
// Test with recursive relationships
//- (void)testShouldHandleConnectionFailures
//{
// NSString *localBaseURL = [NSString stringWithFormat:@"http://127.0.0.1:3001"];