[Code Style] Use prototypes in persistence queue

WTD-1482
This commit is contained in:
Victor Woeltjen
2015-08-14 15:40:20 -07:00
parent 3e8ea972c2
commit f8cb3f464c
5 changed files with 155 additions and 158 deletions

View File

@ -33,25 +33,25 @@ define(
* @memberof platform/persistence/queue * @memberof platform/persistence/queue
*/ */
function PersistenceFailureController() { function PersistenceFailureController() {
return {
/**
* Format a timestamp for display in the dialog.
* @memberof platform/persistence/queue.PersistenceFailureController#
*/
formatTimestamp: function (timestamp) {
return moment.utc(timestamp)
.format(Constants.TIMESTAMP_FORMAT);
},
/**
* Format a user name for display in the dialog.
* @memberof platform/persistence/queue.PersistenceFailureController#
*/
formatUsername: function (username) {
return username || Constants.UNKNOWN_USER;
}
};
} }
/**
* Format a timestamp for display in the dialog.
* @memberof platform/persistence/queue.PersistenceFailureController#
*/
PersistenceFailureController.prototype.formatTimestamp = function (timestamp) {
return moment.utc(timestamp)
.format(Constants.TIMESTAMP_FORMAT);
};
/**
* Format a user name for display in the dialog.
* @memberof platform/persistence/queue.PersistenceFailureController#
*/
PersistenceFailureController.prototype.formatUsername = function (username) {
return username || Constants.UNKNOWN_USER;
};
return PersistenceFailureController; return PersistenceFailureController;
} }
); );

View File

