New state-handling approach for item tag changes

If this works out I think we'll want to use this approach for
all data layer changes.

Previously, an unsaved change on an object would update its state
immediately, which was fine for synchronous code but breaks down if a
save involves multiple asynchronous calls, because modifying state after
the relevant data has been saved to the DB but before the `_changed`
object has been cleared would mean that new changes would be lost. Now,
changes are written to _changedData, and a get for the data first checks
_changedData before checking the state property (e.g., _tags) directly.
The changedData property is cleared as it's written, and once the object
is saved, the reload updates the state property with the new data.
This commit is contained in:
Dan Stillman 2017-07-16 18:15:12 -04:00
parent 09a859d7e3
commit ef7da3486a
4 changed files with 78 additions and 21 deletions

View file

@ -241,7 +241,8 @@ describe("Zotero.DataObject", function() {
assert.isTrue(obj.hasChanged());
}
})
})
});
describe("#save()", function () {
it("should add new identifiers to cache", function* () {
@ -264,6 +265,33 @@ describe("Zotero.DataObject", function() {
}
})
it("should handle additional tag change in the middle of a save", function* () {
var item = yield createDataObject('item');
item.setTags(['a']);
var deferred = new Zotero.Promise.defer();
var origFunc = Zotero.Notifier.queue.bind(Zotero.Notifier);
sinon.stub(Zotero.Notifier, "queue").callsFake(function (event, type, ids, extraData) {
// Add a new tag after the first one has been added to the DB and before the save is
// finished. The changed state should've cleared before saving to the DB the first
// time, so the second setTags() should mark the item as changed and allow the new tag
// to be saved in the second saveTx().
if (event == 'add' && type == 'item-tag') {
item.setTags(['a', 'b']);
Zotero.Notifier.queue.restore();
deferred.resolve(item.saveTx());
}
origFunc(...arguments);
});
yield Zotero.Promise.all([item.saveTx(), deferred.promise]);
assert.sameMembers(item.getTags().map(o => o.tag), ['a', 'b']);
var tags = yield Zotero.DB.columnQueryAsync(
"SELECT name FROM tags JOIN itemTags USING (tagID) WHERE itemID=?", item.id
);
assert.sameMembers(tags, ['a', 'b']);
});
describe("Edit Check", function () {
var group;