Ember Data Merge Handling
Internally, Ember Data models use a state machine to represent their lifecycle. One of the limitations of the current implementation is that records cannot be modified:
- By the client application if the record is in-flight, or
- By the server (via an out-of-band
load()
) if the record has pending changes on the client.
One solution to both of these limitations is to implement merging logic if there a conflict between client and server truth is detected. The default could be as simple as last-write-wins or as sophisticated as a three-way merge (à la Git’s default), with the application developer given the option of overriding the default merge strategy.
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
addresses: DS.hasMany('App.Address'),
merge: function(original, local, remote) {
//... resolve conflict here
return resolved;
}
});
Outstanding Issues
Dirtiness
After a merge is resolved, how do you indicate whether the record is still dirty? For example, imagine that after a merge all of the changes made locally have been subsumed by the new truth that arrived from the adapter. At this point, the record should be considered clean.
Perhaps the best thing to do here is to simply diff the returned hash against the new remote hash, and mark the record as clean if they are the same.
Merge Hook Location
The most natural place for the merge hook to be implemented seems to be on the DS.Model class, but:
- Is this actually a model concern? May people want adapter-specific merge semantics?
- The above proposed API makes
merge
a reserved attribute and relationship name. Are we okay with this?
Arguments to Merge Hook
We obviously need to provide original, local and remote copies of the
data. But should those arguments be raw data hashes, or DS.Model
instances? I think it would break people’s mental model if they
weren’t models. For example, imagine you have a computed property on
your DS.Model
and you want to use that to compare the two. The fact
that this wouldn’t work if we passed raw hashes would be confusing.
If we do pass DS.Model instances, we may need to do some work to make sure that people don’t somehow hang on to references, because they will essentially be inert. This may take some infrastructure work.