@ -34,6 +34,24 @@ define(
* @memberof platform/persistence/queue * @memberof platform/persistence/queue
*/ */
function PersistenceFailureHandler($q, dialogService) { function PersistenceFailureHandler($q, dialogService) {
this.$q = $q;
this.dialogService = dialogService;
}
/**
* Handle persistence failures by providing the user with a
* dialog summarizing these failures, and giving the option
* to overwrite/cancel as appropriate.
* @param {Array} failures persistence failures, as prepared
* by PersistenceQueueHandler
* @memberof platform/persistence/queue.PersistenceFailureHandler#
*/
PersistenceFailureHandler.prototype.handle = function handleFailures(failures) {
// Prepare dialog for display
var dialogModel = new PersistenceFailureDialog(failures),
revisionErrors = dialogModel.model.revised,
$q = this.$q;
// Refresh revision information for the domain object associated // Refresh revision information for the domain object associated
// with this persistence failure // with this persistence failure
function refresh(failure) { function refresh(failure) {
@ -100,39 +118,20 @@ define(
return $q.all(failures.map(discard)); return $q.all(failures.map(discard));
} }
// Handle failures in persistence // Handle user input (did they choose to overwrite?)
function handleFailures(failures) { function handleChoice(key) {
// Prepare dialog for display // If so, try again
var dialogModel = new PersistenceFailureDialog(failures), if (key === PersistenceFailureConstants.OVERWRITE_KEY) {
revisionErrors = dialogModel.model.revised; return retry(revisionErrors);
} else {
// Handle user input (did they choose to overwrite?) return discardAll(revisionErrors);
function handleChoice(key) {
// If so, try again
if (key === PersistenceFailureConstants.OVERWRITE_KEY) {
return retry(revisionErrors);
} else {
return discardAll(revisionErrors);
}
} }
// Prompt for user input, the overwrite if they said so.
return dialogService.getUserChoice(dialogModel)
.then(handleChoice, handleChoice);
} }
return { // Prompt for user input, the overwrite if they said so.
/** return this.dialogService.getUserChoice(dialogModel)
* Handle persistence failures by providing the user with a .then(handleChoice, handleChoice);
* dialog summarizing these failures, and giving the option };
* to overwrite/cancel as appropriate.
* @param {Array} failures persistence failures, as prepared
* by PersistenceQueueHandler
* @memberof platform/persistence/queue.PersistenceFailureHandler#
*/
handle: handleFailures
};
}
return PersistenceFailureHandler; return PersistenceFailureHandler;
} }

View File

@ -38,6 +38,25 @@ define(
* @memberof platform/persistence/queue * @memberof platform/persistence/queue
*/ */
function PersistenceQueueHandler($q, failureHandler) { function PersistenceQueueHandler($q, failureHandler) {
this.$q = $q;
this.failureHandler = failureHandler;
}
/**
* Invoke the persist method on the provided persistence
* capabilities.
* @param {Object.<string,PersistenceCapability>} persistences
* capabilities to invoke, in id->capability pairs.
* @param {Object.<string,DomainObject>} domainObjects
* associated domain objects, in id->object pairs.
* @param {PersistenceQueue} queue the persistence queue,
* to requeue as necessary
* @memberof platform/persistence/queue.PersistenceQueueHandler#
*/
PersistenceQueueHandler.prototype.persist = function (persistences, domainObjects, queue) {
var ids = Object.keys(persistences),
$q = this.$q,
failureHandler = this.failureHandler;
// Handle a group of persistence invocations // Handle a group of persistence invocations
function persistGroup(ids, persistences, domainObjects, queue) { function persistGroup(ids, persistences, domainObjects, queue) {
@ -81,33 +100,16 @@ define(
// Handle any failures from the full operation // Handle any failures from the full operation
function handleFailure(value) { function handleFailure(value) {
return failures.length > 0 ? return failures.length > 0 ?
failureHandler.handle(failures) : failureHandler.handle(failures) :
value; value;
} }
// Try to persist everything, then handle any failures // Try to persist everything, then handle any failures
return $q.all(ids.map(tryPersist)).then(handleFailure); return $q.all(ids.map(tryPersist)).then(handleFailure);
} }
return persistGroup(ids, persistences, domainObjects, queue);
return { };
/**
* Invoke the persist method on the provided persistence
* capabilities.
* @param {Object.<string,PersistenceCapability>} persistences
* capabilities to invoke, in id->capability pairs.
* @param {Object.<string,DomainObject>} domainObjects
* associated domain objects, in id->object pairs.
* @param {PersistenceQueue} queue the persistence queue,
* to requeue as necessary
* @memberof platform/persistence/queue.PersistenceQueueHandler#
*/
persist: function (persistences, domainObjects, queue) {
var ids = Object.keys(persistences);
return persistGroup(ids, persistences, domainObjects, queue);
}
};
}
return PersistenceQueueHandler; return PersistenceQueueHandler;
} }

View File

@ -44,18 +44,32 @@ define(
* @constructor * @constructor
* @memberof platform/persistence/queue * @memberof platform/persistence/queue
*/ */
function PersistenceQueueImpl($q, $timeout, handler, DELAY) { function PersistenceQueueImpl($q, $timeout, handler, delay) {
var self,
persistences = {}, this.persistences = {};
objects = {}, this.objects = {};
lastObservedSize = 0, this.lastObservedSize = 0;
pendingTimeout, this.activeDefer = $q.defer();
flushPromise,
activeDefer = $q.defer(); // If no delay is provided, use a default
this.delay = delay || 0;
this.handler = handler;
this.$timeout = $timeout;
this.$q = $q;
}
// Schedule a flushing of the queue (that is, plan to flush
// all objects in the queue)
PersistenceQueueImpl.prototype.scheduleFlush = function () {
var self = this,
$timeout = this.$timeout,
$q = this.$q,
handler = this.handler;
// Check if the queue's size has stopped increasing) // Check if the queue's size has stopped increasing)
function quiescent() { function quiescent() {
return Object.keys(persistences).length === lastObservedSize; return Object.keys(self.persistences).length
=== self.lastObservedSize;
} }
// Persist all queued objects // Persist all queued objects
@ -64,74 +78,71 @@ define(
// this will be replaced with a promise for the next round // this will be replaced with a promise for the next round
// of persistence calls, so we want to make sure we clear // of persistence calls, so we want to make sure we clear
// the correct one when this flush completes. // the correct one when this flush completes.
var flushingDefer = activeDefer; var flushingDefer = self.activeDefer;
// Clear the active promise for a queue flush // Clear the active promise for a queue flush
function clearFlushPromise(value) { function clearFlushPromise(value) {
flushPromise = undefined; self.flushPromise = undefined;
flushingDefer.resolve(value); flushingDefer.resolve(value);
return value; return value;
} }
// Persist all queued objects // Persist all queued objects
flushPromise = handler.persist(persistences, objects, self) self.flushPromise = handler.persist(
.then(clearFlushPromise, clearFlushPromise); self.persistences,
self.objects,
self
).then(clearFlushPromise, clearFlushPromise);
// Reset queue, etc. // Reset queue, etc.
persistences = {}; self.persistences = {};
objects = {}; self.objects = {};
lastObservedSize = 0; self.lastObservedSize = 0;
pendingTimeout = undefined; self.pendingTimeout = undefined;
activeDefer = $q.defer(); self.activeDefer = $q.defer();
} }
// Schedule a flushing of the queue (that is, plan to flush function maybeFlush() {
// all objects in the queue) // Timeout fired, so clear it
function scheduleFlush() { self.pendingTimeout = undefined;
function maybeFlush() { // Only flush when we've stopped receiving updates
// Timeout fired, so clear it if (quiescent()) {
pendingTimeout = undefined; flush();
// Only flush when we've stopped receiving updates
(quiescent() ? flush : scheduleFlush)();
// Update lastObservedSize to detect quiescence
lastObservedSize = Object.keys(persistences).length;
}
// If we are already flushing the queue...
if (flushPromise) {
// Wait until that's over before considering a flush
flushPromise.then(maybeFlush);
} else { } else {
// Otherwise, schedule a flush on a timeout (to give self.scheduleFlush();
// a window for other updates to get aggregated)
pendingTimeout = pendingTimeout ||
$timeout(maybeFlush, DELAY, false);
} }
// Update lastObservedSize to detect quiescence
return activeDefer.promise; self.lastObservedSize = Object.keys(self.persistences).length;
} }
// If no delay is provided, use a default // If we are already flushing the queue...
DELAY = DELAY || 0; if (self.flushPromise) {
// Wait until that's over before considering a flush
self.flushPromise.then(maybeFlush);
} else {
// Otherwise, schedule a flush on a timeout (to give
// a window for other updates to get aggregated)
self.pendingTimeout = self.pendingTimeout ||
$timeout(maybeFlush, self.delay, false);
}
self = { return self.activeDefer.promise;
/** };
* Queue persistence of a domain object.
* @param {DomainObject} domainObject the domain object
* @param {PersistenceCapability} persistence the object's
* undecorated persistence capability
* @memberof platform/persistence/queue.PersistenceQueueImpl#
*/
put: function (domainObject, persistence) {
var id = domainObject.getId();
persistences[id] = persistence;
objects[id] = domainObject;
return scheduleFlush();
}
};
return self;
} /**
* Queue persistence of a domain object.
* @param {DomainObject} domainObject the domain object
* @param {PersistenceCapability} persistence the object's
* undecorated persistence capability
* @returns {Promise} a promise which will resolve upon persistence
*/
PersistenceQueueImpl.prototype.put = function (domainObject, persistence) {
var id = domainObject.getId();
this.persistences[id] = persistence;
this.objects[id] = domainObject;
return this.scheduleFlush();
};
return PersistenceQueueImpl; return PersistenceQueueImpl;
} }

