Skip to content

Commit

Permalink
Add support for inferring attributes that match the snake-case _id pa…
Browse files Browse the repository at this point in the history
…ttern. closes RestKit#1047
  • Loading branch information
blakewatters committed Dec 2, 2012
1 parent cbb3f70 commit a5dc037
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Code/CoreData/RKEntityMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
When `RKIdentificationAttributesInferredFromEntity` is invoked, the entity is first checked for a user info key specifying the identifying attributes. If the user info of the given entity contains a value for the key 'RKEntityIdentificationAttributes', then that value is used to construct an array of attributes. The user info key must contain a string or an array of strings specifying the names of attributes that exist in the given entity.
If no attributes are specified in the user info, then the entity is searched for an attribute whose name matches the llama-cased name of the entity. For example, an entity named 'Article' would have an inferred identifier attribute of 'articleID' and an entity named 'ApprovedComment' would be inferred as 'approvedCommentID'. If such an attribute is found within the entity, an array is returned containing the attribute. If none is returned, the the attributes are searched for the following names:
If no attributes are specified in the user info, then the entity is searched for an attribute whose name matches the llama-cased or snake-cased name of the entity. For example, an entity named 'Article' would have an inferred identifying attributes of 'articleID' and 'article_id', and an entity named 'ApprovedComment' would be inferred as 'approvedCommentID' and 'approved_comment_id'. If such an attribute is found within the entity, an array is returned containing the attribute. If none is returned, the the attributes are searched for the following names:
1. 'identifier'
1. 'id'
Expand Down
22 changes: 18 additions & 4 deletions Code/CoreData/RKEntityMapping.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,26 @@
return nil;
}

// Given 'Human', returns 'humanID'; Given 'AmenityReview' returns 'amenityReviewID'
static NSString *RKEntityIdentificationAttributeNameForEntity(NSEntityDescription *entity)
static NSString *RKUnderscoredStringFromCamelCasedString(NSString *camelCasedString)
{
NSError *error = nil;
NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))" options:0 error:&error];
if (! regularExpression) return nil;
NSMutableArray *lowercasedComponents = [NSMutableArray array];
[regularExpression enumerateMatchesInString:camelCasedString options:0 range:NSMakeRange(0, [camelCasedString length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[lowercasedComponents addObject:[[camelCasedString substringWithRange:[result range]] lowercaseString]];
}];
return [lowercasedComponents componentsJoinedByString:@"_"];
}

// Given 'Human', returns 'humanID' and 'human_id'; Given 'AmenityReview' returns 'amenityReviewID' and 'amenity_review_id'
static NSArray *RKEntityIdentificationAttributeNamesForEntity(NSEntityDescription *entity)
{
NSString *entityName = [entity name];
NSString *lowerCasedFirstCharacter = [[entityName substringToIndex:1] lowercaseString];
return [NSString stringWithFormat:@"%@%@ID", lowerCasedFirstCharacter, [entityName substringFromIndex:1]];
NSString *camelizedIDAttributeName = [NSString stringWithFormat:@"%@%@ID", lowerCasedFirstCharacter, [entityName substringFromIndex:1]];
NSString *underscoredIDAttributeName = [NSString stringWithFormat:@"%@_id", RKUnderscoredStringFromCamelCasedString([entity name])];
return @[ camelizedIDAttributeName, underscoredIDAttributeName ];
}

static NSArray *RKEntityIdentificationAttributeNames()
Expand Down Expand Up @@ -97,7 +111,7 @@
return RKArrayOfAttributesForEntityFromAttributesOrNames(entity, attributes);
}

NSMutableArray *identifyingAttributes = [NSMutableArray arrayWithObject:RKEntityIdentificationAttributeNameForEntity(entity)];
NSMutableArray *identifyingAttributes = [RKEntityIdentificationAttributeNamesForEntity(entity) mutableCopy];
[identifyingAttributes addObjectsFromArray:RKEntityIdentificationAttributeNames()];
for (NSString *attributeName in identifyingAttributes) {
NSAttributeDescription *attribute = [entity attributesByName][attributeName];
Expand Down
41 changes: 39 additions & 2 deletions Tests/Logic/CoreData/RKEntityMappingTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,6 @@ - (void)testThatInitEntityIdentifierWithEmptyAttributesRaisesException

#pragma mark - Entity Identifier Inference

// TODO: The attributes to auto-detect: entityNameID, ID, identififer, url, URL

- (void)testEntityIdentifierInferenceForEntityWithLlamaCasedIDAttribute
{
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
Expand Down Expand Up @@ -467,4 +465,43 @@ - (void)testEntityIdentifierInferenceFromUserInfoKeyRaisesErrorForNonexistantAtt
}
}

- (void)testInferenceOfSnakeCasedEntityName
{
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
[entity setName:@"Monkey"];
NSAttributeDescription *identifierAttribute = [NSAttributeDescription new];
[identifierAttribute setName:@"monkey_id"];
[entity setProperties:@[ identifierAttribute ]];
NSArray *identificationAttributes = RKIdentificationAttributesInferredFromEntity(entity);
expect(identificationAttributes).notTo.beNil();
NSArray *attributeNames = @[ @"monkey_id" ];
expect([identificationAttributes valueForKey:@"name"]).to.equal(attributeNames);
}

- (void)testInferenceOfCompoundSnakeCasedEntityName
{
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
[entity setName:@"ArcticMonkey"];
NSAttributeDescription *identifierAttribute = [NSAttributeDescription new];
[identifierAttribute setName:@"arctic_monkey_id"];
[entity setProperties:@[ identifierAttribute ]];
NSArray *identificationAttributes = RKIdentificationAttributesInferredFromEntity(entity);
expect(identificationAttributes).notTo.beNil();
NSArray *attributeNames = @[ @"arctic_monkey_id" ];
expect([identificationAttributes valueForKey:@"name"]).to.equal(attributeNames);
}

- (void)testInferenceOfSnakeCasedEntityNameWithAbbreviation
{
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
[entity setName:@"ArcticMonkeyURL"];
NSAttributeDescription *identifierAttribute = [NSAttributeDescription new];
[identifierAttribute setName:@"arctic_monkey_url_id"];
[entity setProperties:@[ identifierAttribute ]];
NSArray *identificationAttributes = RKIdentificationAttributesInferredFromEntity(entity);
expect(identificationAttributes).notTo.beNil();
NSArray *attributeNames = @[ @"arctic_monkey_url_id" ];
expect([identificationAttributes valueForKey:@"name"]).to.equal(attributeNames);
}

@end

0 comments on commit a5dc037

Please sign in to comment.