More data layer changes

- Moved ::_get() and _set() from Collection/Search into DataObject, and
  disabled in Item
- Don't disable new items after save. We now put new objects into the
  DataObjects cache from save() so that changes made post-save are
  picked up by other code using .get().
- Added 'skipCache' save() option to avoid reloading data on new objects
  and adding them to the cache. (This will be used in syncing, where
  objects might be in another library where they're not needed right
  away.) Objects created with this option are instead disabled to
  prevent reuse.
- Modified some tests to try to make sure we're reloading everything properly
  after a save.
- Documented save() options
This commit is contained in:
Dan Stillman 2015-05-21 03:19:28 -04:00
parent 4792b7cd48
commit a3f4fe181f
12 changed files with 113 additions and 145 deletions

View file

@ -82,40 +82,6 @@ Zotero.defineProperty(Zotero.Collection.prototype, 'parent', {
}
});
Zotero.Collection.prototype._set = function (field, value) {
if (field == 'id' || field == 'libraryID' || field == 'key') {
return this._setIdentifier(field, value);
}
this._requireData('primaryData');
switch (field) {
case 'name':
value = value.trim().normalize();
break;
case 'version':
value = parseInt(value);
break;
case 'synced':
value = !!value;
break;
}
if (this['_' + field] != value) {
this._markFieldChange(field, this['_' + field]);
if (!this._changed.primaryData) {
this._changed.primaryData = {};
}
this._changed.primaryData[field] = true;
switch (field) {
default:
this['_' + field] = value;
}
}
}
Zotero.Collection.prototype.getID = function() {
Zotero.debug('Collection.getID() deprecated -- use Collection.id');
@ -340,15 +306,14 @@ Zotero.Collection.prototype._saveData = Zotero.Promise.coroutine(function* (env)
});
Zotero.Collection.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
var isNew = env.isNew;
if (isNew && Zotero.Libraries.isGroupLibrary(this.libraryID)) {
if (env.isNew && Zotero.Libraries.isGroupLibrary(this.libraryID)) {
var groupID = Zotero.Groups.getGroupIDFromLibraryID(this.libraryID);
var group = Zotero.Groups.get(groupID);
group.clearCollectionCache();
}
if (!env.options.skipNotifier) {
if (isNew) {
if (env.isNew) {
Zotero.Notifier.trigger('add', 'collection', this.id, env.notifierData);
}
else {
@ -361,17 +326,12 @@ Zotero.Collection.prototype._finalizeSave = Zotero.Promise.coroutine(function* (
this.ObjectsClass.refreshChildCollections(env.parentIDs);
}
// New collections have to be reloaded via Zotero.Collections.get(), so mark them as disabled
if (isNew) {
var id = this.id;
this._disabled = true;
return id;
if (!env.skipCache) {
yield this.reload();
this._clearChanged();
}
yield this.reload();
this._clearChanged();
return true;
return env.isNew ? this.id : true;
});

View file

@ -50,6 +50,9 @@ Zotero.DataObject = function () {
this._parentID = null;
this._parentKey = null;
// Set in dataObjects.js
this._inCache = false;
this._loaded = {};
this._skipDataTypeLoad = {};
this._markAllDataTypeLoadStates(false);
@ -90,6 +93,8 @@ Zotero.defineProperty(Zotero.DataObject.prototype, 'ObjectsClass', {
Zotero.DataObject.prototype._get = function (field) {
if (field != 'id') this._disabledCheck();
if (this['_' + field] !== null) {
return this['_' + field];
}
@ -98,6 +103,44 @@ Zotero.DataObject.prototype._get = function (field) {
}
Zotero.DataObject.prototype._set = function (field, value) {
this._disabledCheck();
if (field == 'id' || field == 'libraryID' || field == 'key') {
return this._setIdentifier(field, value);
}
this._requireData('primaryData');
switch (field) {
case 'name':
value = value.trim().normalize();
break;
case 'version':
value = parseInt(value);
break;
case 'synced':
value = !!value;
break;
}
if (this['_' + field] != value) {
this._markFieldChange(field, this['_' + field]);
if (!this._changed.primaryData) {
this._changed.primaryData = {};
}
this._changed.primaryData[field] = true;
switch (field) {
default:
this['_' + field] = value;
}
}
}
Zotero.DataObject.prototype._setIdentifier = function (field, value) {
// If primary data is loaded, the only allowed identifier change is libraryID
// (allowed mainly for the library switcher in the advanced search window),
@ -544,6 +587,14 @@ Zotero.DataObject.prototype.editCheck = function () {
/**
* Save changes to database
*
* @params {Object} [options]
* @params {Boolean} [options.skipCache] - Don't save add new object to the cache; if set, object
* is disabled after save
* @params {Boolean} [options.skipDateModifiedUpdate]
* @params {Boolean} [options.skipClientDateModifiedUpdate]
* @params {Boolean} [options.skipNotifier] - Don't trigger Zotero.Notifier events
* @params {Boolean} [options.skipSelect] - Don't select object automatically in trees
* @params {Boolean} [options.skipSyncedUpdate] - Don't automatically set 'synced' to false
* @return {Promise<Integer|Boolean>} Promise for itemID of new item,
* TRUE on item update, or FALSE if item was unchanged
*/
@ -650,7 +701,7 @@ Zotero.DataObject.prototype._initSave = Zotero.Promise.coroutine(function* (env)
return false;
}
// Undo registerIdentifiers() on failure
// Undo registerObject() on failure
var func = function () {
this.ObjectsClass.unload(env.id);
}.bind(this);
@ -699,9 +750,18 @@ Zotero.DataObject.prototype._saveData = function (env) {
};
Zotero.DataObject.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
// Register this object's identifiers in Zotero.DataObjects
if (env.isNew) {
this.ObjectsClass.registerIdentifiers(env.id, env.libraryID, env.key);
if (!env.skipCache) {
// Register this object's identifiers in Zotero.DataObjects
this.ObjectsClass.registerObject(this);
}
// If object isn't being reloaded, disable it, since its data may be out of date
else {
this._disabled = true;
}
}
else if (env.skipCache) {
Zotero.logError("skipCache is only for new objects");
}
});
@ -778,3 +838,10 @@ Zotero.DataObject.prototype._erasePostCommit = function(env) {
Zotero.DataObject.prototype._generateKey = function () {
return Zotero.Utilities.generateObjectKey();
}
Zotero.DataObject.prototype._disabledCheck = function () {
if (this._disabled) {
Zotero.logError(this._ObjectType + " is disabled -- "
+ "use Zotero." + this._ObjectTypePlural + ".getAsync()");
}
}

View file

@ -395,13 +395,19 @@ Zotero.DataObjects.prototype.reloadAll = function (libraryID) {
}
Zotero.DataObjects.prototype.registerIdentifiers = function (id, libraryID, key) {
Zotero.DataObjects.prototype.registerObject = function (obj) {
var id = obj.id;
var libraryID = obj.libraryID;
var key = obj.key;
Zotero.debug("Registering " + this._ZDO_object + " " + id + " as " + libraryID + "/" + key);
if (!this._objectIDs[libraryID]) {
this._objectIDs[libraryID] = {};
}
this._objectIDs[libraryID][key] = id;
this._objectKeys[id] = [libraryID, key];
this._objectCache[id] = obj;
obj._inCache = true;
}
@ -521,6 +527,7 @@ Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (library
obj.loadFromRow(rowObj, true);
if (!options || !options.noCache) {
this._objectCache[id] = obj;
obj._inCache = true;
}
}
loaded[id] = obj;

View file

@ -178,10 +178,14 @@ Zotero.Item.prototype.isPrimaryField = function (fieldName) {
return this.ObjectsClass.isPrimaryField(fieldName);
}
Zotero.Item.prototype._get = function (fieldName) {
Zotero.Item.prototype._get = function () {
throw new Error("_get is not valid for items");
}
Zotero.Item.prototype._set = function () {
throw new Error("_set is not valid for items");
}
Zotero.Item.prototype._setParentKey = function() {
if (!this.isNote() && !this.isAttachment()) {
throw new Error("_setParentKey() can only be called on items of type 'note' or 'attachment'");
@ -207,11 +211,7 @@ Zotero.Item.prototype._setParentKey = function() {
* @return {String} Value as string or empty string if value is not present
*/
Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped) {
// We don't allow access after saving to force use of the centrally cached
// object, but we make an exception for the id
if (field != 'id') {
this._disabledCheck();
}
if (field != 'id') this._disabledCheck();
//Zotero.debug('Requesting field ' + field + ' for item ' + this._id, 4);
@ -694,12 +694,12 @@ Zotero.Item.prototype.getFieldsNotInType = function (itemTypeID, allowBaseConver
* Field can be passed as fieldID or fieldName
*/
Zotero.Item.prototype.setField = function(field, value, loadIn) {
this._disabledCheck();
if (typeof value == 'string') {
value = value.trim().normalize();
}
this._disabledCheck();
//Zotero.debug("Setting field '" + field + "' to '" + value + "' (loadIn: " + (loadIn ? 'true' : 'false') + ") for item " + this.id + " ");
if (!field) {
@ -1730,21 +1730,16 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
});
Zotero.Item.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
// New items have to be reloaded via Zotero.Items.get(), so mark them as disabled
if (env.isNew) {
var id = this.id;
this._disabled = true;
return id;
if (!env.skipCache) {
// Always reload primary data. DataObject.reload() only reloads changed data types, so
// it won't reload, say, dateModified and firstCreator if only creator data was changed
// and not primaryData.
yield this.loadPrimaryData(true);
yield this.reload();
this._clearChanged();
}
// Always reload primary data. DataObject.reload() only reloads changed data types, so
// it won't reload, say, dateModified and firstCreator if only creator data was changed
// and not primaryData.
yield this.loadPrimaryData(true);
yield this.reload();
this._clearChanged();
return true;
return env.isNew ? this.id : true;
});
/**
@ -4807,12 +4802,3 @@ Zotero.Item.prototype._getOldCreators = function () {
}
return oldCreators;
}
Zotero.Item.prototype._disabledCheck = function () {
if (this._disabled) {
var msg = "New Zotero.Item objects shouldn't be accessed after save -- use Zotero.Items.get()";
Zotero.debug(msg, 2, true);
Components.utils.reportError(msg);
}
}

View file

@ -87,40 +87,6 @@ Zotero.defineProperty(Zotero.Search.prototype, 'conditions', {
get: function() this.getConditions()
});
Zotero.Search.prototype._set = function (field, value) {
if (field == 'id' || field == 'libraryID' || field == 'key') {
return this._setIdentifier(field, value);
}
this._requireData('primaryData');
switch (field) {
case 'name':
value = value.trim().normalize();
break;
case 'version':
value = parseInt(value);
break;
case 'synced':
value = !!value;
break;
}
if (this['_' + field] != value) {
this._markFieldChange(field, this['_' + field]);
if (!this._changed.primaryData) {
this._changed.primaryData = {};
}
this._changed.primaryData[field] = true;
switch (field) {
default:
this['_' + field] = value;
}
}
}
Zotero.Search.prototype.loadFromRow = function (row) {
this._id = row.savedSearchID;
@ -205,31 +171,26 @@ Zotero.Search.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
});
Zotero.Search.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
var isNew = env.isNew;
if (isNew) {
if (env.isNew) {
Zotero.Notifier.trigger('add', 'search', this.id, env.notifierData);
}
else if (!env.options.skipNotifier) {
Zotero.Notifier.trigger('modify', 'search', this.id, env.notifierData);
}
if (isNew && Zotero.Libraries.isGroupLibrary(this.libraryID)) {
if (env.isNew && Zotero.Libraries.isGroupLibrary(this.libraryID)) {
var groupID = Zotero.Groups.getGroupIDFromLibraryID(this.libraryID);
var group = yield Zotero.Groups.get(groupID);
group.clearSearchCache();
}
if (isNew) {
var id = this.id;
this._disabled = true;
return id;
if (!env.skipCache) {
yield this.loadPrimaryData(true);
yield this.reload();
this._clearChanged();
}
yield this.loadPrimaryData(true);
yield this.reload();
this._clearChanged();
return isNew ? this.id : true;
return env.isNew ? this.id : true;
});