Permalink
Browse files

Merge pull request #294 from Mantle/mismatched-keypaths

Throw an error if mappings don't match property keys
  • Loading branch information...
2 parents 175ec19 + 6400511 commit 9f5f0aad4d46f16d3c99791858afd4d9d7d9282d @jspahrsummers jspahrsummers committed Apr 24, 2014
View
4 Mantle/MTLJSONAdapter.h
@@ -63,6 +63,10 @@ extern const NSInteger MTLJSONAdapterErrorNoClassFound;
// The provided JSONDictionary is not valid.
extern const NSInteger MTLJSONAdapterErrorInvalidJSONDictionary;
+// The model's implementation of +JSONKeyPathsByPropertyKey included a key which
+// does not actually exist in +propertyKeys.
+extern const NSInteger MTLJSONAdapterErrorInvalidJSONMapping;
+
// Converts a MTLModel object to and from a JSON dictionary.
@interface MTLJSONAdapter : NSObject
View
20 Mantle/MTLJSONAdapter.m
@@ -13,6 +13,7 @@
NSString * const MTLJSONAdapterErrorDomain = @"MTLJSONAdapterErrorDomain";
const NSInteger MTLJSONAdapterErrorNoClassFound = 2;
const NSInteger MTLJSONAdapterErrorInvalidJSONDictionary = 3;
+const NSInteger MTLJSONAdapterErrorInvalidJSONMapping = 4;
// An exception was thrown and caught.
const NSInteger MTLJSONAdapterErrorExceptionThrown = 1;
@@ -141,7 +142,24 @@ - (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)mo
NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:JSONDictionary.count];
- for (NSString *propertyKey in [self.modelClass propertyKeys]) {
+ NSSet *propertyKeys = [self.modelClass propertyKeys];
+
+ for (NSString *JSONKeyPath in self.JSONKeyPathsByPropertyKey) {
+ if ([propertyKeys containsObject:JSONKeyPath]) continue;
+
+ if (error != NULL) {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid JSON mapping", nil),
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%1$@ could not be parsed because its JSON mapping contains illegal property keys.", nil), modelClass]
+ };
+
+ *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorInvalidJSONMapping userInfo:userInfo];
+ }
+
+ return nil;
+ }
+
+ for (NSString *propertyKey in propertyKeys) {
NSString *JSONKeyPath = [self JSONKeyPathForPropertyKey:propertyKey];
if (JSONKeyPath == nil) continue;
View
4 Mantle/MTLManagedObjectAdapter.h
@@ -183,6 +183,10 @@ extern const NSInteger MTLManagedObjectAdapterErrorUniqueFetchRequestFailed;
// NSArray or NSSet of MTLModel<MTLManagedObjectSerializing> instances.
extern const NSInteger MTLManagedObjectAdapterErrorUnsupportedRelationshipClass;
+// The model's implementation of +managedObjectKeysByPropertyKey included a key
+// which does not actually exist in +propertyKeys.
+extern const NSInteger MTLManagedObjectAdapterErrorInvalidManagedObjectMapping;
+
// Converts a MTLModel object to and from an NSManagedObject.
@interface MTLManagedObjectAdapter : NSObject
View
18 Mantle/MTLManagedObjectAdapter.m
@@ -19,6 +19,7 @@
const NSInteger MTLManagedObjectAdapterErrorUnsupportedManagedObjectPropertyType = 5;
const NSInteger MTLManagedObjectAdapterErrorUnsupportedRelationshipClass = 6;
const NSInteger MTLManagedObjectAdapterErrorUniqueFetchRequestFailed = 7;
+const NSInteger MTLManagedObjectAdapterErrorInvalidManagedObjectMapping = 8;
// Performs the given block in the context's queue, if it has one.
static id performInContext(NSManagedObjectContext *context, id (^block)(void)) {
@@ -250,6 +251,23 @@ - (id)modelFromManagedObject:(NSManagedObject *)managedObject processedObjects:(
}
+ (id)modelOfClass:(Class)modelClass fromManagedObject:(NSManagedObject *)managedObject error:(NSError **)error {
+ NSSet *propertyKeys = [modelClass propertyKeys];
+
+ for (NSString *mappedPropertyKey in [modelClass managedObjectKeysByPropertyKey]) {
+ if ([propertyKeys containsObject:mappedPropertyKey]) continue;
+
+ if (error != NULL) {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Invalid entity attribute mapping", nil),
+ NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedString(@"%1$@ could not be parsed because its entity attribute mapping contains illegal property keys.", nil), modelClass]
+ };
+
+ *error = [NSError errorWithDomain:MTLManagedObjectAdapterErrorDomain code:MTLManagedObjectAdapterErrorInvalidManagedObjectMapping userInfo:userInfo];
+ }
+
+ return nil;
+ }
+
CFMutableDictionaryRef processedObjects = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (processedObjects == NULL) return nil;
View
3 MantleTests/MTLCoreDataTestModels.h
@@ -59,3 +59,6 @@
@end
+// Maps a non-existant property "name" to the "string" attribute.
+@interface MTLIllegalManagedObjectMappingModel : MTLModel <MTLManagedObjectSerializing>
+@end
View
14 MantleTests/MTLCoreDataTestModels.m
@@ -115,3 +115,17 @@ + (NSString *)managedObjectEntityName {
}
@end
+
+@implementation MTLIllegalManagedObjectMappingModel
+
++ (NSString *)managedObjectEntityName {
+ return @"Parent";
+}
+
++ (NSDictionary *)managedObjectKeysByPropertyKey {
+ return @{
+ @"name": @"username"
+ };
+}
+
+@end
View
13 MantleTests/MTLJSONAdapterSpec.m
@@ -88,6 +88,19 @@
expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONDictionary);
});
+it(@"should return nil and error with an illegal JSON mapping", ^{
+ NSDictionary *values = @{
+ @"username": @"foo"
+ };
+
+ NSError *error = nil;
+ MTLIllegalJSONMappingModel *model = [MTLJSONAdapter modelOfClass:MTLIllegalJSONMappingModel.class fromJSONDictionary:values error:&error];
+ expect(model).beNil();
+ expect(error).notTo.beNil();
+ expect(error.domain).to.equal(MTLJSONAdapterErrorDomain);
+ expect(error.code).to.equal(MTLJSONAdapterErrorInvalidJSONMapping);
+});
+
it(@"should support key paths across arrays", ^{
NSDictionary *values = @{
@"users": @[
View
14 MantleTests/MTLManagedObjectAdapterSpec.m
@@ -226,6 +226,20 @@
expect(error).notTo.beNil();
});
+ it(@"should return nil and error with an illegal JSON mapping", ^{
+ MTLParent *parent = [MTLParent insertInManagedObjectContext:context];
+ expect(parent).notTo.beNil();
+
+ parent.string = @"foobar";
+
+ NSError *error = nil;
+ MTLIllegalManagedObjectMappingModel *model = [MTLManagedObjectAdapter modelOfClass:MTLIllegalManagedObjectMappingModel.class fromManagedObject:parent error:&error];
+ expect(model).beNil();
+ expect(error).notTo.beNil();
+ expect(error.domain).to.equal(MTLManagedObjectAdapterErrorDomain);
+ expect(error.code).to.equal(MTLManagedObjectAdapterErrorInvalidManagedObjectMapping);
+ });
+
it(@"should return an error if model doesn't validate for insert", ^{
MTLParentIncorrectTestModel *parentModel = [MTLParentIncorrectTestModel modelWithDictionary:@{} error:NULL];
View
4 MantleTests/MTLTestModel.h
@@ -70,3 +70,7 @@ extern const NSInteger MTLTestModelNameMissing;
// Returns a default name of 'foobar' when validateName:error: is invoked
@interface MTLSelfValidatingModel : MTLValidationModel
@end
+
+// Maps a non-existant property "name" to the "username" key in JSON.
+@interface MTLIllegalJSONMappingModel : MTLModel <MTLJSONSerializing>
+@end
View
10 MantleTests/MTLTestModel.m
@@ -175,3 +175,13 @@ - (BOOL)validateName:(NSString **)name error:(NSError **)error {
}
@end
+
+@implementation MTLIllegalJSONMappingModel
+
++ (NSDictionary *)JSONKeyPathsByPropertyKey {
+ return @{
+ @"name": @"username"
+ };
+}
+
+@end

0 comments on commit 9f5f0aa

Please sign in to comment.