Permalink
Browse files

Relax the use of use an the informal procotol for the `errorMessage` …

…property in favor of the `description` method. closes #1104, closes #1087, closes #1095

* Change contract to the use the `description` method instead of `errorMessage`. This makes it work with any class out of the box
* Add import for RKErrorMessage to the Support.h header so it is immediately available
* Fix incorrect keyPath in the README.md
* Add additional notes about how the errors are constructed to the README
  • Loading branch information...
1 parent 7984d4f commit 0a033596da6c9446e2bcf4cd67febc83b81b2ca7 @blakewatters blakewatters committed Dec 23, 2012
@@ -197,9 +197,9 @@
/**
Returns a representation of a mapping result as an `NSError` value.
- The returned `NSError` object is in the `RKErrorDomain` domain and has the `RKMappingErrorFromMappingResult` code. The value for the `NSLocalizedDescriptionKey` is computed by retrieving the objects in the mapping result as an array, evaluating `valueForKeyPath:@"errorMessage"` against the array, and joining the returned error messages by comma to form a single string value. The source error objects are returned with the `NSError` in the `userInfo` dictionary under the `RKObjectMapperErrorObjectsKey` key.
+ The returned `NSError` object is in the `RKErrorDomain` domain and has the `RKMappingErrorFromMappingResult` code. The value for the `NSLocalizedDescriptionKey` is computed by retrieving the objects in the mapping result as an array, evaluating `valueForKeyPath:@"description"` against the array, and joining the returned error messages by comma to form a single string value. The source error objects are returned with the `NSError` in the `userInfo` dictionary under the `RKObjectMapperErrorObjectsKey` key.
- The `errorMessage` property is significant as it is an informal protocol that must be adopted by objects wishing to representing response errors.
+ This implementation assumes that the class used to represent the response error will return a string description of the client side error when sent the `description` message.
@return An error object representing the objects contained in the mapping result.
@see `RKErrorMessage`
@@ -37,7 +37,7 @@
NSArray *collection = [mappingResult array];
NSString *description = nil;
if ([collection count] > 0) {
- description = [[collection valueForKeyPath:@"errorMessage"] componentsJoinedByString:@", "];
+ description = [[collection valueForKeyPath:@"description"] componentsJoinedByString:@", "];
} else {
RKLogWarning(@"Expected mapping result to contain at least one object to construct an error");
}
@@ -23,10 +23,6 @@
/**
The `RKErrorMessage` is a simple class used for representing error messages returned by a remote backend system with which the client application is communicating. Error messages are typically returned in a response body in the Client Error class (status code 4xx range).
- ## Error Message Informal Protocol
-
- The `errorMessage` property method is the sole method of an informal protocol that must be adopted by objects wishing to represent error messages within RestKit. This protocol is by the `RKErrorFromMappingResult` function when constructing `NSError` messages from a mapped response body.
-
@see `RKErrorFromMappingResult`
*/
@interface RKErrorMessage : NSObject
@@ -24,8 +24,7 @@ @implementation RKErrorMessage
- (NSString *)description
{
- return [NSString stringWithFormat:@"<%@:%p error message = \"%@\" userInfo = %@>",
- NSStringFromClass([self class]), self, self.errorMessage, self.userInfo];
+ return self.errorMessage;
}
@end
View
@@ -20,6 +20,7 @@
// Load shared support code
#import "RKErrors.h"
+#import "RKErrorMessage.h"
#import "RKMIMETypes.h"
#import "RKLog.h"
#import "RKPathMatcher.h"
View
@@ -230,9 +230,10 @@ operation.managedObjectCache = managedObjectStore.managedObjectCache;
// GET /articles/error.json returns a 422 (Unprocessable Entity)
// JSON looks like {"errors": "Some Error Has Occurred"}
+// You can map errors to any class, but `RKErrorMessage` is included for free
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
// The entire value at the source key path containing the errors maps to the message
-[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:[NSNull null] toKeyPath:@"message"]];
+[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:[NSNull null] toKeyPath:@"errorMessage"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError);
// Any response in the 4xx status code range with an "errors" key path uses this mapping
@@ -241,6 +242,7 @@ RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptor
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://restkit.org/articles/error.json"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[errorDescriptor]];
[operation setCompletionBlockWithSuccess:nil failure:^(RKObjectRequestOperation *operation, NSError *error) {
+ // The `description` method of the class the error is mapped to is used to construct the value of the localizedDescription
NSLog(@"Loaded this error: %@", [error localizedDescription]);
}];
```
@@ -14,6 +14,20 @@
NSString *RKPathAndQueryStringFromURLRelativeToURL(NSURL *URL, NSURL *baseURL);
+@interface RKServerError : NSObject
+@property (nonatomic, copy) NSString *message;
+@property (nonatomic, assign) NSInteger code;
+@end
+
+@implementation RKServerError
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"%@ (%ld)", self.message, (long) self.code];
+}
+
+@end
+
@interface RKObjectResponseMapperOperationTest : RKTestCase
@end
@@ -121,6 +135,21 @@ - (void)testThatMappingEmptyJSONDictionaryClientErrorResponseReturnsErrorNoDescr
expect([mapper.error localizedDescription]).to.equal(@"Loaded an unprocessable client error response (422)");
}
+- (void)testMappingServerErrorToCustomErrorClass
+{
+ RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKServerError class]];
+ [mapping addAttributeMappingsFromArray:@[ @"code", @"message" ]];
+ RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:[NSIndexSet indexSetWithIndex:422]];
+ NSURL *URL = [NSURL URLWithString:@"http://restkit.org"];
+ NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:422 HTTPVersion:@"1.1" headerFields:@{@"Content-Type": @"application/json"}];
+ NSData *data = [@"{\"code\": 12345, \"message\": \"This is the error message\"}" dataUsingEncoding:NSUTF8StringEncoding];
+ RKObjectResponseMapperOperation *mapper = [[RKObjectResponseMapperOperation alloc] initWithResponse:response data:data responseDescriptors:@[responseDescriptor]];
+ [mapper start];
+ expect(mapper.error).notTo.beNil();
+ expect(mapper.error.code).to.equal(RKMappingErrorFromMappingResult);
+ expect([mapper.error localizedDescription]).to.equal(@"This is the error message (12345)");
+}
+
#pragma mark - Response Descriptor Matching
- (void)testThatResponseMapperMatchesBaseURLWithoutPathAppropriately

0 comments on commit 0a03359

Please sign in to comment.