Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to merge policy #80

Merged
merged 5 commits into from
Jun 26, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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