[Code Style] Use prototypes in platform

WTD-1482
This commit is contained in:
Victor Woeltjen
2015-08-11 12:54:50 -07:00
parent f377c7cb71
commit b7765ff388
35 changed files with 1331 additions and 1379 deletions

View File

@ -39,68 +39,59 @@ define(
*
* @memberof platform/core
* @constructor
* @implements {Capability}
*/
function CompositionCapability($injector, domainObject) {
var objectService,
lastPromise,
lastModified;
// Get a reference to the object service from $injector
function injectObjectService() {
objectService = $injector.get("objectService");
return objectService;
}
// Get a reference to the object service (either cached or
// from the injector)
function getObjectService() {
return objectService || injectObjectService();
}
// Promise this domain object's composition (an array of domain
// object instances corresponding to ids in its model.)
function promiseComposition() {
var model = domainObject.getModel(),
ids;
// Then filter out non-existent objects,
// and wrap others (such that they expose a
// "context" capability)
function contextualize(objects) {
return ids.filter(function (id) {
return objects[id];
}).map(function (id) {
return new ContextualDomainObject(
objects[id],
domainObject
);
});
}
// Make a new request if we haven't made one, or if the
// object has been modified.
if (!lastPromise || lastModified !== model.modified) {
ids = model.composition || [];
lastModified = model.modified;
// Load from the underlying object service
lastPromise = getObjectService().getObjects(ids)
.then(contextualize);
}
return lastPromise;
}
return {
/**
* Request the composition of this object.
* @returns {Promise.<DomainObject[]>} a list of all domain
* objects which compose this domain object.
* @memberof platform/core.CompositionCapability#
*/
invoke: promiseComposition
this.injectObjectService = function () {
this.objectService = $injector.get("objectService");
};
this.domainObject = domainObject;
}
/**
* Request the composition of this object.
* @returns {Promise.<DomainObject[]>} a list of all domain
* objects which compose this domain object.
*/
CompositionCapability.prototype.invoke = function () {
var domainObject = this.domainObject,
model = domainObject.getModel(),
ids;
// Then filter out non-existent objects,
// and wrap others (such that they expose a
// "context" capability)
function contextualize(objects) {
return ids.filter(function (id) {
return objects[id];
}).map(function (id) {
return new ContextualDomainObject(
objects[id],
domainObject
);
});
}
// Lazily acquire object service (avoids cyclical dependency)
if (!this.objectService) {
this.injectObjectService();
}
// Make a new request if we haven't made one, or if the
// object has been modified.
if (!this.lastPromise || this.lastModified !== model.modified) {
ids = model.composition || [];
this.lastModified = model.modified;
// Load from the underlying object service
this.lastPromise = this.objectService.getObjects(ids)
.then(contextualize);
}
return this.lastPromise;
};
/**
* Test to determine whether or not this capability should be exposed
* by a domain object based on its model. Checks for the presence of

View File

@ -38,79 +38,76 @@ define(
*
* @memberof platform/core
* @constructor
* @implements {Capability}
*/
function ContextCapability(parentObject, domainObject) {
return {
/**
* Get the immediate parent of a domain object.
*
* A domain object may be contained in multiple places; its
* parent (as exposed by this capability) is the domain
* object from which this object was accessed, usually
* by way of a `composition` capability.
*
* @returns {DomainObject} the immediate parent of this
* domain object.
* @memberof platform/core.ContextCapability#
*/
getParent: function () {
return parentObject;
},
/**
* Get an array containing the complete direct ancestry
* of this domain object, including the domain object
* itself.
*
* A domain object may be contained in multiple places; its
* parent and all ancestors (as exposed by this capability)
* serve as a record of how this specific domain object
* instance was reached.
*
* The first element in the returned array is the deepest
* ancestor; subsequent elements are progressively more
* recent ancestors, with the domain object which exposed
* the capability occupying the last element of the array.
*
* @returns {DomainObject[]} the full composition ancestry
* of the domain object which exposed this
* capability.
* @memberof platform/core.ContextCapability#
*/
getPath: function () {
var parentPath = [],
parentContext;
if (parentObject) {
parentContext = parentObject.getCapability("context");
parentPath = parentContext ?
parentContext.getPath() :
[parentObject];
}
return parentPath.concat([domainObject]);
},
/**
* Get the deepest ancestor available for this domain object;
* equivalent to `getPath()[0]`.
*
* See notes on `getPath()` for how ancestry is defined in
* the context of this capability.
*
* @returns {DomainObject} the deepest ancestor of the domain
* object which exposed this capability.
* @memberof platform/core.ContextCapability#
*/
getRoot: function () {
var parentContext = parentObject &&
parentObject.getCapability('context');
return parentContext ?
parentContext.getRoot() :
(parentObject || domainObject);
}
};
this.parentObject = parentObject;
this.domainObject = domainObject;
}
/**
* Get the immediate parent of a domain object.
*
* A domain object may be contained in multiple places; its
* parent (as exposed by this capability) is the domain
* object from which this object was accessed, usually
* by way of a `composition` capability.
*
* @returns {DomainObject} the immediate parent of this
* domain object.
*/
ContextCapability.prototype.getParent = function () {
return this.parentObject;
};
/**
* Get an array containing the complete direct ancestry
* of this domain object, including the domain object
* itself.
*
* A domain object may be contained in multiple places; its
* parent and all ancestors (as exposed by this capability)
* serve as a record of how this specific domain object
* instance was reached.
*
* The first element in the returned array is the deepest
* ancestor; subsequent elements are progressively more
* recent ancestors, with the domain object which exposed
* the capability occupying the last element of the array.
*
* @returns {DomainObject[]} the full composition ancestry
* of the domain object which exposed this
* capability.
*/
ContextCapability.prototype.getPath = function () {
var parentObject = this.parentObject,
parentContext =
parentObject && parentObject.getCapability('context'),
parentPath = parentContext ?
parentContext.getPath() : [ this.parentObject ];
return parentPath.concat([this.domainObject]);
};
/**
* Get the deepest ancestor available for this domain object;
* equivalent to `getPath()[0]`.
*
* See notes on `getPath()` for how ancestry is defined in
* the context of this capability.
*
* @returns {DomainObject} the deepest ancestor of the domain
* object which exposed this capability.
*/
ContextCapability.prototype.getRoot = function () {
var parentContext = this.parentObject &&
this.parentObject.getCapability('context');
return parentContext ?
parentContext.getRoot() :
(this.parentObject || this.domainObject);
};
return ContextCapability;
}
);