View File

@ -40,11 +40,21 @@ define(
* *
* @memberof platform/persistence/queue * @memberof platform/persistence/queue
* @constructor * @constructor
* @implements {CapabilityService}
* @param {platform/persistence/queue.PersistenceQueue} persistenceQueue
* @param {CapabilityService} the decorated capability service
*/ */
function QueuingPersistenceCapabilityDecorator( function QueuingPersistenceCapabilityDecorator(
persistenceQueue, persistenceQueue,
capabilityService capabilityService
) { ) {
this.persistenceQueue = persistenceQueue;
this.capabilityService = capabilityService;
}
QueuingPersistenceCapabilityDecorator.prototype.getCapabilities = function (model) {
var capabilityService = this.capabilityService,
persistenceQueue = this.persistenceQueue;
function decoratePersistence(capabilities) { function decoratePersistence(capabilities) {
var originalPersistence = capabilities.persistence; var originalPersistence = capabilities.persistence;
@ -53,8 +63,8 @@ define(
// Get/instantiate the original // Get/instantiate the original
var original = var original =
(typeof originalPersistence === 'function') ? (typeof originalPersistence === 'function') ?
originalPersistence(domainObject) : originalPersistence(domainObject) :
originalPersistence; originalPersistence;
// Provide a decorated version // Provide a decorated version
return new QueuingPersistenceCapability( return new QueuingPersistenceCapability(
@ -67,35 +77,10 @@ define(
return capabilities; return capabilities;
} }
function getCapabilities(model) { return decoratePersistence(
return decoratePersistence( capabilityService.getCapabilities(model)
capabilityService.getCapabilities(model) );
); };
}
return {
/**
* Get all capabilities associated with a given domain
* object.
*
* This returns a promise for an object containing key-value
* pairs, where keys are capability names and values are
* either:
*
* * Capability instances
* * Capability constructors (which take a domain object
* as their argument.)
*
*
* @param {*} model the object model
* @returns {Object.<string,function|Capability>} all
* capabilities known to be valid for this model, as
* key-value pairs
* @memberof platform/persistence/queue.QueuingPersistenceCapabilityDecorator#
*/
getCapabilities: getCapabilities
};
}
return QueuingPersistenceCapabilityDecorator; return QueuingPersistenceCapabilityDecorator;
} }