mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-23 12:27:52 +08:00
Introduced mapping contexts support.
Extended RKObjectMappingProvider to store collections of object mappings for different use cases. The framework now stores object mappings, serialization mappings, an error mapping and a pagination mapping using the context support. Contexts can be added to the provider via method calls or extension via a category.
This commit is contained in:
@@ -151,7 +151,7 @@
|
||||
|
||||
#pragma mark - Response Object Mapping
|
||||
|
||||
- (RKObjectMappingResult*)mapResponseWithMappingProvider:(RKObjectMappingProvider*)mappingProvider toObject:(id)targetObject error:(NSError**)error {
|
||||
- (RKObjectMappingResult*)mapResponseWithMappingProvider:(RKObjectMappingProvider*)mappingProvider toObject:(id)targetObject inContext:(RKObjectMappingProviderContext)context error:(NSError**)error {
|
||||
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:self.response.MIMEType];
|
||||
NSAssert1(parser, @"Cannot perform object load without a parser for MIME Type '%@'", self.response.MIMEType);
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider];
|
||||
mapper.targetObject = targetObject;
|
||||
mapper.delegate = self;
|
||||
mapper.context = context;
|
||||
RKObjectMappingResult* result = [mapper performMapping];
|
||||
|
||||
// Log any mapping errors
|
||||
@@ -205,15 +206,15 @@
|
||||
RKObjectMappingProvider* mappingProvider;
|
||||
if (self.objectMapping) {
|
||||
NSString* rootKeyPath = self.objectMapping.rootKeyPath ? self.objectMapping.rootKeyPath : @"";
|
||||
RKLogDebug(@"Found directly configured object mapping, creating temporary mapping provider for keyPath %@", rootKeyPath);
|
||||
mappingProvider = [[RKObjectMappingProvider new] autorelease];
|
||||
RKLogDebug(@"Found directly configured object mapping, creating temporary mapping provider for keyPath %@", rootKeyPath);
|
||||
mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
[mappingProvider setMapping:self.objectMapping forKeyPath:rootKeyPath];
|
||||
} else {
|
||||
RKLogDebug(@"No object mapping provider, using mapping provider from parent object manager to perform KVC mapping");
|
||||
mappingProvider = self.mappingProvider;
|
||||
}
|
||||
|
||||
return [self mapResponseWithMappingProvider:mappingProvider toObject:self.targetObject error:error];
|
||||
return [self mapResponseWithMappingProvider:mappingProvider toObject:self.targetObject inContext:RKObjectMappingProviderContextObjectsByKeyPath error:error];
|
||||
}
|
||||
|
||||
|
||||
@@ -283,8 +284,8 @@
|
||||
- (void)handleResponseError {
|
||||
// Since we are mapping what we know to be an error response, we don't want to map the result back onto our
|
||||
// target object
|
||||
NSError* error = nil;
|
||||
RKObjectMappingResult* result = [self mapResponseWithMappingProvider:self.mappingProvider toObject:nil error:&error];
|
||||
NSError *error = nil;
|
||||
RKObjectMappingResult *result = [self mapResponseWithMappingProvider:self.mappingProvider toObject:nil inContext:RKObjectMappingProviderContextErrors error:&error];
|
||||
if (result) {
|
||||
error = [result asError];
|
||||
} else {
|
||||
|
||||
@@ -58,9 +58,9 @@ static RKObjectManager* sharedManager = nil;
|
||||
|
||||
// Setup default error message mappings
|
||||
RKObjectMapping* errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
|
||||
errorMapping.rootKeyPath = @"errors";
|
||||
[errorMapping mapKeyPath:@"" toAttribute:@"errorMessage"];
|
||||
[_mappingProvider setMapping:errorMapping forKeyPath:@"error"];
|
||||
[_mappingProvider setMapping:errorMapping forKeyPath:@"errors"];
|
||||
_mappingProvider.errorMapping = errorMapping;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reachabilityChanged:)
|
||||
|
||||
@@ -48,17 +48,15 @@
|
||||
@end
|
||||
|
||||
@interface RKObjectMapper : NSObject {
|
||||
id _sourceObject;
|
||||
id _targetObject;
|
||||
RKObjectMappingProvider* _mappingProvider;
|
||||
id<RKObjectMapperDelegate> _delegate;
|
||||
NSMutableArray* _errors;
|
||||
RKMappingOperationQueue *_operationQueue;
|
||||
@protected
|
||||
RKMappingOperationQueue *operationQueue;
|
||||
NSMutableArray* errors;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) id sourceObject;
|
||||
@property (nonatomic, assign) id targetObject;
|
||||
@property (nonatomic, readonly) RKObjectMappingProvider* mappingProvider;
|
||||
@property (nonatomic, assign) RKObjectMappingProviderContext context;
|
||||
@property (nonatomic, assign) id<RKObjectMapperDelegate> delegate;
|
||||
@property (nonatomic, readonly) NSArray* errors;
|
||||
|
||||
|
||||
@@ -28,44 +28,50 @@
|
||||
|
||||
@implementation RKObjectMapper
|
||||
|
||||
@synthesize sourceObject = _sourceObject;
|
||||
@synthesize targetObject = _targetObject;
|
||||
@synthesize delegate =_delegate;
|
||||
@synthesize mappingProvider = _mappingProvider;
|
||||
@synthesize errors = _errors;
|
||||
@synthesize sourceObject;
|
||||
@synthesize targetObject;
|
||||
@synthesize delegate;
|
||||
@synthesize mappingProvider;
|
||||
@synthesize errors;
|
||||
@synthesize context;
|
||||
|
||||
+ (id)mapperWithObject:(id)object mappingProvider:(RKObjectMappingProvider*)mappingProvider {
|
||||
return [[[self alloc] initWithObject:object mappingProvider:mappingProvider] autorelease];
|
||||
+ (id)mapperWithObject:(id)object mappingProvider:(RKObjectMappingProvider *)theMappingProvider {
|
||||
return [[[self alloc] initWithObject:object mappingProvider:theMappingProvider] autorelease];
|
||||
}
|
||||
|
||||
- (id)initWithObject:(id)object mappingProvider:(RKObjectMappingProvider*)mappingProvider {
|
||||
- (id)initWithObject:(id)object mappingProvider:(RKObjectMappingProvider *)theMappingProvider {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_sourceObject = [object retain];
|
||||
_mappingProvider = mappingProvider;
|
||||
_errors = [NSMutableArray new];
|
||||
_operationQueue = [RKMappingOperationQueue new];
|
||||
sourceObject = [object retain];
|
||||
mappingProvider = theMappingProvider;
|
||||
errors = [NSMutableArray new];
|
||||
operationQueue = [RKMappingOperationQueue new];
|
||||
context = RKObjectMappingProviderContextObjectsByKeyPath;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_sourceObject release];
|
||||
[_errors release];
|
||||
[_operationQueue release];
|
||||
[sourceObject release];
|
||||
[errors release];
|
||||
[operationQueue release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#pragma mark - Errors
|
||||
|
||||
- (NSArray *)errors {
|
||||
return [NSArray arrayWithArray:errors];
|
||||
}
|
||||
|
||||
- (NSUInteger)errorCount {
|
||||
return [self.errors count];
|
||||
}
|
||||
|
||||
- (void)addError:(NSError*)error {
|
||||
- (void)addError:(NSError *)error {
|
||||
NSAssert(error, @"Cannot add a nil error");
|
||||
[_errors addObject:error];
|
||||
[errors addObject:error];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(objectMapper:didAddError:)]) {
|
||||
[self.delegate objectMapper:self didAddError:error];
|
||||
@@ -74,18 +80,18 @@
|
||||
RKLogWarning(@"Adding mapping error: %@", [error localizedDescription]);
|
||||
}
|
||||
|
||||
- (void)addErrorWithCode:(RKObjectMapperErrorCode)errorCode message:(NSString*)errorMessage keyPath:(NSString*)keyPath userInfo:(NSDictionary*)otherInfo {
|
||||
NSMutableDictionary* userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
- (void)addErrorWithCode:(RKObjectMapperErrorCode)errorCode message:(NSString *)errorMessage keyPath:(NSString *)keyPath userInfo:(NSDictionary *)otherInfo {
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
errorMessage, NSLocalizedDescriptionKey,
|
||||
@"RKObjectMapperKeyPath", keyPath ? keyPath : (NSString*) [NSNull null],
|
||||
@"RKObjectMapperKeyPath", keyPath ? keyPath : (NSString *) [NSNull null],
|
||||
nil];
|
||||
[userInfo addEntriesFromDictionary:otherInfo];
|
||||
NSError* error = [NSError errorWithDomain:RKRestKitErrorDomain code:errorCode userInfo:userInfo];
|
||||
NSError *error = [NSError errorWithDomain:RKRestKitErrorDomain code:errorCode userInfo:userInfo];
|
||||
[self addError:error];
|
||||
}
|
||||
|
||||
- (void)addErrorForUnmappableKeyPath:(NSString*)keyPath {
|
||||
NSString* errorMessage = [NSString stringWithFormat:@"Could not find an object mapping for keyPath: '%@'", keyPath];
|
||||
- (void)addErrorForUnmappableKeyPath:(NSString *)keyPath {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Could not find an object mapping for keyPath: '%@'", keyPath];
|
||||
[self addErrorWithCode:RKObjectMapperErrorObjectMappingNotFound message:errorMessage keyPath:keyPath userInfo:nil];
|
||||
}
|
||||
|
||||
@@ -111,22 +117,22 @@
|
||||
|
||||
#pragma mark - Mapping Primitives
|
||||
|
||||
- (id)mapObject:(id)mappableObject atKeyPath:(NSString*)keyPath usingMapping:(id<RKObjectMappingDefinition>)mapping {
|
||||
- (id)mapObject:(id)mappableObject atKeyPath:(NSString *)keyPath usingMapping:(id<RKObjectMappingDefinition>)mapping {
|
||||
NSAssert([mappableObject respondsToSelector:@selector(setValue:forKeyPath:)], @"Expected self.object to be KVC compliant");
|
||||
id destinationObject = nil;
|
||||
|
||||
if (self.targetObject) {
|
||||
destinationObject = self.targetObject;
|
||||
RKObjectMapping* objectMapping = nil;
|
||||
RKObjectMapping *objectMapping = nil;
|
||||
if ([mapping isKindOfClass:[RKDynamicObjectMapping class]]) {
|
||||
objectMapping = [(RKDynamicObjectMapping*)mapping objectMappingForDictionary:mappableObject];
|
||||
objectMapping = [(RKDynamicObjectMapping *)mapping objectMappingForDictionary:mappableObject];
|
||||
} else if ([mapping isKindOfClass:[RKObjectMapping class]]) {
|
||||
objectMapping = (RKObjectMapping*)mapping;
|
||||
objectMapping = (RKObjectMapping *)mapping;
|
||||
} else {
|
||||
NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([mapping class]));
|
||||
}
|
||||
if (NO == [[self.targetObject class] isSubclassOfClass:objectMapping.objectClass]) {
|
||||
NSString* errorMessage = [NSString stringWithFormat:
|
||||
NSString *errorMessage = [NSString stringWithFormat:
|
||||
@"Expected an object mapping for class of type '%@', provider returned one for '%@'",
|
||||
NSStringFromClass([self.targetObject class]), NSStringFromClass(objectMapping.objectClass)];
|
||||
[self addErrorWithCode:RKObjectMapperErrorObjectMappingTypeMismatch message:errorMessage keyPath:keyPath userInfo:nil];
|
||||
@@ -150,19 +156,19 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray*)mapCollection:(NSArray*)mappableObjects atKeyPath:(NSString*)keyPath usingMapping:(id<RKObjectMappingDefinition>)mapping {
|
||||
- (NSArray *)mapCollection:(NSArray *)mappableObjects atKeyPath:(NSString *)keyPath usingMapping:(id<RKObjectMappingDefinition>)mapping {
|
||||
NSAssert(mappableObjects != nil, @"Cannot map without an collection of mappable objects");
|
||||
NSAssert(mapping != nil, @"Cannot map without a mapping to consult");
|
||||
|
||||
NSArray* objectsToMap = mappableObjects;
|
||||
NSArray *objectsToMap = mappableObjects;
|
||||
if (mapping.forceCollectionMapping) {
|
||||
// If we have forced mapping of a dictionary, map each subdictionary
|
||||
if ([mappableObjects isKindOfClass:[NSDictionary class]]) {
|
||||
RKLogDebug(@"Collection mapping forced for NSDictionary, mapping each key/value independently...");
|
||||
objectsToMap = [NSMutableArray arrayWithCapacity:[mappableObjects count]];
|
||||
for (id key in mappableObjects) {
|
||||
NSDictionary* dictionaryToMap = [NSDictionary dictionaryWithObject:[mappableObjects valueForKey:key] forKey:key];
|
||||
[(NSMutableArray*)objectsToMap addObject:dictionaryToMap];
|
||||
NSDictionary *dictionaryToMap = [NSDictionary dictionaryWithObject:[mappableObjects valueForKey:key] forKey:key];
|
||||
[(NSMutableArray *)objectsToMap addObject:dictionaryToMap];
|
||||
}
|
||||
} else {
|
||||
RKLogWarning(@"Collection mapping forced but mappable objects is of type '%@' rather than NSDictionary", NSStringFromClass([mappableObjects class]));
|
||||
@@ -170,9 +176,9 @@
|
||||
}
|
||||
|
||||
// Ensure we are mapping onto a mutable collection if there is a target
|
||||
NSMutableArray* mappedObjects = self.targetObject ? self.targetObject : [NSMutableArray arrayWithCapacity:[mappableObjects count]];
|
||||
NSMutableArray *mappedObjects = self.targetObject ? self.targetObject : [NSMutableArray arrayWithCapacity:[mappableObjects count]];
|
||||
if (NO == [mappedObjects respondsToSelector:@selector(addObject:)]) {
|
||||
NSString* errorMessage = [NSString stringWithFormat:
|
||||
NSString *errorMessage = [NSString stringWithFormat:
|
||||
@"Cannot map a collection of objects onto a non-mutable collection. Unexpected destination object type '%@'",
|
||||
NSStringFromClass([mappedObjects class])];
|
||||
[self addErrorWithCode:RKObjectMapperErrorObjectMappingTypeMismatch message:errorMessage keyPath:keyPath userInfo:nil];
|
||||
@@ -205,12 +211,12 @@
|
||||
[self.delegate objectMapper:self willMapFromObject:mappableObject toObject:destinationObject atKeyPath:keyPath usingMapping:mapping];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
NSError *error = nil;
|
||||
|
||||
RKObjectMappingOperation* operation = [RKObjectMappingOperation mappingOperationFromObject:mappableObject
|
||||
RKObjectMappingOperation *operation = [RKObjectMappingOperation mappingOperationFromObject:mappableObject
|
||||
toObject:destinationObject
|
||||
withMapping:mapping];
|
||||
operation.queue = _operationQueue;
|
||||
operation.queue = operationQueue;
|
||||
BOOL success = [operation performMapping:&error];
|
||||
if (success) {
|
||||
if ([self.delegate respondsToSelector:@selector(objectMapper:didMapFromObject:toObject:atKeyPath:usingMapping:)]) {
|
||||
@@ -228,14 +234,14 @@
|
||||
|
||||
- (id)objectWithMapping:(id<RKObjectMappingDefinition>)mapping andData:(id)mappableData {
|
||||
NSAssert([mapping conformsToProtocol:@protocol(RKObjectMappingDefinition)], @"Expected an object implementing RKObjectMappingDefinition");
|
||||
RKObjectMapping* objectMapping = nil;
|
||||
RKObjectMapping *objectMapping = nil;
|
||||
if ([mapping isKindOfClass:[RKDynamicObjectMapping class]]) {
|
||||
objectMapping = [(RKDynamicObjectMapping*)mapping objectMappingForDictionary:mappableData];
|
||||
objectMapping = [(RKDynamicObjectMapping *)mapping objectMappingForDictionary:mappableData];
|
||||
if (! objectMapping) {
|
||||
RKLogDebug(@"Mapping %@ declined mapping for data %@: returned nil objectMapping", mapping, mappableData);
|
||||
}
|
||||
} else if ([mapping isKindOfClass:[RKObjectMapping class]]) {
|
||||
objectMapping = (RKObjectMapping*)mapping;
|
||||
objectMapping = (RKObjectMapping *)mapping;
|
||||
} else {
|
||||
NSAssert(objectMapping, @"Encountered unknown mapping type '%@'", NSStringFromClass([mapping class]));
|
||||
}
|
||||
@@ -247,24 +253,25 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Primary entry point for the mapper.
|
||||
- (RKObjectMappingResult*)performMapping {
|
||||
NSAssert(self.sourceObject != nil, @"Cannot perform object mapping without a source object to map from");
|
||||
NSAssert(self.mappingProvider != nil, @"Cannot perform object mapping without an object mapping provider");
|
||||
|
||||
RKLogDebug(@"Performing object mapping sourceObject: %@\n and targetObject: %@", self.sourceObject, self.targetObject);
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(objectMapperWillBeginMapping:)]) {
|
||||
[self.delegate objectMapperWillBeginMapping:self];
|
||||
- (id)performMappingForObject:(id)mappableValue atKeyPath:(NSString *)keyPath usingMapping:(id<RKObjectMappingDefinition>)mapping {
|
||||
id mappingResult;
|
||||
if (mapping.forceCollectionMapping || [mappableValue isKindOfClass:[NSArray class]] || [mappableValue isKindOfClass:[NSSet class]]) {
|
||||
RKLogDebug(@"Found mappable collection at keyPath '%@': %@", keyPath, mappableValue);
|
||||
mappingResult = [self mapCollection:mappableValue atKeyPath:keyPath usingMapping:mapping];
|
||||
} else {
|
||||
RKLogDebug(@"Found mappable data at keyPath '%@': %@", keyPath, mappableValue);
|
||||
mappingResult = [self mapObject:mappableValue atKeyPath:keyPath usingMapping:mapping];
|
||||
}
|
||||
|
||||
// Perform the mapping
|
||||
return mappingResult;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)performKeyPathMappingUsingMappingDictionary:(NSDictionary *)mappingsByKeyPath {
|
||||
BOOL foundMappable = NO;
|
||||
NSMutableDictionary* results = [NSMutableDictionary dictionary];
|
||||
NSDictionary* mappingsByKeyPath = [self.mappingProvider mappingsByKeyPath];
|
||||
for (NSString* keyPath in mappingsByKeyPath) {
|
||||
id mappingResult;
|
||||
id mappableValue;
|
||||
NSMutableDictionary *results = [NSMutableDictionary dictionary];
|
||||
for (NSString *keyPath in mappingsByKeyPath) {
|
||||
id mappingResult = nil;
|
||||
id mappableValue = nil;
|
||||
|
||||
RKLogTrace(@"Examining keyPath '%@' for mappable content...", keyPath);
|
||||
|
||||
@@ -291,22 +298,51 @@
|
||||
if ([self.delegate respondsToSelector:@selector(objectMapper:didFindMappableObject:atKeyPath:withMapping:)]) {
|
||||
[self.delegate objectMapper:self didFindMappableObject:mappableValue atKeyPath:keyPath withMapping:mapping];
|
||||
}
|
||||
if (mapping.forceCollectionMapping || [mappableValue isKindOfClass:[NSArray class]] || [mappableValue isKindOfClass:[NSSet class]]) {
|
||||
RKLogDebug(@"Found mappable collection at keyPath '%@': %@", keyPath, mappableValue);
|
||||
mappingResult = [self mapCollection:mappableValue atKeyPath:keyPath usingMapping:mapping];
|
||||
} else {
|
||||
RKLogDebug(@"Found mappable data at keyPath '%@': %@", keyPath, mappableValue);
|
||||
mappingResult = [self mapObject:mappableValue atKeyPath:keyPath usingMapping:mapping];
|
||||
}
|
||||
|
||||
mappingResult = [self performMappingForObject:mappableValue atKeyPath:keyPath usingMapping:mapping];
|
||||
|
||||
if (mappingResult) {
|
||||
[results setObject:mappingResult forKey:keyPath];
|
||||
}
|
||||
}
|
||||
|
||||
if (NO == foundMappable) return nil;
|
||||
return results;
|
||||
}
|
||||
|
||||
// Primary entry point for the mapper.
|
||||
- (RKObjectMappingResult *)performMapping {
|
||||
NSAssert(self.sourceObject != nil, @"Cannot perform object mapping without a source object to map from");
|
||||
NSAssert(self.mappingProvider != nil, @"Cannot perform object mapping without an object mapping provider");
|
||||
|
||||
RKLogDebug(@"Performing object mapping sourceObject: %@\n and targetObject: %@", self.sourceObject, self.targetObject);
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(objectMapperWillBeginMapping:)]) {
|
||||
[self.delegate objectMapperWillBeginMapping:self];
|
||||
}
|
||||
|
||||
// Perform the mapping
|
||||
BOOL foundMappable = NO;
|
||||
NSMutableDictionary *results = nil;
|
||||
|
||||
// Handle mapping selection for context
|
||||
id mappingsForContext = [self.mappingProvider valueForContext:context];
|
||||
if ([mappingsForContext isKindOfClass:[NSDictionary class]]) {
|
||||
results = [self performKeyPathMappingUsingMappingDictionary:mappingsForContext];
|
||||
foundMappable = (results != nil);
|
||||
} else if ([mappingsForContext conformsToProtocol:@protocol(RKObjectMappingDefinition)]) {
|
||||
id mappableData = self.sourceObject;
|
||||
if ([mappingsForContext respondsToSelector:@selector(rootKeyPath)] && [mappingsForContext rootKeyPath] != nil) {
|
||||
mappableData = [self.sourceObject valueForKeyPath:[mappingsForContext rootKeyPath]];
|
||||
}
|
||||
id mappingResult = [self performMappingForObject:mappableData atKeyPath:@"" usingMapping:mappingsForContext];
|
||||
foundMappable = YES;
|
||||
results = [NSDictionary dictionaryWithObject:mappingResult forKey:@""];
|
||||
}
|
||||
|
||||
// Allow any queued operations to complete
|
||||
RKLogDebug(@"The following operations are in the queue: %@", _operationQueue.operations);
|
||||
[_operationQueue waitUntilAllOperationsAreFinished];
|
||||
RKLogDebug(@"The following operations are in the queue: %@", operationQueue.operations);
|
||||
[operationQueue waitUntilAllOperationsAreFinished];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(objectMapperDidFinishMapping:)]) {
|
||||
[self.delegate objectMapperDidFinishMapping:self];
|
||||
|
||||
@@ -121,7 +121,7 @@ NSString* const RKObjectMappingNestingAttributeKeyName = @"<RK_NESTING_ATTRIBUTE
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"RKObjectMapping class => %@: keyPath mappings => %@", NSStringFromClass(self.objectClass), _mappings];
|
||||
return [NSString stringWithFormat:@"<%@:%p objectClass=%@ keyPath mappings => %@>", NSStringFromClass([self class]), self, NSStringFromClass(self.objectClass), _mappings];
|
||||
}
|
||||
|
||||
- (id)mappingForKeyPath:(NSString*)keyPath {
|
||||
|
||||
@@ -21,6 +21,16 @@
|
||||
#import "RKObjectMapping.h"
|
||||
#import "RKDynamicObjectMapping.h"
|
||||
|
||||
// Internal framework contexts
|
||||
typedef enum {
|
||||
RKObjectMappingProviderContextObjectsByKeyPath = 1000,
|
||||
RKObjectMappingProviderContextObjectsByType,
|
||||
RKObjectMappingProviderContextObjectsByURL,
|
||||
RKObjectMappingProviderContextSerialization,
|
||||
RKObjectMappingProviderContextErrors,
|
||||
RKObjectMappingProviderContextPagination
|
||||
} RKObjectMappingProviderContext;
|
||||
|
||||
/**
|
||||
The mapping provider is a repository of registered object mappings for use by instances
|
||||
of RKObjectManager and RKObjectMapper. It provides for the storage and retrieval of object
|
||||
@@ -42,14 +52,11 @@
|
||||
that they target.
|
||||
*/
|
||||
@interface RKObjectMappingProvider : NSObject {
|
||||
NSMutableArray *_objectMappings;
|
||||
NSMutableDictionary *_mappingsByKeyPath;
|
||||
NSMutableDictionary *_serializationMappings;
|
||||
NSMutableArray *_errorMappings;
|
||||
NSMutableDictionary *mappingContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a new autoreleased object mapping provider
|
||||
Creates and returns an autoreleased RKObjectMappingProvider instance.
|
||||
|
||||
@return A new autoreleased object mapping provider instance.
|
||||
*/
|
||||
@@ -197,16 +204,13 @@
|
||||
- (RKObjectMapping *)serializationMappingForClass:(Class)objectClass;
|
||||
|
||||
/**
|
||||
Adds an object mapping to the provider to be used when mapping a payload known
|
||||
to contain an error representation.
|
||||
An object mapping used when the remote system returns an error status code
|
||||
and a payload with a MIME Type that RestKit is capable of parsing.
|
||||
|
||||
@param errorMapping The error mapping to add to the provider
|
||||
@see RKObjectLoader
|
||||
@see RKParserRegistry
|
||||
*/
|
||||
// TODO: Should these be key-path based?
|
||||
- (void)addErrorMapping:(RKObjectMapping *)errorMapping;
|
||||
- (void)removeErrorMapping:(RKObjectMapping *)errorMapping;
|
||||
- (NSArray *)errorMappings;
|
||||
@property (nonatomic, retain) RKObjectMapping *errorMapping;
|
||||
|
||||
/**
|
||||
An object mapping used when mapping pagination metadata (current page, object count, etc)
|
||||
@@ -215,12 +219,12 @@
|
||||
|
||||
For example, if using the popular will_paginate plugin with Ruby on Rails, we would configure
|
||||
our pagination mapping like so:
|
||||
|
||||
// Assumes the JSON format of http://stackoverflow.com/questions/4699182/will-paginate-json-support
|
||||
RKObjectMapping *paginationMapping = [RKObjectMapping mappingForClass:[RKObjectPaginator class]];
|
||||
[paginationMapping mapKeyPath:@"current_page" toAttribute:@"currentPage"];
|
||||
[paginationMapping mapKeyPath:@"per_page" toAttribute:@"perPage"];
|
||||
[paginationMapping mapKeyPath:@"total_entries" toAttribute:@"objectCount"];
|
||||
|
||||
// Assumes the JSON format of http://stackoverflow.com/questions/4699182/will-paginate-json-support
|
||||
RKObjectMapping *paginationMapping = [RKObjectMapping mappingForClass:[RKObjectPaginator class]];
|
||||
[paginationMapping mapKeyPath:@"current_page" toAttribute:@"currentPage"];
|
||||
[paginationMapping mapKeyPath:@"per_page" toAttribute:@"perPage"];
|
||||
[paginationMapping mapKeyPath:@"total_entries" toAttribute:@"objectCount"];
|
||||
|
||||
@see RKObjectPaginator
|
||||
*/
|
||||
@@ -228,6 +232,27 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface RKObjectMappingProvider (Contexts)
|
||||
|
||||
- (void)initializeContext:(RKObjectMappingProviderContext)context withValue:(id)value;
|
||||
- (id)valueForContext:(RKObjectMappingProviderContext)context;
|
||||
- (void)setValue:(id)value forContext:(RKObjectMappingProviderContext)context;
|
||||
|
||||
- (id<RKObjectMappingDefinition>)mappingForContext:(RKObjectMappingProviderContext)context;
|
||||
/**
|
||||
Stores a single object mapping for a given context. Useful when a component needs to enable
|
||||
configuration via one (and only one) object mapping.
|
||||
*/
|
||||
- (void)setMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context;
|
||||
- (NSArray *)mappingsForContext:(RKObjectMappingProviderContext)context;
|
||||
- (void)addMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context;
|
||||
- (void)removeMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context;
|
||||
- (id<RKObjectMappingDefinition>)mappingForKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context;
|
||||
- (void)setMapping:(id<RKObjectMappingDefinition>)mapping forKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context;
|
||||
- (void)removeMappingForKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context;
|
||||
|
||||
@end
|
||||
|
||||
// Method signatures being phased out
|
||||
@interface RKObjectMappingProvider (CompatibilityAliases)
|
||||
+ (RKObjectMappingProvider *)objectMappingProvider;
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
|
||||
@implementation RKObjectMappingProvider
|
||||
|
||||
@synthesize paginationMapping;
|
||||
|
||||
+ (RKObjectMappingProvider *)mappingProvider {
|
||||
return [[self new] autorelease];
|
||||
}
|
||||
@@ -31,42 +29,43 @@
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_objectMappings = [NSMutableArray new];
|
||||
_mappingsByKeyPath = [NSMutableDictionary new];
|
||||
_serializationMappings = [NSMutableDictionary new];
|
||||
mappingContexts = [NSMutableDictionary new];
|
||||
[self initializeContext:RKObjectMappingProviderContextObjectsByKeyPath withValue:[NSMutableDictionary dictionary]];
|
||||
[self initializeContext:RKObjectMappingProviderContextObjectsByType withValue:[NSMutableArray array]];
|
||||
[self initializeContext:RKObjectMappingProviderContextObjectsByURL withValue:[NSMutableDictionary dictionary]];
|
||||
[self initializeContext:RKObjectMappingProviderContextSerialization withValue:[NSMutableDictionary dictionary]];
|
||||
[self initializeContext:RKObjectMappingProviderContextErrors withValue:[NSMutableArray array]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_objectMappings release];
|
||||
[_mappingsByKeyPath release];
|
||||
[_serializationMappings release];
|
||||
[mappingContexts release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)setObjectMapping:(id<RKObjectMappingDefinition>)objectOrDynamicMapping forKeyPath:(NSString *)keyPath {
|
||||
[_mappingsByKeyPath setValue:objectOrDynamicMapping forKey:keyPath];
|
||||
[self setMapping:objectOrDynamicMapping forKeyPath:keyPath context:RKObjectMappingProviderContextObjectsByKeyPath];
|
||||
}
|
||||
|
||||
- (void)removeObjectMappingForKeyPath:(NSString *)keyPath {
|
||||
[_mappingsByKeyPath removeObjectForKey:keyPath];
|
||||
[self removeMappingForKeyPath:keyPath context:RKObjectMappingProviderContextObjectsByKeyPath];
|
||||
}
|
||||
|
||||
- (id<RKObjectMappingDefinition>)objectMappingForKeyPath:(NSString *)keyPath {
|
||||
return [_mappingsByKeyPath objectForKey:keyPath];
|
||||
return [self mappingForKeyPath:keyPath context:RKObjectMappingProviderContextObjectsByKeyPath];
|
||||
}
|
||||
|
||||
- (void)setSerializationMapping:(RKObjectMapping *)mapping forClass:(Class)objectClass {
|
||||
[_serializationMappings setValue:mapping forKey:NSStringFromClass(objectClass)];
|
||||
[self setMapping:mapping forKeyPath:NSStringFromClass(objectClass) context:RKObjectMappingProviderContextSerialization];
|
||||
}
|
||||
|
||||
- (RKObjectMapping*)serializationMappingForClass:(Class)objectClass {
|
||||
return (RKObjectMapping *)[_serializationMappings objectForKey:NSStringFromClass(objectClass)];
|
||||
- (RKObjectMapping *)serializationMappingForClass:(Class)objectClass {
|
||||
return [self mappingForKeyPath:NSStringFromClass(objectClass) context:RKObjectMappingProviderContextSerialization];
|
||||
}
|
||||
|
||||
- (NSDictionary*)objectMappingsByKeyPath {
|
||||
return _mappingsByKeyPath;
|
||||
return [NSDictionary dictionaryWithDictionary:(NSDictionary *) [self valueForContext:RKObjectMappingProviderContextObjectsByKeyPath]];
|
||||
}
|
||||
|
||||
- (void)registerObjectMapping:(RKObjectMapping *)objectMapping withRootKeyPath:(NSString *)keyPath {
|
||||
@@ -79,12 +78,14 @@
|
||||
}
|
||||
|
||||
- (void)addObjectMapping:(RKObjectMapping *)objectMapping {
|
||||
[_objectMappings addObject:objectMapping];
|
||||
[self addMapping:objectMapping context:RKObjectMappingProviderContextObjectsByType];
|
||||
}
|
||||
|
||||
- (NSArray *)objectMappingsForClass:(Class)theClass {
|
||||
NSMutableArray *mappings = [NSMutableArray array];
|
||||
NSArray *mappingsToSearch = [[NSArray arrayWithArray:_objectMappings] arrayByAddingObjectsFromArray:[_mappingsByKeyPath allValues]];
|
||||
NSArray *mappingByType = [self valueForContext:RKObjectMappingProviderContextObjectsByType];
|
||||
NSArray *mappingByKeyPath = [[self valueForContext:RKObjectMappingProviderContextObjectsByKeyPath] allValues];
|
||||
NSArray *mappingsToSearch = [[NSArray arrayWithArray:mappingByType] arrayByAddingObjectsFromArray:mappingByKeyPath];
|
||||
for (NSObject <RKObjectMappingDefinition> *candidateMapping in mappingsToSearch) {
|
||||
if ( ![candidateMapping respondsToSelector:@selector(objectClass)] || [mappings containsObject:candidateMapping])
|
||||
continue;
|
||||
@@ -101,16 +102,108 @@
|
||||
return ([objectMappings count] > 0) ? [objectMappings objectAtIndex:0] : nil;
|
||||
}
|
||||
|
||||
- (void)addErrorMapping:(RKObjectMapping *)errorMapping {
|
||||
[_errorMappings addObject:errorMapping];
|
||||
#pragma mark - Error Mappings
|
||||
|
||||
- (RKObjectMapping *)errorMapping {
|
||||
return [self mappingForContext:RKObjectMappingProviderContextErrors];
|
||||
}
|
||||
|
||||
- (void)removeErrorMapping:(RKObjectMapping *)errorMapping {
|
||||
[_errorMappings removeObject:errorMapping];
|
||||
- (void)setErrorMapping:(RKObjectMapping *)errorMapping {
|
||||
[self setMapping:errorMapping context:RKObjectMappingProviderContextErrors];
|
||||
}
|
||||
|
||||
- (NSArray *)errorMappings {
|
||||
return [[_errorMappings copy] autorelease];
|
||||
#pragma mark - Pagination Mapping
|
||||
|
||||
- (RKObjectMapping *)paginationMapping {
|
||||
return [self mappingForContext:RKObjectMappingProviderContextPagination];
|
||||
}
|
||||
|
||||
- (void)setPaginationMapping:(RKObjectMapping *)paginationMapping {
|
||||
[self setMapping:paginationMapping context:RKObjectMappingProviderContextPagination];
|
||||
}
|
||||
|
||||
#pragma mark - Mapping Context Primitives
|
||||
|
||||
- (void)initializeContext:(RKObjectMappingProviderContext)context withValue:(id)value {
|
||||
NSAssert([self valueForContext:context] == nil, @"Attempt to reinitialized an existing mapping provider context.");
|
||||
[self setValue:value forContext:context];
|
||||
}
|
||||
|
||||
- (id)valueForContext:(RKObjectMappingProviderContext)context {
|
||||
NSNumber *contextNumber = [NSNumber numberWithInteger:context];
|
||||
return [mappingContexts objectForKey:contextNumber];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forContext:(RKObjectMappingProviderContext)context {
|
||||
NSNumber *contextNumber = [NSNumber numberWithInteger:context];
|
||||
[mappingContexts setObject:value forKey:contextNumber];
|
||||
}
|
||||
|
||||
- (void)assertStorageForContext:(RKObjectMappingProviderContext)context isKindOfClass:(Class)theClass {
|
||||
id contextValue = [self valueForContext:context];
|
||||
NSAssert([contextValue isKindOfClass:theClass], @"Storage type mismatch for context %d: expected a %@, got %@.", context, theClass, [contextValue class]);
|
||||
}
|
||||
|
||||
- (void)setMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context {
|
||||
NSNumber *contextNumber = [NSNumber numberWithInteger:context];
|
||||
[mappingContexts setObject:mapping forKey:contextNumber];
|
||||
}
|
||||
|
||||
- (id<RKObjectMappingDefinition>)mappingForContext:(RKObjectMappingProviderContext)context {
|
||||
id contextValue = [self valueForContext:context];
|
||||
if (contextValue == nil) return nil;
|
||||
Protocol *protocol = @protocol(RKObjectMappingDefinition);
|
||||
NSAssert([contextValue conformsToProtocol:protocol], @"Storage type mismatch for context %d: expected a %@, got %@.", context, protocol, [contextValue class]);
|
||||
return contextValue;
|
||||
}
|
||||
|
||||
- (NSArray *)mappingsForContext:(RKObjectMappingProviderContext)context {
|
||||
id contextValue = [self valueForContext:context];
|
||||
if (contextValue == nil) return [NSArray array];
|
||||
[self assertStorageForContext:context isKindOfClass:[NSArray class]];
|
||||
|
||||
return [NSArray arrayWithArray:contextValue];
|
||||
}
|
||||
|
||||
- (void)addMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context {
|
||||
NSMutableArray *contextValue = [self valueForContext:context];
|
||||
if (contextValue == nil) {
|
||||
contextValue = [NSMutableArray arrayWithCapacity:1];
|
||||
[self setValue:contextValue forContext:context];
|
||||
}
|
||||
[self assertStorageForContext:context isKindOfClass:[NSArray class]];
|
||||
[contextValue addObject:mapping];
|
||||
}
|
||||
|
||||
- (void)removeMapping:(id<RKObjectMappingDefinition>)mapping context:(RKObjectMappingProviderContext)context {
|
||||
NSMutableArray *contextValue = [self valueForContext:context];
|
||||
NSAssert(contextValue, @"Attempted to remove mapping from undefined context: %d", context);
|
||||
[self assertStorageForContext:context isKindOfClass:[NSArray class]];
|
||||
NSAssert([contextValue containsObject:mapping], @"Attempted to remove mapping from collection that does not include it for context: %d", context);
|
||||
[contextValue removeObject:mapping];
|
||||
}
|
||||
|
||||
- (id<RKObjectMappingDefinition>)mappingForKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context {
|
||||
NSMutableDictionary *contextValue = [self valueForContext:context];
|
||||
NSAssert(contextValue, @"Attempted to retrieve mapping from undefined context: %d", context);
|
||||
[self assertStorageForContext:context isKindOfClass:[NSDictionary class]];
|
||||
return [contextValue valueForKey:keyPath];
|
||||
}
|
||||
|
||||
- (void)setMapping:(id<RKObjectMappingDefinition>)mapping forKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context {
|
||||
NSMutableDictionary *contextValue = [self valueForContext:context];
|
||||
if (contextValue == nil) {
|
||||
contextValue = [NSMutableDictionary dictionary];
|
||||
[self setValue:contextValue forContext:context];
|
||||
}
|
||||
[self assertStorageForContext:context isKindOfClass:[NSDictionary class]];
|
||||
[contextValue setValue:mapping forKey:keyPath];
|
||||
}
|
||||
|
||||
- (void)removeMappingForKeyPath:(NSString *)keyPath context:(RKObjectMappingProviderContext)context {
|
||||
NSMutableDictionary *contextValue = [self valueForContext:context];
|
||||
[self assertStorageForContext:context isKindOfClass:[NSDictionary class]];
|
||||
[contextValue removeObjectForKey:keyPath];
|
||||
}
|
||||
|
||||
#pragma mark - Aliases
|
||||
|
||||
@@ -121,21 +121,63 @@
|
||||
/// The number of objects to load per page
|
||||
@property (nonatomic, assign) NSUInteger perPage;
|
||||
|
||||
/// The number of pages in the total collection
|
||||
@property (nonatomic, readonly) NSUInteger pageCount;
|
||||
|
||||
/// The total number of objects in the collection
|
||||
@property (nonatomic, readonly) NSUInteger objectCount;
|
||||
|
||||
/// The current page number the paginator has loaded
|
||||
@property (nonatomic, readonly) NSUInteger currentPage;
|
||||
|
||||
/// Returns YES when the paginator has loaded a page of objects
|
||||
@property (nonatomic, readonly, getter = isLoaded) BOOL loaded;
|
||||
|
||||
/// Returns YES when there is a next page to load
|
||||
/**
|
||||
Returns the page number for the most recently loaded page of objects.
|
||||
|
||||
@return The page number for the current page of objects.
|
||||
@exception NSInternalInconsistencyException Raised if isLoaded is NO.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSUInteger currentPage;
|
||||
|
||||
/**
|
||||
Returns the number of pages in the total resource collection.
|
||||
|
||||
@return A count of the number of pages in the resource collection.
|
||||
@exception NSInternalInconsistencyException Raised if hasPageCount is NO.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSUInteger pageCount;
|
||||
|
||||
/**
|
||||
Returns the total number of objects in the collection
|
||||
|
||||
@return A count of the number of objects in the resource collection.
|
||||
@exception NSInternalInconsistencyException Raised if hasObjectCount is NO.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSUInteger objectCount;
|
||||
|
||||
/**
|
||||
Returns a Boolean value indicating if the total number of pages in the collection
|
||||
is known by the paginator.
|
||||
|
||||
@return YES if the paginator knows the page count, otherwise NO
|
||||
*/
|
||||
- (BOOL)hasPageCount;
|
||||
|
||||
/**
|
||||
Returns a Boolean value indicating if the total number of objects in the collection
|
||||
is known by the paginator.
|
||||
|
||||
@return YES if the paginator knows the number of objects in the paginated collection, otherwise NO.
|
||||
*/
|
||||
- (BOOL)hasObjectCount;
|
||||
|
||||
/**
|
||||
Returns a Boolean value indicating if there is a next page in the collection.
|
||||
|
||||
@return YES if there is a next page, otherwise NO.
|
||||
@exception NSInternalInconsistencyException Raised if isLoaded or hasPageCount is NO.
|
||||
*/
|
||||
- (BOOL)hasNextPage;
|
||||
|
||||
/// Returns YES when there is a previous page to load
|
||||
/**
|
||||
Returns a Boolean value indicating if there is a previous page in the collection.
|
||||
|
||||
@return YES if there is a previous page, otherwise NO.
|
||||
@exception NSInternalInconsistencyException Raised if isLoaded is NO.
|
||||
*/
|
||||
- (BOOL)hasPreviousPage;
|
||||
|
||||
/** @name Paginator Actions */
|
||||
@@ -199,13 +241,6 @@
|
||||
*/
|
||||
- (void)paginator:(RKObjectPaginator *)paginator willLoadPage:(NSUInteger)page objectLoader:(RKObjectLoader *)loader;
|
||||
|
||||
/**
|
||||
Sent to the delegate when the paginator has loaded the last page in the collection
|
||||
|
||||
@param paginator The paginator instance that has loaded the last page
|
||||
*/
|
||||
- (void)paginatorDidLoadLastPage:(RKObjectPaginator *)paginator;
|
||||
|
||||
/**
|
||||
Sent to the delegate when the paginator has loaded the first page in the collection
|
||||
|
||||
@@ -213,4 +248,11 @@
|
||||
*/
|
||||
- (void)paginatorDidLoadFirstPage:(RKObjectPaginator *)paginator;
|
||||
|
||||
/**
|
||||
Sent to the delegate when the paginator has loaded the last page in the collection
|
||||
|
||||
@param paginator The paginator instance that has loaded the last page
|
||||
*/
|
||||
- (void)paginatorDidLoadLastPage:(RKObjectPaginator *)paginator;
|
||||
|
||||
@end
|
||||
|
||||
@@ -54,7 +54,9 @@ static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
|
||||
if (self) {
|
||||
patternURL = [aPatternURL copy];
|
||||
mappingProvider = [aMappingProvider retain];
|
||||
currentPage = 0;
|
||||
currentPage = NSUIntegerMax;
|
||||
pageCount = NSUIntegerMax;
|
||||
objectCount = NSUIntegerMax;
|
||||
perPage = RKObjectPaginatorDefaultPerPage;
|
||||
loaded = NO;
|
||||
}
|
||||
@@ -72,6 +74,8 @@ static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
|
||||
mappingProvider = nil;
|
||||
[objectStore release];
|
||||
objectStore = nil;
|
||||
[objectLoader cancel];
|
||||
objectLoader.delegate = nil;
|
||||
[objectLoader release];
|
||||
objectLoader = nil;
|
||||
|
||||
@@ -86,6 +90,42 @@ static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
|
||||
return [patternURL URLByInterpolatingResourcePathWithObject:self];
|
||||
}
|
||||
|
||||
// Private. Public consumers can rely on isLoaded
|
||||
- (BOOL)hasCurrentPage {
|
||||
return currentPage != NSUIntegerMax;
|
||||
}
|
||||
|
||||
- (BOOL)hasPageCount {
|
||||
return pageCount != NSUIntegerMax;
|
||||
}
|
||||
|
||||
- (BOOL)hasObjectCount {
|
||||
return objectCount != NSUIntegerMax;
|
||||
}
|
||||
|
||||
- (NSUInteger)currentPage {
|
||||
// Referenced during initial load, so we don't rely on isLoaded.
|
||||
NSAssert([self hasCurrentPage], @"Current page has not been initialized.");
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
- (NSUInteger)pageCount {
|
||||
NSAssert([self hasPageCount], @"Page count not available.");
|
||||
return pageCount;
|
||||
}
|
||||
|
||||
- (BOOL)hasNextPage {
|
||||
NSAssert(self.isLoaded, @"Cannot determine hasNextPage: paginator is not loaded.");
|
||||
NSAssert([self hasPageCount], @"Cannot determine hasNextPage: page count is not known.");
|
||||
|
||||
return self.currentPage < self.pageCount;
|
||||
}
|
||||
|
||||
- (BOOL)hasPreviousPage {
|
||||
NSAssert(self.isLoaded, @"Cannot determine hasPreviousPage: paginator is not loaded.");
|
||||
return self.currentPage > 1;
|
||||
}
|
||||
|
||||
#pragma mark - RKObjectLoaderDelegate methods
|
||||
|
||||
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
|
||||
@@ -93,6 +133,18 @@ static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
|
||||
loaded = YES;
|
||||
RKLogInfo(@"Loaded objects: %@", objects);
|
||||
[self.delegate paginator:self didLoadObjects:objects forPage:self.currentPage];
|
||||
|
||||
if ([self hasPageCount] && self.currentPage == 1) {
|
||||
if ([self.delegate respondsToSelector:@selector(paginatorDidLoadFirstPage:)]) {
|
||||
[self.delegate paginatorDidLoadFirstPage:self];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self hasPageCount] && self.currentPage == self.pageCount) {
|
||||
if ([self.delegate respondsToSelector:@selector(paginatorDidLoadLastPage:)]) {
|
||||
[self.delegate paginatorDidLoadLastPage:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
|
||||
@@ -107,28 +159,17 @@ static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
|
||||
BOOL success = [mappingOperation performMapping:&error];
|
||||
if (!success) {
|
||||
pageCount = currentPage = 0;
|
||||
RKLogError(@"Paginator didn't map info to compute page count. Assuming no pages.");
|
||||
} else if (self.perPage) {
|
||||
RKLogError(@"Paginator didn't map info to compute page count. Assuming no pages.");
|
||||
} else if (self.perPage && [self hasObjectCount]) {
|
||||
float objectCountFloat = self.objectCount;
|
||||
pageCount = ceilf( objectCountFloat / self.perPage);
|
||||
pageCount = ceilf(objectCountFloat / self.perPage);
|
||||
RKLogInfo(@"Paginator objectCount: %d pageCount: %d", self.objectCount, self.pageCount);
|
||||
} else {
|
||||
} else {
|
||||
NSAssert(NO, @"Paginator perPage set is 0.");
|
||||
RKLogError(@"Paginator perPage set is 0.");
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasNextPage {
|
||||
NSAssert(self.isLoaded, @"Cannot determine hasNextPage: paginator is not loaded.");
|
||||
|
||||
return self.currentPage < self.pageCount;
|
||||
}
|
||||
|
||||
- (BOOL)hasPreviousPage {
|
||||
NSAssert(self.isLoaded, @"Cannot determine hasPreviousPage: paginator is not loaded.");
|
||||
return self.currentPage > 0;
|
||||
}
|
||||
|
||||
#pragma mark - Action methods
|
||||
|
||||
- (void)loadNextPage {
|
||||
@@ -151,6 +192,11 @@ static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
|
||||
}
|
||||
self.objectLoader.method = RKRequestMethodGET;
|
||||
self.objectLoader.delegate = self;
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(paginator:willLoadPage:objectLoader:)]) {
|
||||
[self.delegate paginator:self willLoadPage:pageNumber objectLoader:self.objectLoader];
|
||||
}
|
||||
|
||||
[self.objectLoader send];
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
250CA67D147D8E8B0047D347 /* OCHamcrest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 250CA67B147D8E800047D347 /* OCHamcrest.framework */; };
|
||||
250CA67E147D8E8F0047D347 /* OCHamcrestIOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 250CA67C147D8E800047D347 /* OCHamcrestIOS.framework */; };
|
||||
250CA680147D8F050047D347 /* OCHamcrest.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 250CA67B147D8E800047D347 /* OCHamcrest.framework */; };
|
||||
2513504E14B8FE6B00A7E893 /* RKConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */; };
|
||||
2513504F14B8FE6B00A7E893 /* RKConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */; };
|
||||
2513504E14B8FE6B00A7E893 /* RKConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2513504F14B8FE6B00A7E893 /* RKConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
25160D1A14564E810060A5C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25160D1914564E810060A5C5 /* Foundation.framework */; };
|
||||
25160D2814564E820060A5C5 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25160D2714564E820060A5C5 /* SenTestingKit.framework */; };
|
||||
25160D2A14564E820060A5C5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25160D2914564E820060A5C5 /* UIKit.framework */; };
|
||||
|
||||
@@ -113,8 +113,8 @@
|
||||
RKObjectMappingProvider* provider = [[RKObjectMappingProvider new] autorelease];
|
||||
RKObjectMapping* errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
|
||||
[errorMapping addAttributeMapping:[RKObjectAttributeMapping mappingFromKeyPath:@"" toKeyPath:@"errorMessage"]];
|
||||
[provider setMapping:errorMapping forKeyPath:@"error"];
|
||||
[provider setMapping:errorMapping forKeyPath:@"errors"];
|
||||
errorMapping.rootKeyPath = @"errors";
|
||||
provider.errorMapping = errorMapping;
|
||||
return provider;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
- (void)testShouldAllowYouToRemoveAMappingByKeyPath {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider objectMappingProvider];
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKManagedObjectMapping* catMapping = [RKManagedObjectMapping mappingForClass:[RKCat class]];
|
||||
assertThat(catMapping, isNot(equalTo(nil)));
|
||||
[catMapping mapAttributes:@"name", nil];
|
||||
@@ -76,4 +76,94 @@
|
||||
assertThat(returnedMapping, is(nilValue()));
|
||||
}
|
||||
|
||||
- (void)testSettingMappingInAContext {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
STAssertNoThrow([mappingProvider setMapping:mapping context:1000], nil);
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfMapping {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider setMapping:mapping context:1000];
|
||||
assertThat([mappingProvider mappingForContext:1000], is(equalTo(mapping)));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfMappingsCollectionForUndefinedContextReturnsEmptyArray {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
NSArray *collection = [mappingProvider mappingsForContext:1000];
|
||||
assertThat(collection, is(empty()));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfMappingsCollectionWhenSingleMappingIsStoredRaisesError {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider setMapping:mapping context:1000];
|
||||
STAssertThrows([mappingProvider mappingsForContext:1000], @"Expected collection mapping retrieval to throw due to storage of single mapping");
|
||||
}
|
||||
|
||||
- (void)testAddingMappingToCollectionContext {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
STAssertNoThrow([mappingProvider addMapping:mapping context:1000], nil);
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfMappingCollection {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping_1 = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider addMapping:mapping_1 context:1000];
|
||||
RKObjectMapping *mapping_2 = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider addMapping:mapping_2 context:1000];
|
||||
NSArray *collection = [mappingProvider mappingsForContext:1000];
|
||||
assertThat(collection, hasItems(mapping_1, mapping_2, nil));
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfMappingCollectionReturnsImmutableArray {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping_1 = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider addMapping:mapping_1 context:1000];
|
||||
RKObjectMapping *mapping_2 = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider addMapping:mapping_2 context:1000];
|
||||
NSArray *collection = [mappingProvider mappingsForContext:1000];
|
||||
assertThat(collection, isNot(instanceOf([NSMutableArray class])));
|
||||
}
|
||||
|
||||
- (void)testRemovalOfMappingFromCollection {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping_1 = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider addMapping:mapping_1 context:1000];
|
||||
RKObjectMapping *mapping_2 = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider addMapping:mapping_2 context:1000];
|
||||
[mappingProvider removeMapping:mapping_1 context:1000];
|
||||
NSArray *collection = [mappingProvider mappingsForContext:1000];
|
||||
assertThat(collection, onlyContains(mapping_2, nil));
|
||||
}
|
||||
|
||||
- (void)testAttemptToRemoveMappingFromContextThatDoesNotIncludeItRaisesError {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
STAssertThrows([mappingProvider removeMapping:mapping context:1000], @"Removal of mapping not included in context should raise an error.");
|
||||
}
|
||||
|
||||
- (void)testSettingMappingForKeyPathInContext {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
STAssertNoThrow([mappingProvider setMapping:mapping forKeyPath:@"testing" context:1000], nil);
|
||||
}
|
||||
|
||||
- (void)testRetrievalOfMappingForKeyPathInContext {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider setMapping:mapping forKeyPath:@"testing" context:1000];
|
||||
assertThat([mappingProvider mappingForKeyPath:@"testing" context:1000], is(equalTo(mapping)));
|
||||
}
|
||||
|
||||
- (void)testRemovalOfMappingByKeyPathInContext {
|
||||
RKObjectMappingProvider *mappingProvider = [RKObjectMappingProvider mappingProvider];
|
||||
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableArray class]];
|
||||
[mappingProvider setMapping:mapping forKeyPath:@"testing" context:1000];
|
||||
[mappingProvider removeMappingForKeyPath:@"testing" context:1000];
|
||||
assertThat([mappingProvider mappingForKeyPath:@"testing" context:1000], is(nilValue()));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -111,6 +111,18 @@ NSString * const RKSpecPaginatorDelegateTimeoutException = @"RKSpecPaginatorDele
|
||||
self.paginationError = error;
|
||||
}
|
||||
|
||||
- (void)paginator:(RKObjectPaginator *)paginator willLoadPage:(NSUInteger)page objectLoader:(RKObjectLoader *)loader {
|
||||
// Necessary for OCMock expectations
|
||||
}
|
||||
|
||||
- (void)paginatorDidLoadFirstPage:(RKObjectPaginator *)paginator {
|
||||
// Necessary for OCMock expectations
|
||||
}
|
||||
|
||||
- (void)paginatorDidLoadLastPage:(RKObjectPaginator *)paginator {
|
||||
// Necessary for OCMock expectations
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RKObjectPaginatorSpec : RKSpec {
|
||||
@@ -150,6 +162,18 @@ static NSString * const RKObjectPaginatorSpecResourcePathPattern = @"/paginate?p
|
||||
assertThat(paginator.mappingProvider, is(equalTo(mappingProvider)));
|
||||
}
|
||||
|
||||
- (void)testInitDoesNotHavePageCount {
|
||||
RKURL *patternURL = [RKURL URLWithBaseURLString:@"http://restkit.org" resourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:nil];
|
||||
assertThatBool([paginator hasPageCount], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testInitDoesNotHaveObjectCount {
|
||||
RKURL *patternURL = [RKURL URLWithBaseURLString:@"http://restkit.org" resourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:nil];
|
||||
assertThatBool([paginator hasObjectCount], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)testThatLoadWithNilMappingProviderRaisesException {
|
||||
RKURL *patternURL = [RKURL URLWithBaseURLString:@"http://restkit.org" resourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:nil];
|
||||
@@ -168,13 +192,19 @@ static NSString * const RKObjectPaginatorSpecResourcePathPattern = @"/paginate?p
|
||||
- (void)testThatResourcePathPatternEvaluatesAgainstPaginator {
|
||||
RKURL *patternURL = [RKURL URLWithBaseURLString:@"http://restkit.org" resourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:nil];
|
||||
assertThat([[paginator URL] resourcePath], is(equalTo(@"/paginate?per_page=25&page=0")));
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
NSUInteger currentPage = 1;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThat([[paginator URL] resourcePath], is(equalTo(@"/paginate?per_page=25&page=1")));
|
||||
}
|
||||
|
||||
- (void)testThatURLReturnsReflectsStateOfPaginator {
|
||||
RKURL *patternURL = [RKURL URLWithBaseURLString:@"http://restkit.org" resourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:nil];
|
||||
assertThat([[paginator URL] absoluteString], is(equalTo(@"http://restkit.org/paginate?page=0&per_page=25")));
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
NSUInteger currentPage = 1;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThat([[mockPaginator URL] absoluteString], is(equalTo(@"http://restkit.org/paginate?page=1&per_page=25")));
|
||||
}
|
||||
|
||||
- (void)testLoadingAPageOfObjects {
|
||||
@@ -196,7 +226,7 @@ static NSString * const RKObjectPaginatorSpecResourcePathPattern = @"/paginate?p
|
||||
paginator.delegate = testDelegate;
|
||||
[paginator loadPage:1];
|
||||
[testDelegate waitForLoad];
|
||||
assertThatInteger(paginator.perPage, is(equalToInteger(2)));
|
||||
assertThatInteger(paginator.perPage, is(equalToInteger(3)));
|
||||
}
|
||||
|
||||
- (void)testLoadingPageOfObjectMapsTotalEntries {
|
||||
@@ -233,6 +263,41 @@ static NSString * const RKObjectPaginatorSpecResourcePathPattern = @"/paginate?p
|
||||
assertThat([[testDelegate paginatedObjects] valueForKey:@"name"], is(equalTo([NSArray arrayWithObjects:@"Blake", @"Sarah", @"Colin", nil])));
|
||||
}
|
||||
|
||||
- (void)testLoadingPageOfObjectHasPageCount {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:mappingProvider];
|
||||
RKSpecPaginatorDelegate *testDelegate = [RKSpecPaginatorDelegate paginatorDelegate];
|
||||
paginator.delegate = testDelegate;
|
||||
[paginator loadPage:1];
|
||||
[testDelegate waitForLoad];
|
||||
assertThatBool([paginator hasPageCount], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testLoadingPageOfObjectHasObjectCount {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:mappingProvider];
|
||||
RKSpecPaginatorDelegate *testDelegate = [RKSpecPaginatorDelegate paginatorDelegate];
|
||||
paginator.delegate = testDelegate;
|
||||
[paginator loadPage:1];
|
||||
[testDelegate waitForLoad];
|
||||
assertThatBool([paginator hasObjectCount], is(equalToBool(YES)));
|
||||
}
|
||||
|
||||
- (void)testDelegateIsInformedOfWillLoadPage {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:mappingProvider];
|
||||
RKSpecPaginatorDelegate *testDelegate = [RKSpecPaginatorDelegate paginatorDelegate];
|
||||
id mockDelegate = [OCMockObject partialMockForObject:testDelegate];
|
||||
[[mockDelegate expect] paginator:paginator willLoadPage:1 objectLoader:OCMOCK_ANY];
|
||||
paginator.delegate = mockDelegate;
|
||||
[paginator loadPage:1];
|
||||
[mockDelegate waitForLoad];
|
||||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)testDelegateIsInformedOnError {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
@@ -246,6 +311,32 @@ static NSString * const RKObjectPaginatorSpecResourcePathPattern = @"/paginate?p
|
||||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)testDelegateIsInformedOnLoadOfFirstPage {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:mappingProvider];
|
||||
RKSpecPaginatorDelegate *testDelegate = [RKSpecPaginatorDelegate paginatorDelegate];
|
||||
id mockDelegate = [OCMockObject partialMockForObject:testDelegate];
|
||||
[[mockDelegate expect] paginatorDidLoadFirstPage:paginator];
|
||||
paginator.delegate = mockDelegate;
|
||||
[paginator loadPage:1];
|
||||
[mockDelegate waitForLoad];
|
||||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)testDelegateIsInformedOnLoadOfLastPage {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
RKObjectPaginator *paginator = [RKObjectPaginator paginatorWithPatternURL:patternURL mappingProvider:mappingProvider];
|
||||
RKSpecPaginatorDelegate *testDelegate = [RKSpecPaginatorDelegate paginatorDelegate];
|
||||
id mockDelegate = [OCMockObject partialMockForObject:testDelegate];
|
||||
[[mockDelegate expect] paginatorDidLoadLastPage:paginator];
|
||||
paginator.delegate = mockDelegate;
|
||||
[paginator loadPage:2];
|
||||
[mockDelegate waitForLoad];
|
||||
[mockDelegate verify];
|
||||
}
|
||||
|
||||
- (void)testLoadingNextPageOfObjects {
|
||||
RKURL *patternURL = [RKSpecGetBaseURL() URLByAppendingResourcePath:RKObjectPaginatorSpecResourcePathPattern];
|
||||
RKObjectMappingProvider *mappingProvider = [self paginationMappingProvider];
|
||||
@@ -287,37 +378,71 @@ static NSString * const RKObjectPaginatorSpecResourcePathPattern = @"/paginate?p
|
||||
assertThat(testDelegate.paginationError, is(notNilValue()));
|
||||
}
|
||||
|
||||
- (void)itShouldKnowIfItHasANextPage {
|
||||
- (void)testKnowledgeOfHasANextPage {
|
||||
RKObjectPaginator *paginator = [[RKObjectPaginator new] autorelease];
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
NSUInteger perPage = 5;
|
||||
NSUInteger currentPage = 1;
|
||||
NSUInteger objectCount = 10;
|
||||
BOOL isLoaded = YES;
|
||||
NSUInteger perPage = 5;
|
||||
NSUInteger pageCount = 3;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(isLoaded)] isLoaded];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(perPage)] perPage];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(objectCount)] objectCount];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(pageCount)] pageCount];
|
||||
|
||||
NSUInteger currentPage = 1;
|
||||
[[[mockPaginator expect] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThatBool([mockPaginator hasNextPage], is(equalToBool(YES)));
|
||||
currentPage = 2;
|
||||
[[[mockPaginator expect] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThatBool([mockPaginator hasNextPage], is(equalToBool(YES)));
|
||||
currentPage = 3;
|
||||
[[[mockPaginator expect] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThatBool([mockPaginator hasNextPage], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
- (void)itShouldKnowIfItHasAPreviousPage {
|
||||
- (void)testHasNextPageRaisesExpectionWhenNotLoaded {
|
||||
RKObjectPaginator *paginator = [[RKObjectPaginator new] autorelease];
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
NSUInteger perPage = 5;
|
||||
NSUInteger currentPage = 3;
|
||||
NSUInteger objectCount = 10;
|
||||
BOOL loaded = NO;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(loaded)] isLoaded];
|
||||
STAssertThrows([mockPaginator hasNextPage], @"Expected exception due to isLoaded == NO");
|
||||
}
|
||||
|
||||
- (void)testHasNextPageRaisesExpectionWhenPageCountIsUnknown {
|
||||
RKObjectPaginator *paginator = [[RKObjectPaginator new] autorelease];
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
BOOL loaded = YES;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(loaded)] isLoaded];
|
||||
BOOL hasPageCount = NO;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(hasPageCount)] hasPageCount];
|
||||
STAssertThrows([mockPaginator hasNextPage], @"Expected exception due to pageCount == NSUIntegerMax");
|
||||
}
|
||||
|
||||
- (void)testHasPreviousPageRaisesExpectionWhenNotLoaded {
|
||||
RKObjectPaginator *paginator = [[RKObjectPaginator new] autorelease];
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
BOOL loaded = NO;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(loaded)] isLoaded];
|
||||
STAssertThrows([mockPaginator hasPreviousPage], @"Expected exception due to isLoaded == NO");
|
||||
}
|
||||
|
||||
- (void)testKnowledgeOfPreviousPage {
|
||||
RKObjectPaginator *paginator = [[RKObjectPaginator new] autorelease];
|
||||
id mockPaginator = [OCMockObject partialMockForObject:paginator];
|
||||
BOOL isLoaded = YES;
|
||||
NSUInteger perPage = 5;
|
||||
NSUInteger pageCount = 3;
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(isLoaded)] isLoaded];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(perPage)] perPage];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(objectCount)] objectCount];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
[[[mockPaginator stub] andReturnValue:OCMOCK_VALUE(pageCount)] pageCount];
|
||||
|
||||
NSUInteger currentPage = 3;
|
||||
[[[mockPaginator expect] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThatBool([mockPaginator hasPreviousPage], is(equalToBool(YES)));
|
||||
currentPage = 2;
|
||||
[[[mockPaginator expect] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThatBool([mockPaginator hasPreviousPage], is(equalToBool(YES)));
|
||||
currentPage = 1;
|
||||
[[[mockPaginator expect] andReturnValue:OCMOCK_VALUE(currentPage)] currentPage];
|
||||
assertThatBool([mockPaginator hasPreviousPage], is(equalToBool(NO)));
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ class RestKit::SpecServer < Sinatra::Base
|
||||
status 200
|
||||
content_type 'application/json'
|
||||
|
||||
per_page = 2
|
||||
per_page = 3
|
||||
total_entries = 6
|
||||
current_page = params[:page].to_i
|
||||
entries = []
|
||||
|
||||
Reference in New Issue
Block a user