View File

@ -44,6 +44,7 @@ define(
*
* @memberof platform/core
* @constructor
* @implements {DomainObject}
*/
function ContextualDomainObject(domainObject, parentObject) {
// Prototypally inherit from the domain object, and

View File

@ -29,6 +29,19 @@ define(
function () {
"use strict";
/**
* A capability provides an interface with dealing with some
* dynamic behavior associated with a domain object.
* @interface Capability
*/
/**
* Optional; if present, will be used by `DomainObject#useCapability`
* to simplify interaction with a specific capability. Parameters
* and return values vary depending on capability type.
* @method Capability#invoke
*/
/**
* Provides capabilities based on extension definitions,
* matched to domain object models.

View File

@ -45,13 +45,40 @@ define(
* in the type's definition, which contains an array of names of
* capabilities to be delegated.
*
* @param domainObject
* @param $q Angular's $q, for promises
* @param {DomainObject} domainObject the delegating domain object
* @memberof platform/core
* @constructor
* @implements {Capability}
*/
function DelegationCapability($q, domainObject) {
var delegateCapabilities = {},
type = domainObject.getCapability("type");
var type = domainObject.getCapability("type"),
self = this;
this.$q = $q;
this.delegateCapabilities = {};
this.domainObject = domainObject;
// Generate set for easy lookup of capability delegation
if (type && type.getDefinition) {
(type.getDefinition().delegates || []).forEach(function (key) {
self.delegateCapabilities[key] = true;
});
}
}
/**
* Get the domain objects which are intended to be delegated
* responsibility for some specific capability.
*
* @param {string} key the name of the delegated capability
* @returns {DomainObject[]} the domain objects to which
* responsibility for this capability is delegated.
* @memberof platform/core.DelegationCapability#
*/
DelegationCapability.prototype.getDelegates = function (key) {
var domainObject = this.domainObject;
function filterObjectsWithCapability(capability) {
return function (objects) {
@ -65,55 +92,40 @@ define(
return domainObject.useCapability('composition');
}
function doesDelegate(key) {
return delegateCapabilities[key] || false;
}
return this.doesDelegateCapability(key) ?
promiseChildren().then(
filterObjectsWithCapability(key)
) :
this.$q.when([]);
};
function getDelegates(capability) {
return doesDelegate(capability) ?
promiseChildren().then(
filterObjectsWithCapability(capability)
) :
$q.when([]);
}
// Generate set for easy lookup of capability delegation
if (type && type.getDefinition) {
(type.getDefinition().delegates || []).forEach(function (key) {
delegateCapabilities[key] = true;
});
}
return {
/**
* Invoke this capability; alias of `getDelegates`, used to
* simplify usage, e.g.:
*
* `domainObject.useCapability("delegation", "telemetry")`
*
* ...will retrieve all members of a domain object's
* composition which have a "telemetry" capability.
*
* @param {string} the name of the delegated capability
* @returns {DomainObject[]} the domain objects to which
* responsibility for this capability is delegated.
* @memberof platform/core.DelegationCapability#
*/
invoke: getDelegates,
/**
* Get the domain objects which are intended to be delegated
* responsibility for some specific capability.
*
* @param {string} the name of the delegated capability
* @returns {DomainObject[]} the domain objects to which
* responsibility for this capability is delegated.
* @memberof platform/core.DelegationCapability#
*/
getDelegates: getDelegates,
doesDelegateCapability: doesDelegate
};
}
/**
* Check if the domain object which exposed this capability
* wishes to delegate another capability.
*
* @param {string} key the capability to check for
* @returns {boolean} true if the capability is delegated
*/
DelegationCapability.prototype.doesDelegateCapability = function (key) {
return !!(this.delegateCapabilities[key]);
};
/**
* Invoke this capability; alias of `getDelegates`, used to
* simplify usage, e.g.:
*
* `domainObject.useCapability("delegation", "telemetry")`
*
* ...will retrieve all members of a domain object's
* composition which have a "telemetry" capability.
*
* @param {string} the name of the delegated capability
* @returns {DomainObject[]} the domain objects to which
* responsibility for this capability is delegated.
* @memberof platform/core.DelegationCapability#
*/
DelegationCapability.prototype.invoke =
DelegationCapability.prototype.getDelegates;
return DelegationCapability;

View File

@ -11,8 +11,6 @@ define(
* @property {string} name the human-readable name of this property
* @property {string} value the human-readable value of this property,
* for this specific domain object
* @constructor
* @memberof platform/core
*/
var TIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
@ -27,10 +25,23 @@ define(
* `value` properties describing that domain object (suitable for
* display.)
*
* @param {DomainObject} domainObject the domain object whose
* metadata is to be exposed
* @implements {Capability}
* @constructor
* @memberof platform/core
*/
function MetadataCapability(domainObject) {
var model = domainObject.getModel();
this.domainObject = domainObject;
}
/**
* Get metadata about this object.
* @returns {MetadataProperty[]} metadata about this object
*/
MetadataCapability.prototype.invoke = function () {
var domainObject = this.domainObject,
model = domainObject.getModel();
function hasDisplayableValue(metadataProperty) {
var t = typeof metadataProperty.value;
@ -39,8 +50,8 @@ define(
function formatTimestamp(timestamp) {
return typeof timestamp === 'number' ?
(moment.utc(timestamp).format(TIME_FORMAT) + " UTC") :
undefined;
(moment.utc(timestamp).format(TIME_FORMAT) + " UTC") :
undefined;
}
function getProperties() {
@ -75,20 +86,9 @@ define(
];
}
function getMetadata() {
return getProperties().concat(getCommonMetadata())
.filter(hasDisplayableValue);
}
return {
/**
* Get metadata about this object.
* @returns {MetadataProperty[]} metadata about this object
* @memberof platform/core.MetadataCapability#
*/
invoke: getMetadata
};
}
return getProperties().concat(getCommonMetadata())
.filter(hasDisplayableValue);
};
return MetadataCapability;
}

View File

@ -69,100 +69,103 @@ define(
* });
* ```
*
* @param {Function} topic a service for creating listeners
* @param {Function} now a service to get the current time
* @param {DomainObject} domainObject the domain object
* which will expose this capability
* @memberof platform/core
* @constructor
* @implements {Capability}
*/
function MutationCapability(topic, now, domainObject) {
var t = topic(TOPIC_PREFIX + domainObject.getId());
this.mutationTopic = topic(TOPIC_PREFIX + domainObject.getId());
this.now = now;
this.domainObject = domainObject;
}
function mutate(mutator, timestamp) {
// Get the object's model and clone it, so the
// mutator function has a temporary copy to work with.
var model = domainObject.getModel(),
clone = JSON.parse(JSON.stringify(model)),
useTimestamp = arguments.length > 1;
/**
* Modify the domain object's model, using a provided
* function. This function will receive a copy of the
* domain object's model as an argument; behavior
* varies depending on that function's return value:
*
* * If no value (or undefined) is returned by the mutator,
* the state of the model object delivered as the mutator's
* argument will become the domain object's new model.
* This is useful for writing code that modifies the model
* directly.
* * If a plain object is returned, that object will be used
* as the domain object's new model.
* * If boolean `false` is returned, the mutation will be
* cancelled.
* * If a promise is returned, its resolved value will be
* handled as one of the above.
*
*
* @param {Function} mutator the function which will make
* changes to the domain object's model.
* @param {number} [timestamp] timestamp to record for
* this mutation (otherwise, system time will be
* used)
* @returns {Promise.<boolean>} a promise for the result
* of the mutation; true if changes were made.
*/
MutationCapability.prototype.mutate = function (mutator, timestamp) {
// Get the object's model and clone it, so the
// mutator function has a temporary copy to work with.
var domainObject = this.domainObject,
now = this.now,
t = this.mutationTopic,
model = domainObject.getModel(),
clone = JSON.parse(JSON.stringify(model)),
useTimestamp = arguments.length > 1;
// Function to handle copying values to the actual
function handleMutation(mutationResult) {
// If mutation result was undefined, just use
// the clone; this allows the mutator to omit return
// values and just change the model directly.
var result = mutationResult || clone;
// Function to handle copying values to the actual
function handleMutation(mutationResult) {
// If mutation result was undefined, just use
// the clone; this allows the mutator to omit return
// values and just change the model directly.
var result = mutationResult || clone;
// Allow mutators to change their mind by
// returning false.
if (mutationResult !== false) {
// Copy values if result was a different object
// (either our clone or some other new thing)
if (model !== result) {
copyValues(model, result);
}
model.modified = useTimestamp ? timestamp : now();
t.notify(model);
// Allow mutators to change their mind by
// returning false.
if (mutationResult !== false) {
// Copy values if result was a different object
// (either our clone or some other new thing)
if (model !== result) {
copyValues(model, result);
}
// Report the result of the mutation
return mutationResult !== false;
model.modified = useTimestamp ? timestamp : now();
t.notify(model);
}
// Invoke the provided mutator, then make changes to
// the underlying model (if applicable.)
return fastPromise(mutator(clone)).then(handleMutation);
// Report the result of the mutation
return mutationResult !== false;
}
function listen(listener) {
return t.listen(listener);
}
// Invoke the provided mutator, then make changes to
// the underlying model (if applicable.)
return fastPromise(mutator(clone)).then(handleMutation);
};
return {
/**
* Alias of `mutate`, used to support useCapability.
* @memberof platform/core.MutationCapability#
*/
invoke: mutate,
/**
* Modify the domain object's model, using a provided
* function. This function will receive a copy of the
* domain object's model as an argument; behavior
* varies depending on that function's return value:
*
* * If no value (or undefined) is returned by the mutator,
* the state of the model object delivered as the mutator's
* argument will become the domain object's new model.
* This is useful for writing code that modifies the model
* directly.
* * If a plain object is returned, that object will be used
* as the domain object's new model.
* * If boolean `false` is returned, the mutation will be
* cancelled.
* * If a promise is returned, its resolved value will be
* handled as one of the above.
*
*
* @param {function} mutator the function which will make
* changes to the domain object's model.
* @param {number} [timestamp] timestamp to record for
* this mutation (otherwise, system time will be
* used)
* @returns {Promise.<boolean>} a promise for the result
* of the mutation; true if changes were made.
* @memberof platform/core.MutationCapability#
*/
mutate: mutate,
/**
* Listen for mutations of this domain object's model.
* The provided listener will be invoked with the domain
* object's new model after any changes. To stop listening,
* invoke the function returned by this method.
* @param {Function} listener function to call on mutation
* @returns {Function} a function to stop listening
* @memberof platform/core.MutationCapability#
*/
listen: listen
};
}
/**
* Listen for mutations of this domain object's model.
* The provided listener will be invoked with the domain
* object's new model after any changes. To stop listening,
* invoke the function returned by this method.
* @param {Function} listener function to call on mutation
* @returns {Function} a function to stop listening
* @memberof platform/core.MutationCapability#
*/
MutationCapability.prototype.listen = function (listener) {
return this.mutationTopic.listen(listener);
};
/**
* Alias of `mutate`, used to support useCapability.
*/
MutationCapability.prototype.invoke =
MutationCapability.prototype.mutate;
return MutationCapability;
}

View File

@ -33,7 +33,7 @@ define(
*
* @param {PersistenceService} persistenceService the underlying
* provider of persistence capabilities.
* @param {string} SPACE the name of the persistence space to
* @param {string} space the name of the persistence space to
* use (this is an arbitrary string, useful in principle
* for distinguishing different persistence stores from
* one another.)
@ -42,10 +42,60 @@ define(
*
* @memberof platform/core
* @constructor
* @implements {Capability}
*/
function PersistenceCapability(persistenceService, SPACE, domainObject) {
function PersistenceCapability(persistenceService, space, domainObject) {
// Cache modified timestamp
var modified = domainObject.getModel().modified;
this.modified = domainObject.getModel().modified;
this.domainObject = domainObject;
this.space = space;
this.persistenceService = persistenceService;
}
// Utility function for creating promise-like objects which
// resolve synchronously when possible
function fastPromise(value) {
return (value || {}).then ? value : {
then: function (callback) {
return fastPromise(callback(value));
}
};
}
/**
* Persist any changes which have been made to this
* domain object's model.
* @returns {Promise} a promise which will be resolved
* if persistence is successful, and rejected
* if not.
*/
PersistenceCapability.prototype.persist = function () {
var domainObject = this.domainObject,
modified = domainObject.getModel().modified;
// Update persistence timestamp...
domainObject.useCapability("mutation", function (model) {
model.persisted = modified;
}, modified);
// ...and persist
return this.persistenceService.updateObject(
this.getSpace(),
domainObject.getId(),
domainObject.getModel()
);
};
/**
* Update this domain object to match the latest from
* persistence.
* @returns {Promise} a promise which will be resolved
* when the update is complete
*/
PersistenceCapability.prototype.refresh = function () {
var domainObject = this.domainObject,
model = domainObject.getModel();
// Update a domain object's model upon refresh
function updateModel(model) {
@ -55,75 +105,28 @@ define(
}, modified);
}
// For refresh; update a domain object model, only if there
// are no unsaved changes.
function updatePersistenceTimestamp() {
var modified = domainObject.getModel().modified;
domainObject.useCapability("mutation", function (model) {
model.persisted = modified;
}, modified);
}
// Only update if we don't have unsaved changes
return (model.modified === model.persisted) ?
this.persistenceService.readObject(
this.getSpace(),
this.domainObject.getId()
).then(updateModel) :
fastPromise(false);
};
// Utility function for creating promise-like objects which
// resolve synchronously when possible
function fastPromise(value) {
return (value || {}).then ? value : {
then: function (callback) {
return fastPromise(callback(value));
}
};
}
return {
/**
* Persist any changes which have been made to this
* domain object's model.
* @returns {Promise} a promise which will be resolved
* if persistence is successful, and rejected
* if not.
* @memberof platform/core.PersistenceCapability#
*/
persist: function () {
updatePersistenceTimestamp();
return persistenceService.updateObject(
SPACE,
domainObject.getId(),
domainObject.getModel()
);
},
/**
* Update this domain object to match the latest from
* persistence.
* @returns {Promise} a promise which will be resolved
* when the update is complete
* @memberof platform/core.PersistenceCapability#
*/
refresh: function () {
var model = domainObject.getModel();
// Only update if we don't have unsaved changes
return (model.modified === model.persisted) ?
persistenceService.readObject(
SPACE,
domainObject.getId()
).then(updateModel) :
fastPromise(false);
},
/**
* Get the space in which this domain object is persisted;
* this is useful when, for example, decided which space a
* newly-created domain object should be persisted to (by
* default, this should be the space of its containing
* object.)
*
* @returns {string} the name of the space which should
* be used to persist this object
* @memberof platform/core.PersistenceCapability#
*/
getSpace: function () {
return SPACE;
}
};
}
/**
* Get the space in which this domain object is persisted;
* this is useful when, for example, decided which space a
* newly-created domain object should be persisted to (by
* default, this should be the space of its containing
* object.)
*
* @returns {string} the name of the space which should
* be used to persist this object
*/
PersistenceCapability.prototype.getSpace = function () {
return this.space;
};
return PersistenceCapability;
}

View File

@ -40,92 +40,80 @@ define(
*
* @memberof platform/core
* @constructor
* @implements {Capability}
*/
function RelationshipCapability($injector, domainObject) {
var objectService,
lastPromise = {},
lastModified;
// Get a reference to the object service from $injector
function injectObjectService() {
objectService = $injector.get("objectService");
return objectService;
}
// Get a reference to the object service (either cached or
// from the injector)
function getObjectService() {
return objectService || injectObjectService();
}
// Promise this domain object's composition (an array of domain
// object instances corresponding to ids in its model.)
function promiseRelationships(key) {
var model = domainObject.getModel(),
ids;
// Package objects as an array
function packageObject(objects) {
return ids.map(function (id) {
return objects[id];
}).filter(function (obj) {
return obj;
});
}
// Clear cached promises if modification has occurred
if (lastModified !== model.modified) {
lastPromise = {};
lastModified = model.modified;
}
// Make a new request if needed
if (!lastPromise[key]) {
ids = (model.relationships || {})[key] || [];
lastModified = model.modified;
// Load from the underlying object service
lastPromise[key] = getObjectService().getObjects(ids)
.then(packageObject);
}
return lastPromise[key];
}
// List types of relationships which this object has
function listRelationships() {
var relationships =
(domainObject.getModel() || {}).relationships || {};
// Check if this key really does expose an array of ids
// (to filter out malformed relationships)
function isArray(key) {
return Array.isArray(relationships[key]);
}
return Object.keys(relationships).filter(isArray).sort();
}
return {
/**
* List all types of relationships exposed by this
* object.
* @returns {string[]} a list of all relationship types
* @memberof platform/core.RelationshipCapability#
*/
listRelationships: listRelationships,
/**
* Request related objects, with a given relationship type.
* This will typically require asynchronous lookup, so this
* returns a promise.
* @param {string} key the type of relationship
* @returns {Promise.<DomainObject[]>} a promise for related
* domain objects
* @memberof platform/core.RelationshipCapability#
*/
getRelatedObjects: promiseRelationships
this.injectObjectService = function () {
this.objectService = $injector.get("objectService");
};
this.lastPromise = {};
this.domainObject = domainObject;
}
/**
* List all types of relationships exposed by this
* object.
* @returns {string[]} a list of all relationship types
*/
RelationshipCapability.prototype.listRelationships = function listRelationships() {
var relationships =
(this.domainObject.getModel() || {}).relationships || {};
// Check if this key really does expose an array of ids
// (to filter out malformed relationships)
function isArray(key) {
return Array.isArray(relationships[key]);
}
return Object.keys(relationships).filter(isArray).sort();
};
/**
* Request related objects, with a given relationship type.
* This will typically require asynchronous lookup, so this
* returns a promise.
* @param {string} key the type of relationship
* @returns {Promise.<DomainObject[]>} a promise for related
* domain objects
*/
RelationshipCapability.prototype.getRelatedObjects = function (key) {
var model = this.domainObject.getModel(),
ids;
// Package objects as an array
function packageObject(objects) {
return ids.map(function (id) {
return objects[id];
}).filter(function (obj) {
return obj;
});
}
// Clear cached promises if modification has occurred
if (this.lastModified !== model.modified) {
this.lastPromise = {};
this.lastModified = model.modified;
}
// Make a new request if needed
if (!this.lastPromise[key]) {
ids = (model.relationships || {})[key] || [];
this.lastModified = model.modified;
// Lazily initialize object service now that we need it
if (!this.objectService) {
this.injectObjectService();
}
// Load from the underlying object service
this.lastPromise[key] = this.objectService.getObjects(ids)
.then(packageObject);
}
return this.lastPromise[key];
};
/**
* Test to determine whether or not this capability should be exposed
* by a domain object based on its model. Checks for the presence of