Skip to content

Commit

Permalink
Merge pull request #80 from mutualmobile/cnstoll_mergePolicy
Browse files Browse the repository at this point in the history
Updates to merge policy
  • Loading branch information
cnstoll committed Jun 26, 2014
2 parents 3c36cdf + 769e34b commit 9b029f0
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 14 deletions.
12 changes: 8 additions & 4 deletions Source/MMRecord/MMRecordMarshaler.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@
relationships on various other records. Generally speaking, its expected that a response will
contain either duplicate references to record primary keys, which allow MMRecord to fetch the
appropriate record to associate as a relationship. Or, the response may contain duplicate fully
saturated objects. In this case, the first object will "win", and all other references to that
object will be populated using the first one that is found.
saturated objects. In this case, the first fully saturated object will "win", and all other
references to that object will be populated using the first one that is found.
This method is intended as a means to merge those various different response objects together to
create a master instance of a particular record. In some responses an object may contain a subset
Expand All @@ -128,8 +128,12 @@
@param dictionary The dictionary for the n+1th record response object of a given type and
primary key.
@param protoRecord The proto record created to represent this specific object by MMRecord.
@discussion This method has no default implementation. You must subclass MMRecordMarshaler to
provide your own implementation.
@discussion The default implementation of this method will look for cases where a given record is
only identified by a primary key and includes no additional data to populate it with. In this case
the primary key of that original proto record will be compared with the incoming object dictionary
and if they match then the new dictionary will be associated with the proto record.
@warning If you decide to subclass this method you may want to use the super implementation
as a starting point for your own implementation. Calling super is not required, but is recommended.
*/
+ (void)mergeDuplicateRecordResponseObjectDictionary:(NSDictionary *)dictionary
withExistingProtoRecord:(MMRecordProtoRecord *)protoRecord;
Expand Down
30 changes: 29 additions & 1 deletion Source/MMRecord/MMRecordMarshaler.m
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,35 @@ + (void)establishPrimaryKeyRelationshipFromProtoRecord:(MMRecordProtoRecord *)pr

+ (void)mergeDuplicateRecordResponseObjectDictionary:(NSDictionary *)dictionary
withExistingProtoRecord:(MMRecordProtoRecord *)protoRecord {
// There is no default implementation for this method. Feel free to provide your own :)
if ([protoRecord.dictionary.allKeys count] == 1) {
NSAttributeDescription *primaryAttributeDescription = protoRecord.representation.primaryAttributeDescription;
NSArray *primaryKeyPaths = [protoRecord.representation keyPathsForMappingAttributeDescription:primaryAttributeDescription];

BOOL dictionariesContainIdenticalPrimaryKeys = NO;

for (NSString *keyPath in primaryKeyPaths) {
id dictionaryValue = [dictionary valueForKeyPath:keyPath];
id protoRecordValue = [dictionary valueForKeyPath:keyPath];

if ([dictionaryValue isKindOfClass:[NSNumber class]] && [protoRecordValue isKindOfClass:[NSNumber class]]) {
dictionariesContainIdenticalPrimaryKeys = [dictionaryValue isEqualToNumber:protoRecordValue];
} else if ([dictionaryValue isKindOfClass:[NSString class]] && [protoRecordValue isKindOfClass:[NSString class]]) {
dictionariesContainIdenticalPrimaryKeys = [dictionaryValue isEqualToString:protoRecordValue];
}

if (dictionariesContainIdenticalPrimaryKeys) {
break;
}
}

if (dictionariesContainIdenticalPrimaryKeys) {
protoRecord.dictionary = dictionary;
}
}

if ([dictionary.allKeys count] != [protoRecord.dictionary.allKeys count]) {
[MMRecordDebugger logMessageWithDescription:@"Possible inconsistent duplicate records detected. MMRecord provided the opportunity to merge two dictionaries representing the same record, where those two dictionaries were not equal. You may override the MMRecordMarshaler mergeDuplicateRecordResponseObjectDictionary:withExistingProtoRecord: method to deal with this issue if it becomes a problem. This is not expected behavior and may be due to an response issue."];
}
}


Expand Down
6 changes: 6 additions & 0 deletions Source/MMRecord/MMRecordProtoRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
each unique record in the response object. The proto record is not responsible for that, but it is
an important consideration for a user of this class to consider. This class is responsible for
populating records with their given dictionary, as well as establishing relationships to it's record.
Note: when the value representing a relationship is a single string or number that MMRecord will
convert that string or number into a dictionary with that value, and they primary key identified
for that targetted relationship's entity. That means that the representation of a proto record
will always be in the form of a dictionary, even if the response object form is a string or a
number.
*/

@interface MMRecordProtoRecord : NSObject
Expand Down
22 changes: 13 additions & 9 deletions Source/MMRecord/MMRecordResponse.m
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ - (MMRecordProtoRecord *)protoRecordWithRecordResponseObject:(id)recordResponseO
entity:entity
representation:representation];


if (proto.hasRelationshipPrimarykey == NO) {
if (proto.primaryKeyValue == nil) {
if (self.options.entityPrimaryKeyInjectionBlock != nil) {
Expand Down Expand Up @@ -303,16 +304,19 @@ - (void)addRelationshipProtoRecordsToProtoRecord:(MMRecordProtoRecord *)protoRec
}

for (id object in relationshipObject) {
NSEntityDescription *recordSubEntity = [self subEntityForRecordResponseObject:object
withInitialEntity:entity];

MMRecordProtoRecord *relationshipProto = [self protoRecordWithRecordResponseObject:object
entity:recordSubEntity
existingResponseGroups:responseGroups
parentProtoRecord:protoRecord];

// By keeping the above section of code outside the conditional we can gaurantee that
// the protoRecord generation/parsing method above gets called for every relationship
// proto, which also causes the optional merge code to be run for all possible updated
// relationship protos.
if ([protoRecord canAccomodateAdditionalProtoRecordForRelationshipDescription:relationshipDescription]) {

NSEntityDescription *recordSubEntity = [self subEntityForRecordResponseObject:object
withInitialEntity:entity];

MMRecordProtoRecord *relationshipProto = [self protoRecordWithRecordResponseObject:object
entity:recordSubEntity
existingResponseGroups:responseGroups
parentProtoRecord:protoRecord];

[protoRecord addRelationshipProto:relationshipProto
forRelationshipDescription:relationshipDescription];
}
Expand Down

0 comments on commit 9b029f0

Please sign in to comment.