[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

@ -108,40 +108,18 @@ define(
* of action services * of action services
*/ */
function ActionAggregator(actionProviders) { function ActionAggregator(actionProviders) {
this.actionProviders = actionProviders;
}
function getActions(context) { ActionAggregator.prototype.getActions = function (context) {
// Get all actions from all providers, reduce down // Get all actions from all providers, reduce down
// to one array by concatenation // to one array by concatenation
return actionProviders.map(function (provider) { return this.actionProviders.map(function (provider) {
return provider.getActions(context); return provider.getActions(context);
}).reduce(function (a, b) { }).reduce(function (a, b) {
return a.concat(b); return a.concat(b);
}, []); }, []);
}
return {
/**
* Get a list of actions which are valid in a given
* context.
*
* @param {ActionContext} the context in which
* the action will occur; this is a
* JavaScript object containing key-value
* pairs. Typically, this will contain a
* field "domainObject" which refers to
* the domain object that will be acted
* upon, but may contain arbitrary information
* recognized by specific providers.
* @return {Action[]} an array of actions which
* may be performed in the provided context.
*
* @method
* @memberof ActionAggregator
* @memberof platform/core.ActionAggregator#
*/
getActions: getActions
}; };
}
return ActionAggregator; return ActionAggregator;
} }

View File

@ -49,34 +49,11 @@ define(
* @constructor * @constructor
*/ */
function ActionCapability($q, actionService, domainObject) { function ActionCapability($q, actionService, domainObject) {
this.$q = $q;
// Get all actions which are valid in this context; this.actionService = actionService;
// this simply redirects to the action service, this.domainObject = domainObject;
// but additionally adds a domainObject field.
function getActions(context) {
var baseContext = typeof context === 'string' ?
{ key: context } :
(context || {}),
actionContext = Object.create(baseContext);
actionContext.domainObject = domainObject;
return actionService.getActions(actionContext);
} }
// Alias to getActions(context)[0].perform, with a
// check for empty arrays.
function performAction(context) {
var actions = getActions(context);
return $q.when(
(actions && actions.length > 0) ?
actions[0].perform() :
undefined
);
}
return {
/** /**
* Perform an action. This will find and perform the * Perform an action. This will find and perform the
* first matching action available for the specified * first matching action available for the specified
@ -95,7 +72,19 @@ define(
* was found. * was found.
* @memberof platform/core.ActionCapability# * @memberof platform/core.ActionCapability#
*/ */
perform: performAction, ActionCapability.prototype.getActions = function (context) {
// Get all actions which are valid in this context;
// this simply redirects to the action service,
// but additionally adds a domainObject field.
var baseContext = typeof context === 'string' ?
{ key: context } : (context || {}),
actionContext = Object.create(baseContext);
actionContext.domainObject = this.domainObject;
return this.actionService.getActions(actionContext);
};
/** /**
* Get actions which are available for this domain object, * Get actions which are available for this domain object,
* in this context. * in this context.
@ -111,9 +100,18 @@ define(
* @returns {Action[]} an array of matching actions * @returns {Action[]} an array of matching actions
* @memberof platform/core.ActionCapability# * @memberof platform/core.ActionCapability#
*/ */
getActions: getActions ActionCapability.prototype.perform = function (context) {
// Alias to getActions(context)[0].perform, with a
// check for empty arrays.
var actions = this.getActions(context);
return this.$q.when(
(actions && actions.length > 0) ?
actions[0].perform() :
undefined
);
}; };
}
return ActionCapability; return ActionCapability;
} }

View File

@ -36,11 +36,45 @@ define(
* category of extension.) * category of extension.)
* *
* @memberof platform/core * @memberof platform/core
* @imeplements {ActionService}
* @constructor * @constructor
*/ */
function ActionProvider(actions) { function ActionProvider(actions) {
var actionsByKey = {}, var self = this;
actionsByCategory = {};
// Build up look-up tables
this.actions = actions;
this.actionsByKey = {};
this.actionsByCategory = {};
actions.forEach(function (Action) {
// Get an action's category or categories
var categories = Action.category || [];
// Convert to an array if necessary
categories = Array.isArray(categories) ?
categories : [categories];
// Store action under all relevant categories
categories.forEach(function (category) {
self.actionsByCategory[category] =
self.actionsByCategory[category] || [];
self.actionsByCategory[category].push(Action);
});
// Store action by ekey as well
if (Action.key) {
self.actionsByKey[Action.key] =
self.actionsByKey[Action.key] || [];
self.actionsByKey[Action.key].push(Action);
}
});
}
ActionProvider.prototype.getActions = function (actionContext) {
var context = (actionContext || {}),
category = context.category,
key = context.key,
candidates;
// Instantiate an action; invokes the constructor and // Instantiate an action; invokes the constructor and
// additionally fills in the action's getMetadata method // additionally fills in the action's getMetadata method
@ -77,80 +111,25 @@ define(
}); });
} }
// Get an array of actions that are valid in the supplied context.
function getActions(actionContext) {
var context = (actionContext || {}),
category = context.category,
key = context.key,
candidates;
// Match actions to the provided context by comparing "key" // Match actions to the provided context by comparing "key"
// and/or "category" parameters, if specified. // and/or "category" parameters, if specified.
candidates = actions; candidates = this.actions;
if (key) { if (key) {
candidates = actionsByKey[key]; candidates = this.actionsByKey[key];
if (category) { if (category) {
candidates = candidates.filter(function (Action) { candidates = candidates.filter(function (Action) {
return Action.category === category; return Action.category === category;
}); });
} }
} else if (category) { } else if (category) {
candidates = actionsByCategory[category]; candidates = this.actionsByCategory[category];
} }
// Instantiate those remaining actions, with additional // Instantiate those remaining actions, with additional
// filtering per any appliesTo methods defined on those // filtering per any appliesTo methods defined on those
// actions. // actions.
return createIfApplicable(candidates, context); return createIfApplicable(candidates, context);
}
// Build up look-up tables
actions.forEach(function (Action) {
// Get an action's category or categories
var categories = Action.category || [];
// Convert to an array if necessary
categories = Array.isArray(categories) ?
categories : [categories];
// Store action under all relevant categories
categories.forEach(function (category) {
actionsByCategory[category] =
actionsByCategory[category] || [];
actionsByCategory[category].push(Action);
});
// Store action by ekey as well
if (Action.key) {
actionsByKey[Action.key] =
actionsByKey[Action.key] || [];
actionsByKey[Action.key].push(Action);
}
});
return {
/**
* Get a list of actions which are valid in a given
* context.
*
* @param {ActionContext} the context in which
* the action will occur; this is a
* JavaScript object containing key-value
* pairs. Typically, this will contain a
* field "domainObject" which refers to
* the domain object that will be acted
* upon, but may contain arbitrary information
* recognized by specific providers.
* @return {Action[]} an array of actions which
* may be performed in the provided context.
*
* @method
* @memberof ActionProvider
* @memberof platform/core.ActionProvider#
*/
getActions: getActions
}; };
}
return ActionProvider; return ActionProvider;
} }

View File

@ -36,8 +36,19 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {ActionService}
* @param $log Angular's logging service
* @param {ActionService} actionService the decorated action service
*/ */
function LoggingActionDecorator($log, actionService) { function LoggingActionDecorator($log, actionService) {
this.$log = $log;
this.actionService = actionService;
}
LoggingActionDecorator.prototype.getActions = function () {
var actionService = this.actionService,
$log = this.$log;
// Decorate the perform method of the specified action, such that // Decorate the perform method of the specified action, such that
// it emits a log message whenever performed. // it emits a log message whenever performed.
function addLogging(action) { function addLogging(action) {
@ -59,35 +70,11 @@ define(
return logAction; return logAction;
} }
return {
/**
* Get a list of actions which are valid in a given
* context. These actions will additionally log
* themselves when performed.
*
* @param {ActionContext} the context in which
* the action will occur; this is a
* JavaScript object containing key-value
* pairs. Typically, this will contain a
* field "domainObject" which refers to
* the domain object that will be acted
* upon, but may contain arbitrary information
* recognized by specific providers.
* @return {Action[]} an array of actions which
* may be performed in the provided context.
*
* @method
* @memberof LoggingActionDecorator
* @memberof platform/core.LoggingActionDecorator#
*/
getActions: function () {
return actionService.getActions.apply( return actionService.getActions.apply(
actionService, actionService,
arguments arguments
).map(addLogging); ).map(addLogging);
}
}; };
}
return LoggingActionDecorator; return LoggingActionDecorator;
} }

View File

@ -39,28 +39,25 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {Capability}
*/ */
function CompositionCapability($injector, domainObject) { function CompositionCapability($injector, domainObject) {
var objectService,
lastPromise,
lastModified;
// Get a reference to the object service from $injector // Get a reference to the object service from $injector
function injectObjectService() { this.injectObjectService = function () {
objectService = $injector.get("objectService"); this.objectService = $injector.get("objectService");
return objectService; };
this.domainObject = domainObject;
} }
// Get a reference to the object service (either cached or /**
// from the injector) * Request the composition of this object.
function getObjectService() { * @returns {Promise.<DomainObject[]>} a list of all domain
return objectService || injectObjectService(); * objects which compose this domain object.
} */
CompositionCapability.prototype.invoke = function () {
// Promise this domain object's composition (an array of domain var domainObject = this.domainObject,
// object instances corresponding to ids in its model.) model = domainObject.getModel(),
function promiseComposition() {
var model = domainObject.getModel(),
ids; ids;
// Then filter out non-existent objects, // Then filter out non-existent objects,
@ -77,29 +74,23 @@ define(
}); });
} }
// 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 // Make a new request if we haven't made one, or if the
// object has been modified. // object has been modified.
if (!lastPromise || lastModified !== model.modified) { if (!this.lastPromise || this.lastModified !== model.modified) {
ids = model.composition || []; ids = model.composition || [];
lastModified = model.modified; this.lastModified = model.modified;
// Load from the underlying object service // Load from the underlying object service
lastPromise = getObjectService().getObjects(ids) this.lastPromise = this.objectService.getObjects(ids)
.then(contextualize); .then(contextualize);
} }
return lastPromise; return this.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
}; };
}
/** /**
* Test to determine whether or not this capability should be exposed * Test to determine whether or not this capability should be exposed

View File

@ -38,9 +38,13 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {Capability}
*/ */
function ContextCapability(parentObject, domainObject) { function ContextCapability(parentObject, domainObject) {
return { this.parentObject = parentObject;
this.domainObject = domainObject;
}
/** /**
* Get the immediate parent of a domain object. * Get the immediate parent of a domain object.
* *
@ -51,11 +55,11 @@ define(
* *
* @returns {DomainObject} the immediate parent of this * @returns {DomainObject} the immediate parent of this
* domain object. * domain object.
* @memberof platform/core.ContextCapability#
*/ */
getParent: function () { ContextCapability.prototype.getParent = function () {
return parentObject; return this.parentObject;
}, };
/** /**
* Get an array containing the complete direct ancestry * Get an array containing the complete direct ancestry
* of this domain object, including the domain object * of this domain object, including the domain object
@ -74,21 +78,17 @@ define(
* @returns {DomainObject[]} the full composition ancestry * @returns {DomainObject[]} the full composition ancestry
* of the domain object which exposed this * of the domain object which exposed this
* capability. * capability.
* @memberof platform/core.ContextCapability#
*/ */
getPath: function () { ContextCapability.prototype.getPath = function () {
var parentPath = [], var parentObject = this.parentObject,
parentContext; parentContext =
parentObject && parentObject.getCapability('context'),
if (parentObject) {
parentContext = parentObject.getCapability("context");
parentPath = parentContext ? parentPath = parentContext ?
parentContext.getPath() : parentContext.getPath() : [ this.parentObject ];
[parentObject];
} return parentPath.concat([this.domainObject]);
};
return parentPath.concat([domainObject]);
},
/** /**
* Get the deepest ancestor available for this domain object; * Get the deepest ancestor available for this domain object;
* equivalent to `getPath()[0]`. * equivalent to `getPath()[0]`.
@ -98,18 +98,15 @@ define(
* *
* @returns {DomainObject} the deepest ancestor of the domain * @returns {DomainObject} the deepest ancestor of the domain
* object which exposed this capability. * object which exposed this capability.
* @memberof platform/core.ContextCapability#
*/ */
getRoot: function () { ContextCapability.prototype.getRoot = function () {
var parentContext = parentObject && var parentContext = this.parentObject &&
parentObject.getCapability('context'); this.parentObject.getCapability('context');
return parentContext ? return parentContext ?
parentContext.getRoot() : parentContext.getRoot() :
(parentObject || domainObject); (this.parentObject || this.domainObject);
}
}; };
}
return ContextCapability; return ContextCapability;
} }

View File

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

View File

@ -29,6 +29,19 @@ define(
function () { function () {
"use strict"; "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, * Provides capabilities based on extension definitions,
* matched to domain object models. * matched to domain object models.

View File

@ -45,13 +45,40 @@ define(
* in the type's definition, which contains an array of names of * in the type's definition, which contains an array of names of
* capabilities to be delegated. * capabilities to be delegated.
* *
* @param domainObject * @param $q Angular's $q, for promises
* @param {DomainObject} domainObject the delegating domain object
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {Capability}
*/ */
function DelegationCapability($q, domainObject) { function DelegationCapability($q, domainObject) {
var delegateCapabilities = {}, var type = domainObject.getCapability("type"),
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) { function filterObjectsWithCapability(capability) {
return function (objects) { return function (objects) {
@ -65,26 +92,24 @@ define(
return domainObject.useCapability('composition'); return domainObject.useCapability('composition');
} }
function doesDelegate(key) { return this.doesDelegateCapability(key) ?
return delegateCapabilities[key] || false;
}
function getDelegates(capability) {
return doesDelegate(capability) ?
promiseChildren().then( promiseChildren().then(
filterObjectsWithCapability(capability) filterObjectsWithCapability(key)
) : ) :
$q.when([]); this.$q.when([]);
} };
// Generate set for easy lookup of capability delegation /**
if (type && type.getDefinition) { * Check if the domain object which exposed this capability
(type.getDefinition().delegates || []).forEach(function (key) { * wishes to delegate another capability.
delegateCapabilities[key] = true; *
}); * @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]);
};
return {
/** /**
* Invoke this capability; alias of `getDelegates`, used to * Invoke this capability; alias of `getDelegates`, used to
* simplify usage, e.g.: * simplify usage, e.g.:
@ -99,21 +124,8 @@ define(
* responsibility for this capability is delegated. * responsibility for this capability is delegated.
* @memberof platform/core.DelegationCapability# * @memberof platform/core.DelegationCapability#
*/ */
invoke: getDelegates, DelegationCapability.prototype.invoke =
/** DelegationCapability.prototype.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
};
}
return DelegationCapability; return DelegationCapability;

View File

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

View File

@ -69,18 +69,54 @@ 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 * @param {DomainObject} domainObject the domain object
* which will expose this capability * which will expose this capability
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {Capability}
*/ */
function MutationCapability(topic, now, domainObject) { 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) { /**
* 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 // Get the object's model and clone it, so the
// mutator function has a temporary copy to work with. // mutator function has a temporary copy to work with.
var model = domainObject.getModel(), var domainObject = this.domainObject,
now = this.now,
t = this.mutationTopic,
model = domainObject.getModel(),
clone = JSON.parse(JSON.stringify(model)), clone = JSON.parse(JSON.stringify(model)),
useTimestamp = arguments.length > 1; useTimestamp = arguments.length > 1;
@ -110,47 +146,8 @@ define(
// Invoke the provided mutator, then make changes to // Invoke the provided mutator, then make changes to
// the underlying model (if applicable.) // the underlying model (if applicable.)
return fastPromise(mutator(clone)).then(handleMutation); return fastPromise(mutator(clone)).then(handleMutation);
} };
function listen(listener) {
return t.listen(listener);
}
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. * Listen for mutations of this domain object's model.
* The provided listener will be invoked with the domain * The provided listener will be invoked with the domain
@ -160,9 +157,15 @@ define(
* @returns {Function} a function to stop listening * @returns {Function} a function to stop listening
* @memberof platform/core.MutationCapability# * @memberof platform/core.MutationCapability#
*/ */
listen: listen 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; return MutationCapability;
} }

View File

@ -33,7 +33,7 @@ define(
* *
* @param {PersistenceService} persistenceService the underlying * @param {PersistenceService} persistenceService the underlying
* provider of persistence capabilities. * 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 * use (this is an arbitrary string, useful in principle
* for distinguishing different persistence stores from * for distinguishing different persistence stores from
* one another.) * one another.)
@ -42,26 +42,15 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {Capability}
*/ */
function PersistenceCapability(persistenceService, SPACE, domainObject) { function PersistenceCapability(persistenceService, space, domainObject) {
// Cache modified timestamp // Cache modified timestamp
var modified = domainObject.getModel().modified; this.modified = domainObject.getModel().modified;
// Update a domain object's model upon refresh this.domainObject = domainObject;
function updateModel(model) { this.space = space;
var modified = model.modified; this.persistenceService = persistenceService;
return domainObject.useCapability("mutation", function () {
return model;
}, 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);
} }
// Utility function for creating promise-like objects which // Utility function for creating promise-like objects which
@ -74,40 +63,57 @@ define(
}; };
} }
return {
/** /**
* Persist any changes which have been made to this * Persist any changes which have been made to this
* domain object's model. * domain object's model.
* @returns {Promise} a promise which will be resolved * @returns {Promise} a promise which will be resolved
* if persistence is successful, and rejected * if persistence is successful, and rejected
* if not. * if not.
* @memberof platform/core.PersistenceCapability#
*/ */
persist: function () { PersistenceCapability.prototype.persist = function () {
updatePersistenceTimestamp(); var domainObject = this.domainObject,
return persistenceService.updateObject( modified = domainObject.getModel().modified;
SPACE,
// Update persistence timestamp...
domainObject.useCapability("mutation", function (model) {
model.persisted = modified;
}, modified);
// ...and persist
return this.persistenceService.updateObject(
this.getSpace(),
domainObject.getId(), domainObject.getId(),
domainObject.getModel() domainObject.getModel()
); );
}, };
/** /**
* Update this domain object to match the latest from * Update this domain object to match the latest from
* persistence. * persistence.
* @returns {Promise} a promise which will be resolved * @returns {Promise} a promise which will be resolved
* when the update is complete * when the update is complete
* @memberof platform/core.PersistenceCapability#
*/ */
refresh: function () { PersistenceCapability.prototype.refresh = function () {
var model = domainObject.getModel(); var domainObject = this.domainObject,
model = domainObject.getModel();
// Update a domain object's model upon refresh
function updateModel(model) {
var modified = model.modified;
return domainObject.useCapability("mutation", function () {
return model;
}, modified);
}
// Only update if we don't have unsaved changes // Only update if we don't have unsaved changes
return (model.modified === model.persisted) ? return (model.modified === model.persisted) ?
persistenceService.readObject( this.persistenceService.readObject(
SPACE, this.getSpace(),
domainObject.getId() this.domainObject.getId()
).then(updateModel) : ).then(updateModel) :
fastPromise(false); fastPromise(false);
}, };
/** /**
* Get the space in which this domain object is persisted; * Get the space in which this domain object is persisted;
* this is useful when, for example, decided which space a * this is useful when, for example, decided which space a
@ -117,13 +123,10 @@ define(
* *
* @returns {string} the name of the space which should * @returns {string} the name of the space which should
* be used to persist this object * be used to persist this object
* @memberof platform/core.PersistenceCapability#
*/ */
getSpace: function () { PersistenceCapability.prototype.getSpace = function () {
return SPACE; return this.space;
}
}; };
}
return PersistenceCapability; return PersistenceCapability;
} }

View File

@ -40,28 +40,46 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {Capability}
*/ */
function RelationshipCapability($injector, domainObject) { function RelationshipCapability($injector, domainObject) {
var objectService,
lastPromise = {},
lastModified;
// Get a reference to the object service from $injector // Get a reference to the object service from $injector
function injectObjectService() { this.injectObjectService = function () {
objectService = $injector.get("objectService"); this.objectService = $injector.get("objectService");
return objectService; };
this.lastPromise = {};
this.domainObject = domainObject;
} }
// Get a reference to the object service (either cached or /**
// from the injector) * List all types of relationships exposed by this
function getObjectService() { * object.
return objectService || injectObjectService(); * @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]);
} }
// Promise this domain object's composition (an array of domain return Object.keys(relationships).filter(isArray).sort();
// object instances corresponding to ids in its model.) };
function promiseRelationships(key) {
var model = domainObject.getModel(), /**
* 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; ids;
// Package objects as an array // Package objects as an array
@ -74,57 +92,27 @@ define(
} }
// Clear cached promises if modification has occurred // Clear cached promises if modification has occurred
if (lastModified !== model.modified) { if (this.lastModified !== model.modified) {
lastPromise = {}; this.lastPromise = {};
lastModified = model.modified; this.lastModified = model.modified;
} }
// Make a new request if needed // Make a new request if needed
if (!lastPromise[key]) { if (!this.lastPromise[key]) {
ids = (model.relationships || {})[key] || []; ids = (model.relationships || {})[key] || [];
lastModified = model.modified; this.lastModified = model.modified;
// Lazily initialize object service now that we need it
if (!this.objectService) {
this.injectObjectService();
}
// Load from the underlying object service // Load from the underlying object service
lastPromise[key] = getObjectService().getObjects(ids) this.lastPromise[key] = this.objectService.getObjects(ids)
.then(packageObject); .then(packageObject);
} }
return lastPromise[key]; return this.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
}; };
}
/** /**
* Test to determine whether or not this capability should be exposed * Test to determine whether or not this capability should be exposed

View File

@ -32,10 +32,30 @@ define(
* object are not provided. * object are not provided.
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @param {ModelService} modelService this service to decorate
* @implements {ModelService}
*/ */
function CachingModelDecorator(modelService) { function CachingModelDecorator(modelService) {
var cache = {}, this.cache = {};
cached = {}; this.cached = {};
this.modelService = modelService;
}
// Fast-resolving promise
function fastPromise(value) {
return (value || {}).then ? value : {
then: function (callback) {
return fastPromise(callback(value));
}
};
}
CachingModelDecorator.prototype.getModels = function (ids) {
var cache = this.cache,
cached = this.cached,
neededIds = ids.filter(function notCached(id) {
return !cached[id];
});
// Update the cached instance of a model to a new value. // Update the cached instance of a model to a new value.
// We update in-place to ensure there is only ever one instance // We update in-place to ensure there is only ever one instance
@ -68,30 +88,12 @@ define(
return oldModel; return oldModel;
} }
// Fast-resolving promise
function fastPromise(value) {
return (value || {}).then ? value : {
then: function (callback) {
return fastPromise(callback(value));
}
};
}
// Store this model in the cache
function cacheModel(id, model) {
cache[id] = cached[id] ? updateModel(id, model) : model;
cached[id] = true;
}
// Check if an id is not in cache, for lookup filtering
function notCached(id) {
return !cached[id];
}
// Store the provided models in our cache // Store the provided models in our cache
function cacheAll(models) { function cacheAll(models) {
Object.keys(models).forEach(function (id) { Object.keys(models).forEach(function (id) {
cacheModel(id, models[id]); cache[id] = cached[id] ?
updateModel(id, models[id]) : models[id];
cached[id] = true;
}); });
} }
@ -100,38 +102,16 @@ define(
return cache; return cache;
} }
return {
/**
* Get models for these specified string identifiers.
* These will be given as an object containing keys
* and values, where keys are object identifiers and
* values are models.
* This result may contain either a subset or a
* superset of the total objects.
*
* @param {Array<string>} ids the string identifiers for
* models of interest.
* @returns {Promise<object>} a promise for an object
* containing key-value pairs, where keys are
* ids and values are models
* @method
* @memberof platform/core.CachingModelDecorator#
*/
getModels: function (ids) {
var neededIds = ids.filter(notCached);
// Look up if we have unknown IDs // Look up if we have unknown IDs
if (neededIds.length > 0) { if (neededIds.length > 0) {
return modelService.getModels(neededIds) return this.modelService.getModels(neededIds)
.then(cacheAll) .then(cacheAll)
.then(giveCache); .then(giveCache);
} }
// Otherwise, just expose the cache directly // Otherwise, just expose the cache directly
return fastPromise(cache); return fastPromise(cache);
}
}; };
}
return CachingModelDecorator; return CachingModelDecorator;
} }

View File

@ -29,11 +29,15 @@ define(
/** /**
* Adds placeholder domain object models for any models which * Adds placeholder domain object models for any models which
* fail to load from the underlying model service. * fail to load from the underlying model service.
* @implements {ModelService}
* @constructor * @constructor
* @memberof platform/core * @memberof platform/core
* @param {ModelService} modelService this service to decorate
* @implements {ModelService}
*/ */
function MissingModelDecorator(modelService) { function MissingModelDecorator(modelService) {
this.modelService = modelService;
}
function missingModel(id) { function missingModel(id) {
return { return {
type: "unknown", type: "unknown",
@ -41,8 +45,7 @@ define(
}; };
} }
return { MissingModelDecorator.prototype.getModels = function (ids) {
getModels: function (ids) {
function addMissingModels(models) { function addMissingModels(models) {
var result = {}; var result = {};
ids.forEach(function (id) { ids.forEach(function (id) {
@ -51,10 +54,8 @@ define(
return result; return result;
} }
return modelService.getModels(ids).then(addMissingModels); return this.modelService.getModels(ids).then(addMissingModels);
}
}; };
}
return MissingModelDecorator; return MissingModelDecorator;
} }

View File

@ -29,16 +29,40 @@ define(
function () { function () {
"use strict"; "use strict";
/**
* Allow domain object models to be looked up by their identifiers.
*
* @interface ModelService
*/
/**
* Get domain object models.
*
* This may provide either a superset or a subset of the models
* requested. Absence of a model means it does not exist within
* this service instance.
*
* @method ModelService#getModels
* @param {string[]} ids identifiers for models desired.
* @returns {Promise.<Object>} a promise for an object mapping
* string identifiers to domain object models.
*/
/** /**
* Allows multiple services which provide models for domain objects * Allows multiple services which provide models for domain objects
* to be treated as one. * to be treated as one.
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @param {ModelProvider[]} providers the model providers to be * @implements {ModelService}
* @param $q Angular's $q, for promises
* @param {ModelService[]} providers the model providers to be
* aggregated * aggregated
*/ */
function ModelAggregator($q, providers) { function ModelAggregator($q, providers) {
this.providers = providers;
this.$q = $q;
}
// Pick a domain object model to use, favoring the one // Pick a domain object model to use, favoring the one
// with the most recent timestamp // with the most recent timestamp
@ -62,33 +86,13 @@ define(
return result; return result;
} }
return { ModelAggregator.prototype.getModels = function (ids) {
/** return this.$q.all(this.providers.map(function (provider) {
* Get models with the specified identifiers.
*
* This will invoke the `getModels()` method of all providers
* given at constructor-time, and aggregate the result into
* one object.
*
* Note that the returned object may contain a subset or a
* superset of the models requested.
*
* @param {string[]} ids an array of domain object identifiers
* @returns {Promise.<object>} a promise for an object
* containing key-value pairs,
* where keys are object identifiers and values
* are object models.
* @memberof platform/core.ModelAggregator#
*/
getModels: function (ids) {
return $q.all(providers.map(function (provider) {
return provider.getModels(ids); return provider.getModels(ids);
})).then(function (provided) { })).then(function (provided) {
return mergeModels(provided, ids); return mergeModels(provided, ids);
}); });
}
}; };
}
return ModelAggregator; return ModelAggregator;
} }

View File

@ -35,20 +35,29 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {ModelService}
* @param {PersistenceService} persistenceService the service in which * @param {PersistenceService} persistenceService the service in which
* domain object models are persisted. * domain object models are persisted.
* @param $q Angular's $q service, for working with promises * @param $q Angular's $q service, for working with promises
* @param {string} SPACE the name of the persistence space from which * @param {string} SPACE the name of the persistence space from which
* models should be retrieved. * models should be retrieved.
*/ */
function PersistedModelProvider(persistenceService, $q, SPACE) { function PersistedModelProvider(persistenceService, $q, space) {
// Load a single object model from persistence this.persistenceService = persistenceService;
function loadModel(id) { this.$q = $q;
return persistenceService.readObject(SPACE, id); this.space = space;
}
PersistedModelProvider.prototype.getModels = function (ids) {
var persistenceService = this.persistenceService,
$q = this.$q,
space = this.space;
// Load a single object model from persistence
function loadModel(id) {
return persistenceService.readObject(space, id);
} }
// Promise all persisted models (in id->model form)
function promiseModels(ids) {
// Package the result as id->model // Package the result as id->model
function packageResult(models) { function packageResult(models) {
var result = {}; var result = {};
@ -66,30 +75,7 @@ define(
// Give a promise for all persistence lookups... // Give a promise for all persistence lookups...
return $q.all(ids.map(loadModel)).then(packageResult); return $q.all(ids.map(loadModel)).then(packageResult);
}
return {
/**
* Get models with the specified identifiers.
*
* This will invoke the underlying persistence service to
* retrieve object models which match the provided
* identifiers.
*
* Note that the returned object may contain a subset or a
* superset of the models requested.
*
* @param {string[]} ids an array of domain object identifiers
* @returns {Promise.<object>} a promise for an object
* containing key-value pairs,
* where keys are object identifiers and values
* are object models.
* @memberof platform/core.PersistedModelProvider#
*/
getModels: promiseModels
}; };
}
return PersistedModelProvider; return PersistedModelProvider;
} }

View File

@ -41,41 +41,30 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @implements {ModelService}
* @param {Array} roots all `roots[]` extensions
* @param $q Angular's $q, for promises
* @param $log Anuglar's $log, for logging
*/ */
function RootModelProvider(roots, $q, $log) { function RootModelProvider(roots, $q, $log) {
// Pull out identifiers to used as ROOT's // Pull out identifiers to used as ROOT's
var ids = roots.map(function (root) { return root.id; }), var ids = roots.map(function (root) { return root.id; });
baseProvider = new StaticModelProvider(roots, $q, $log);
function addRoot(models) { this.baseProvider = new StaticModelProvider(roots, $q, $log);
models.ROOT = { this.rootModel = {
name: "The root object", name: "The root object",
type: "root", type: "root",
composition: ids composition: ids
}; };
}
RootModelProvider.prototype.getModels = function (ids) {
var rootModel = this.rootModel;
return this.baseProvider.getModels(ids).then(function (models) {
models.ROOT = rootModel;
return models; return models;
} });
return {
/**
* Get models with the specified identifiers.
*
* Note that the returned object may contain a subset or a
* superset of the models requested.
*
* @param {string[]} ids an array of domain object identifiers
* @returns {Promise.<object>} a promise for an object
* containing key-value pairs,
* where keys are object identifiers and values
* are object models.
* @memberof platform/core.RootModelProvider#
*/
getModels: function (ids) {
return baseProvider.getModels(ids).then(addRoot);
}
}; };
}
return RootModelProvider; return RootModelProvider;
} }

View File

@ -54,33 +54,18 @@ define(
// Prepoulate maps with models to make subsequent lookup faster. // Prepoulate maps with models to make subsequent lookup faster.
models.forEach(addModelToMap); models.forEach(addModelToMap);
return { this.modelMap = modelMap;
/** this.$q = $q;
* Get models for these specified string identifiers. }
* These will be given as an object containing keys
* and values, where keys are object identifiers and StaticModelProvider.prototype.getModels = function (ids) {
* values are models. var modelMap = this.modelMap,
* This result may contain either a subset or a result = {};
* superset of the total objects.
*
* @param {Array<string>} ids the string identifiers for
* models of interest.
* @returns {Promise<object>} a promise for an object
* containing key-value pairs, where keys are
* ids and values are models
* @method
* @memberof StaticModelProvider#
* @memberof platform/core.StaticModelProvider#
*/
getModels: function (ids) {
var result = {};
ids.forEach(function (id) { ids.forEach(function (id) {
result[id] = modelMap[id]; result[id] = modelMap[id];
}); });
return $q.when(result); return this.$q.when(result);
}
}; };
}
return StaticModelProvider; return StaticModelProvider;
} }

View File

@ -1,134 +0,0 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining DomainObject. Created by vwoeltje on 11/7/14.
*/
define(
[],
function () {
"use strict";
/**
* Construct a new domain object with the specified
* identifier, model, and capabilities.
*
* @param {string} id the object's unique identifier
* @param {object} model the "JSONifiable" state of the object
* @param {Object.<string, Capability>|function} capabilities all
* capabilities to be exposed by this object
* @memberof platform/core
* @constructor
*/
function DomainObject(id, model, capabilities) {
return {
/**
* Get the unique identifier for this domain object.
* @return {string} the domain object's unique identifier
* @memberof DomainObject#
* @memberof platform/core.DomainObject#
*/
getId: function () {
return id;
},
/**
* Get the domain object's model. This is useful to
* directly look up known properties of an object, but
* direct modification of a returned model is generally
* discouraged and may result in errors. Instead, an
* object's "mutation" capability should be used.
*
* @return {object} the domain object's persistent state
* @memberof DomainObject#
* @memberof platform/core.DomainObject#
*/
getModel: function () {
return model;
},
/**
* Get a capability associated with this object.
* Capabilities are looked up by string identifiers;
* prior knowledge of a capability's interface is
* necessary.
*
* @return {Capability} the named capability, or undefined
* if not present.
* @memberof DomainObject#
* @memberof platform/core.DomainObject#
*/
getCapability: function (name) {
var capability = capabilities[name];
return typeof capability === 'function' ?
capability(this) : capability;
},
/**g
* Check if this domain object supports a capability
* with the provided name.
*
* @param {string} name the name of the capability to
* check for
* @returns {boolean} true if provided
* @memberof platform/core.DomainObject#
*/
hasCapability: function hasCapability(name) {
return this.getCapability(name) !== undefined;
},
/**
* Use a capability of an object; this is a shorthand
* for:
*
* ```
* hasCapability(name) ?
* getCapability(name).invoke(args...) :
* undefined
* ```
*
* That is, it handles both the check-for-existence and
* invocation of the capability, and checks for existence
* before invoking the capability.
*
* @param {string} name the name of the capability to invoke
* @param {...*} [arguments] to pass to the invocation
* @returns {*}
* @memberof DomainObject#
* @memberof platform/core.DomainObject#
*/
useCapability: function (name) {
// Get tail of args to pass to invoke
var args = Array.prototype.slice.apply(arguments, [1]),
capability = this.getCapability(name);
return (capability && capability.invoke) ?
capability.invoke.apply(capability, args) :
capability;
}
};
}
return DomainObject;
}
);

View File

@ -0,0 +1,143 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/**
* Module defining DomainObject. Created by vwoeltje on 11/7/14.
*/
define(
[],
function () {
"use strict";
/**
* A domain object is an entity of interest to the user.
*
* @interface DomainObject
*/
/**
* Get the unique identifier for this domain object.
*
* @method DomainObject#getId
* @return {string} the domain object's unique identifier
*/
/**
* Get the domain object's model. This is useful to
* directly look up known properties of an object, but
* direct modification of a returned model is generally
* discouraged and may result in errors. Instead, an
* object's `mutation` capability should be used.
*
* @method DomainObject#getModel
* @return {object} the domain object's persistent state
*/
/**
* Get a capability associated with this object.
* Capabilities are looked up by string identifiers;
* prior knowledge of a capability's interface is
* necessary.
*
* @method DomainObject#getCapability
* @param {string} key the identifier for the capability
* @return {Capability} the named capability, or undefined
* if not present.
*/
/**
* Check if this domain object supports a capability
* with the provided name.
*
* @method DomainObject#hasCapability
* @param {string} key the identifier for the capability
* @return {boolean} true if this domain object has this capability
*/
/**
* Use a capability of an object; the behavior of this method
* depends on the interface of the capability, and whether
* or not it is present.
*
* * If the capability is not present for this object,
* no operation occurs.
* * If the capability is present and has an `invoke` method,
* that method is called with any additional arguments
* provided, and its return value is returned.
* * If the capability is present but has no `invoke` method,
* this capability itself is returned.
*
* @method DomainObject#useCapability
* @param {string} name the name of the capability to invoke
* @param {...*} [arguments] to pass to the invocation
* @returns {*|Capability} the result of invocation (see description)
*/
/**
* Construct a new domain object with the specified
* identifier, model, and capabilities.
*
* @param {string} id the object's unique identifier
* @param {object} model the "JSONifiable" state of the object
* @param {Object.<string, Capability>|function} capabilities all
* capabilities to be exposed by this object
* @memberof platform/core
* @constructor
*/
function DomainObjectImpl(id, model, capabilities) {
this.id = id;
this.model = model;
this.capabilities = capabilities;
}
DomainObjectImpl.prototype.getId = function () {
return this.id;
};
DomainObjectImpl.prototype.getModel = function () {
return this.model;
};
DomainObjectImpl.prototype.getCapability = function (name) {
var capability = this.capabilities[name];
return typeof capability === 'function' ?
capability(this) : capability;
};
DomainObjectImpl.prototype.hasCapability = function (name) {
return this.getCapability(name) !== undefined;
};
DomainObjectImpl.prototype.useCapability = function (name) {
// Get tail of args to pass to invoke
var args = Array.prototype.slice.apply(arguments, [1]),
capability = this.getCapability(name);
return (capability && capability.invoke) ?
capability.invoke.apply(capability, args) :
capability;
};
return DomainObjectImpl;
}
);

View File

@ -27,10 +27,31 @@
* @namespace platform/core * @namespace platform/core
*/ */
define( define(
["./DomainObject"], ["./DomainObjectImpl"],
function (DomainObject) { function (DomainObjectImpl) {
"use strict"; "use strict";
/**
* Provides instances of domain objects, as retrieved by their
* identifiers.
*
* @interface ObjectService
*/
/**
* Get a set of objects associated with a list of identifiers.
* The provided result may contain a subset or a superset of
* the total number of objects.
*
* @method ObjectService#getObjects
* @param {string[]} ids the identifiers for domain objects
* of interest.
* @return {Promise<object<string, DomainObject>>} a promise
* for an object containing key-value pairs, where keys
* are string identifiers for domain objects, and
* values are the corresponding domain objects themselves.
*/
/** /**
* Construct a new provider for domain objects. * Construct a new provider for domain objects.
* *
@ -44,6 +65,16 @@ define(
* @constructor * @constructor
*/ */
function DomainObjectProvider(modelService, capabilityService, $q) { function DomainObjectProvider(modelService, capabilityService, $q) {
this.modelService = modelService;
this.capabilityService = capabilityService;
this.$q = $q;
}
DomainObjectProvider.prototype.getObjects = function getObjects(ids) {
var modelService = this.modelService,
capabilityService = this.capabilityService,
$q = this.$q;
// Given a models object (containing key-value id-model pairs) // Given a models object (containing key-value id-model pairs)
// create a function that will look up from the capability // create a function that will look up from the capability
// service based on id; for handy mapping below. // service based on id; for handy mapping below.
@ -65,7 +96,7 @@ define(
ids.forEach(function (id, index) { ids.forEach(function (id, index) {
if (models[id]) { if (models[id]) {
// Create the domain object // Create the domain object
result[id] = new DomainObject( result[id] = new DomainObjectImpl(
id, id,
models[id], models[id],
capabilities[index] capabilities[index]
@ -75,9 +106,6 @@ define(
return result; return result;
} }
// Get object instances; this is the useful API exposed by the
// domain object provider.
function getObjects(ids) {
return modelService.getModels(ids).then(function (models) { return modelService.getModels(ids).then(function (models) {
return $q.all( return $q.all(
ids.map(capabilityResolver(models)) ids.map(capabilityResolver(models))
@ -85,26 +113,7 @@ define(
return assembleResult(ids, models, capabilities); return assembleResult(ids, models, capabilities);
}); });
}); });
}
return {
/**
* Get a set of objects associated with a list of identifiers.
* The provided result may contain a subset or a superset of
* the total number of objects.
*
* @param {Array<string>} ids the identifiers for domain objects
* of interest.
* @return {Promise<object<string, DomainObject>>} a promise
* for an object containing key-value pairs, where keys
* are string identifiers for domain objects, and
* values are the corresponding domain objects themselves.
* @memberof module:core/object/object-provider.ObjectProvider#
* @memberof platform/core.DomainObjectProvider#
*/
getObjects: getObjects
}; };
}
return DomainObjectProvider; return DomainObjectProvider;
} }

View File

@ -31,7 +31,6 @@ define(
* `Date.now()` which can be injected to support testability. * `Date.now()` which can be injected to support testability.
* *
* @returns {Function} a function which returns current system time * @returns {Function} a function which returns current system time
* @constructor
* @memberof platform/core * @memberof platform/core
*/ */
function Now() { function Now() {

View File

@ -42,7 +42,6 @@ define(
* resolve to the returned value of `fn` whenever that is invoked. * resolve to the returned value of `fn` whenever that is invoked.
* *
* @returns {Function} * @returns {Function}
* @constructor
* @memberof platform/core * @memberof platform/core
*/ */
function Throttle($timeout) { function Throttle($timeout) {

View File

@ -44,7 +44,6 @@ define(
* arguments) are private; each call returns a new instance. * arguments) are private; each call returns a new instance.
* *
* @returns {Function} * @returns {Function}
* @constructor
* @memberof platform/core * @memberof platform/core
*/ */
function Topic() { function Topic() {

View File

@ -36,6 +36,8 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @augments {Type}
* @implements {Capability}
* @param {TypeService} typeService the service which * @param {TypeService} typeService the service which
* provides type information * provides type information
* @param {DomainObject} domainObject the domain object * @param {DomainObject} domainObject the domain object

View File

@ -26,10 +26,95 @@ define(
function (TypeProperty) { function (TypeProperty) {
"use strict"; "use strict";
/**
* Describes a type of domain object.
*
* @interface Type
*/
/**
* Get the string key which identifies this type.
* This is the type's machine-readable name/identifier,
* and will correspond to the "type" field of the models
* of domain objects of this type.
*
* @returns {string} the key which identifies this type
* @method Type#getKey
*/
/**
* Get the human-readable name for this type, as should
* be displayed in the user interface when referencing
* this type.
*
* @returns {string} the human-readable name of this type
* @method Type#getName
*/
/**
* Get the human-readable description for this type, as should
* be displayed in the user interface when describing
* this type.
*
* @returns {string} the human-readable description of this type
* @method Type#getDescription
*/
/**
* Get the glyph associated with this type. Glyphs are
* single-character strings which will appear as icons (when
* displayed in an appropriate font) which visually
* distinguish types from one another.
*
* @returns {string} the glyph to be displayed
* @method Type#getGlyph
*/
/**
* Get an array of properties associated with objects of
* this type, as might be shown in a Create wizard or
* an Edit Properties view.
*
* @return {Array<TypeProperty[]} properties associated with
* objects of this type
* @method Type#getPropertiees
*/
/**
* Get the initial state of a model for domain objects of
* this type.
*
* @return {object} initial domain object model
* @method Type#getInitialModel
*/
/**
* Get the raw type definition for this type. This is an
* object containing key-value pairs of type metadata;
* this allows the retrieval and use of custom type
* properties which are not recognized within this interface.
*
* @returns {object} the raw definition for this type
* @method Type#getDefinition
*/
/**
* Check if this type is or inherits from some other type.
*
* @param {string|Type} key either
* a string key for a type, or an instance of a type
* object, which this
* @returns {boolean} true
* @method Type#instanceOf
*/
/**
* Check if a type should support a given feature. This simply
* checks for the presence or absence of the feature key in
* the type definition's "feature" field.
* @param {string} feature a string identifying the feature
* @returns {boolean} true if the feature is supported
* @method Type#hasFeature
*/
/** /**
* Construct a new type. Types describe categories of * Construct a new type. Types describe categories of
* domain objects. * domain objects.
* *
* @implements {Type}
* @param {TypeDefinition} typeDef an object containing * @param {TypeDefinition} typeDef an object containing
* key-value pairs describing a type and its * key-value pairs describing a type and its
* relationship to other types. * relationship to other types.
@ -44,106 +129,44 @@ define(
featureSet[feature] = true; featureSet[feature] = true;
}); });
return { this.typeDef = typeDef;
/** this.featureSet = featureSet;
* Get the string key which identifies this type. this.inheritList = inheritList;
* This is the type's machine-readable name/identifier, }
* and will correspond to the "type" field of the models
* of domain objects of this type. TypeImpl.prototype.getKey = function () {
* return this.typeDef.key;
* @returns {string} the key which identifies this type };
* @memberof module:core/type/type-impl.TypeImpl#
* @memberof platform/core.TypeImpl# TypeImpl.prototype.getName = function () {
*/ return this.typeDef.name;
getKey: function () { };
return typeDef.key;
}, TypeImpl.prototype.getDescription = function () {
/** return this.typeDef.description;
* Get the human-readable name for this type, as should };
* be displayed in the user interface when referencing
* this type. TypeImpl.prototype.getGlyph = function () {
* return this.typeDef.glyph;
* @returns {string} the human-readable name of this type };
* @memberof module:core/type/type-impl.TypeImpl#
* @memberof platform/core.TypeImpl# TypeImpl.prototype.getProperties = function () {
*/ return (this.typeDef.properties || []).map(function (propertyDef) {
getName: function () { return new TypeProperty(propertyDef);
return typeDef.name; });
}, };
/**
* Get the human-readable description for this type, as should TypeImpl.prototype.getInitialModel = function () {
* be displayed in the user interface when describing return this.typeDef.model || {};
* this type. };
*
* @returns {string} the human-readable description of this type TypeImpl.prototype.getDefinition = function () {
* @memberof module:core/type/type-impl.TypeImpl# return this.typeDef;
* @memberof platform/core.TypeImpl# };
*/
getDescription: function () { TypeImpl.prototype.instanceOf = function instanceOf(key) {
return typeDef.description; var typeDef = this.typeDef,
}, inheritList = this.inheritList;
/**
* Get the glyph associated with this type. Glyphs are
* single-character strings which will appear as icons (when
* displayed in an appropriate font) which visually
* distinguish types from one another.
*
* @returns {string} the glyph to be displayed
* @memberof module:core/type/type-impl.TypeImpl#
* @memberof platform/core.TypeImpl#
*/
getGlyph: function () {
return typeDef.glyph;
},
/**
* Get an array of properties associated with objects of
* this type, as might be shown in a Create wizard or
* an Edit Properties view.
*
* @return {Array<TypeProperty>} properties associated with
* objects of this type
* @memberof platform/core.TypeImpl#
*/
getProperties: function () {
return (typeDef.properties || []).map(TypeProperty);
},
/**
* Get the initial state of a model for domain objects of
* this type.
*
* @return {object} initial domain object model
* @memberof platform/core.TypeImpl#
*/
getInitialModel: function () {
return typeDef.model || {};
},
/**
* Get the raw type definition for this type. This is an
* object containing key-value pairs of type metadata;
* this allows the retrieval and use of custom type
* properties which are not recognized within this interface.
*
* @returns {object} the raw definition for this type
* @memberof module:core/type/type-impl.TypeImpl#
* @memberof platform/core.TypeImpl#
*/
getDefinition: function () {
return typeDef;
},
/**
* Check if this type is or inherits from some other type.
*
* TODO: Rename, "instanceOf" is a misnomer (since there is
* no "instance", so to speak.)
*
* @param {string|module:core/type/type-implTypeImpl} key either
* a string key for a type, or an instance of a type
* object, which this
* @returns {boolean} true
* @memberof module:core/type/type-impl.TypeImpl#
* @memberof platform/core.TypeImpl#
*/
instanceOf: function instanceOf(key) {
if (key === typeDef.key) { if (key === typeDef.key) {
return true; return true;
@ -152,24 +175,15 @@ define(
} else if (!key) { } else if (!key) {
return true; return true;
} else if (key !== null && typeof key === 'object') { } else if (key !== null && typeof key === 'object') {
return key.getKey ? instanceOf(key.getKey()) : false; return key.getKey ? this.instanceOf(key.getKey()) : false;
} else { } else {
return false; return false;
} }
},
/**
* Check if a type should support a given feature. This simply
* checks for the presence or absence of the feature key in
* the type definition's "feature" field.
* @param {string} feature a string identifying the feature
* @returns {boolean} true if the feature is supported
* @memberof platform/core.TypeImpl#
*/
hasFeature: function (feature) {
return featureSet[feature] || false;
} }
TypeImpl.prototype.hasFeature = function (feature) {
return this.featureSet[feature] || false;
}; };
}
return TypeImpl; return TypeImpl;
} }

View File

@ -35,9 +35,11 @@ define(
*/ */
function TypeProperty(propertyDefinition) { function TypeProperty(propertyDefinition) {
// Load an appropriate conversion // Load an appropriate conversion
var conversion = new TypePropertyConversion( this.conversion = new TypePropertyConversion(
propertyDefinition.conversion || "identity" propertyDefinition.conversion || "identity"
); );
this.propertyDefinition = propertyDefinition;
}
// Check if a value is defined; used to check if initial array // Check if a value is defined; used to check if initial array
// values have been populated. // values have been populated.
@ -57,6 +59,24 @@ define(
return true; return true;
} }
// Specify a field deeply within an object
function specifyValue(object, propertyPath, value) {
// If path is not an array, just set the property
if (!Array.isArray(propertyPath)) {
object[propertyPath] = value;
} else if (propertyPath.length > 1) {
// Otherwise, look up in defined sequence
object[propertyPath[0]] = object[propertyPath[0]] || {};
specifyValue(
object[propertyPath[0]],
propertyPath.slice(1),
value
);
} else if (propertyPath.length === 1) {
object[propertyPath[0]] = value;
}
}
// Perform a lookup for a value from an object, // Perform a lookup for a value from an object,
// which may recursively look at contained objects // which may recursively look at contained objects
// based on the path provided. // based on the path provided.
@ -85,54 +105,37 @@ define(
return undefined; return undefined;
} }
function specifyValue(object, propertyPath, value) {
// If path is not an array, just set the property
if (!Array.isArray(propertyPath)) {
object[propertyPath] = value;
} else if (propertyPath.length > 1) {
// Otherwise, look up in defined sequence
object[propertyPath[0]] = object[propertyPath[0]] || {};
specifyValue(
object[propertyPath[0]],
propertyPath.slice(1),
value
);
} else if (propertyPath.length === 1) {
object[propertyPath[0]] = value;
}
}
return {
/** /**
* Retrieve the value associated with this property * Retrieve the value associated with this property
* from a given model. * from a given model.
* @memberof platform/core.TypeProperty# * @param {object} model a domain object model to read from
* @returns {*} the value for this property, as read from the model
*/ */
getValue: function (model) { TypeProperty.prototype.getValue = function (model) {
var property = propertyDefinition.property || var property = this.propertyDefinition.property ||
propertyDefinition.key, this.propertyDefinition.key,
initialValue = initialValue =
property && lookupValue(model, property); property && lookupValue(model, property);
// Provide an empty array if this is a multi-item // Provide an empty array if this is a multi-item
// property. // property.
if (Array.isArray(propertyDefinition.items)) { if (Array.isArray(this.propertyDefinition.items)) {
initialValue = initialValue || initialValue = initialValue ||
new Array(propertyDefinition.items.length); new Array(this.propertyDefinition.items.length);
} }
return conversion.toFormValue(initialValue); return this.conversion.toFormValue(initialValue);
}, };
/** /**
* Set a value associated with this property in * Set a value associated with this property in
* an object's model. * an object's model.
* @memberof platform/core.TypeProperty# * @param {object} model a domain object model to update
* @param {*} value the new value to set for this property
*/ */
setValue: function setValue(model, value) { TypeProperty.prototype.setValue = function (model, value) {
var property = propertyDefinition.property || var property = this.propertyDefinition.property ||
propertyDefinition.key; this.propertyDefinition.key;
// If an array contains all undefined values, treat it // If an array contains all undefined values, treat it
// as undefined, to filter back out arrays for input // as undefined, to filter back out arrays for input
@ -141,21 +144,20 @@ define(
// Convert to a value suitable for storage in the // Convert to a value suitable for storage in the
// domain object's model // domain object's model
value = conversion.toModelValue(value); value = this.conversion.toModelValue(value);
return property ? return property ?
specifyValue(model, property, value) : specifyValue(model, property, value) :
undefined; undefined;
}, };
/** /**
* Get the raw definition for this property. * Get the raw definition for this property.
* @memberof platform/core.TypeProperty# * @returns {TypePropertyDefinition}
*/ */
getDefinition: function () { TypeProperty.prototype.getDefinition = function () {
return propertyDefinition; return this.propertyDefinition;
}
}; };
}
return TypeProperty; return TypeProperty;
} }

View File

@ -76,6 +76,23 @@ define(
} }
} }
/**
* Convert a value from its format as read from a form, to a
* format appropriate to store in a model.
* @method platform/core.TypePropertyConversion#toModelValue
* @param {*} formValue value as read from a form
* @returns {*} value to store in a model
*/
/**
* Convert a value from its format as stored in a model, to a
* format appropriate to display in a form.
* @method platform/core.TypePropertyConversion#toFormValue
* @param {*} modelValue value as stored in a model
* @returns {*} value to display within a form
*/
return TypePropertyConversion; return TypePropertyConversion;
} }
); );

View File

@ -26,6 +26,27 @@ define(
function (TypeImpl, mergeModels) { function (TypeImpl, mergeModels) {
'use strict'; 'use strict';
/**
* Provides domain object types that are available/recognized within
* the system.
*
* @interface TypeService
*/
/**
* Get a specific type by name.
*
* @method TypeService#getType
* @param {string} key the key (machine-readable identifier)
* for the type of interest
* @returns {Type} the type identified by this key
*/
/**
* List all known types.
*
* @method TypeService#listTypes
* @returns {Type[]} all known types
*/
var TO_CONCAT = ['inherits', 'capabilities', 'properties', 'features'], var TO_CONCAT = ['inherits', 'capabilities', 'properties', 'features'],
TO_MERGE = ['model']; TO_MERGE = ['model'];
@ -49,30 +70,6 @@ define(
}) : array; }) : array;
} }
/**
* A type provider provides information about types of domain objects
* within the running Open MCT Web instance.
*
* @param {Array<TypeDefinition>} options.definitions the raw type
* definitions for this type.
* @memberof platform/core
* @constructor
*/
function TypeProvider(types) {
var rawTypeDefinitions = types,
typeDefinitions = (function (typeDefArray) {
var result = {};
typeDefArray.forEach(function (typeDef) {
var k = typeDef.key;
if (k) {
result[k] = (result[k] || []).concat(typeDef);
}
});
return result;
}(rawTypeDefinitions)),
typeMap = {},
undefinedType;
// Reduce an array of type definitions to a single type definiton, // Reduce an array of type definitions to a single type definiton,
// which has merged all properties in order. // which has merged all properties in order.
function collapse(typeDefs) { function collapse(typeDefs) {
@ -106,9 +103,54 @@ define(
return collapsed; return collapsed;
} }
/**
* A type provider provides information about types of domain objects
* within the running Open MCT Web instance.
*
* @param {Array<TypeDefinition>} types the raw type
* definitions for this type.
* @memberof platform/core
* @constructor
*/
function TypeProvider(types) {
var rawTypeDefinitions = types,
typeDefinitions = (function (typeDefArray) {
var result = {};
typeDefArray.forEach(function (typeDef) {
var k = typeDef.key;
if (k) {
result[k] = (result[k] || []).concat(typeDef);
}
});
return result;
}(rawTypeDefinitions));
this.typeMap = {};
this.typeDefinitions = typeDefinitions;
this.rawTypeDefinitions = types;
}
TypeProvider.prototype.listTypes = function () {
var self = this;
return removeDuplicates(
this.rawTypeDefinitions.filter(function (def) {
return def.key;
}).map(function (def) {
return def.key;
}).map(function (key) {
return self.getType(key);
})
);
};
TypeProvider.prototype.getType = function (key) {
var typeDefinitions = this.typeDefinitions,
self = this;
function getUndefinedType() { function getUndefinedType() {
return (undefinedType = undefinedType || collapse( return (self.undefinedType = self.undefinedType || collapse(
rawTypeDefinitions.filter(function (typeDef) { self.rawTypeDefinitions.filter(function (typeDef) {
return !typeDef.key; return !typeDef.key;
}) })
)); ));
@ -118,7 +160,6 @@ define(
return Array.isArray(value) ? value : [value]; return Array.isArray(value) ? value : [value];
} }
function lookupTypeDef(typeKey) {
function buildTypeDef(typeKey) { function buildTypeDef(typeKey) {
var typeDefs = typeDefinitions[typeKey] || [], var typeDefs = typeDefinitions[typeKey] || [],
inherits = typeDefs.map(function (typeDef) { inherits = typeDefs.map(function (typeDef) {
@ -134,63 +175,21 @@ define(
// Always provide a default name // Always provide a default name
def.model = def.model || {}; def.model = def.model || {};
def.model.name = def.model.name || ( def.model.name = def.model.name ||
"Unnamed " + (def.name || "Object") ("Unnamed " + (def.name || "Object"));
);
return def; return def;
} }
return (typeMap[typeKey] = typeMap[typeKey] || buildTypeDef(typeKey)); function lookupTypeDef(typeKey) {
return (self.typeMap[typeKey] =
self.typeMap[typeKey] || buildTypeDef(typeKey));
} }
return {
/**
* Get a list of all types defined by this service.
*
* @returns {Promise<Array<module:core/type/type-impl.TypeImpl>>} a
* promise for an array of all type instances defined
* by this service.
* @memberof module:core/type/type-provider.TypeProvider#
* @memberof platform/core.TypeProvider#
*/
listTypes: function () {
var self = this;
return removeDuplicates(
rawTypeDefinitions.filter(function (def) {
return def.key;
}).map(function (def) {
return def.key;
}).map(function (key) {
return self.getType(key);
})
);
},
/**
* Get a specific type by name.
*
* @param {string} [key] the key (machine-readable identifier)
* for the type of interest
* @returns {Promise<module:core/type/type-impl.TypeImpl>} a
* promise for a type object identified by this key.
* @memberof module:core/type/type-provider.TypeProvider#
* @memberof platform/core.TypeProvider#
*/
getType: function (key) {
return new TypeImpl(lookupTypeDef(key)); return new TypeImpl(lookupTypeDef(key));
}
}; };
}
// Services framework is designed to expect factories
TypeProvider.instantiate = TypeProvider;
return TypeProvider; return TypeProvider;
} }
); );

View File

@ -36,10 +36,14 @@ define(
* object. * object.
* *
* @memberof platform/core * @memberof platform/core
* @implements {Capability}
* @constructor * @constructor
*/ */
function ViewCapability(viewService, domainObject) { function ViewCapability(viewService, domainObject) {
return { this.viewService = viewService;
this.domainObject = domainObject;
}
/** /**
* Get all view definitions which are applicable to * Get all view definitions which are applicable to
* this object. * this object.
@ -47,11 +51,9 @@ define(
* which are applicable to this object. * which are applicable to this object.
* @memberof platform/core.ViewCapability# * @memberof platform/core.ViewCapability#
*/ */
invoke: function () { ViewCapability.prototype.invoke = function () {
return viewService.getViews(domainObject); return this.viewService.getViews(this.domainObject);
}
}; };
}
return ViewCapability; return ViewCapability;
} }

View File

@ -29,6 +29,22 @@ define(
function () { function () {
"use strict"; "use strict";
/**
* Provides definitions for views that are available for specific
* domain objects.
*
* @interface ViewService
*/
/**
* Get all views which are applicable to this domain object.
*
* @method ViewService#getViews
* @param {DomainObject} domainObject the domain object to view
* @returns {View[]} all views which can be used to visualize
* this domain object.
*/
/** /**
* A view provider allows view definitions (defined as extensions) * A view provider allows view definitions (defined as extensions)
* to be read, and takes responsibility for filtering these down * to be read, and takes responsibility for filtering these down
@ -58,6 +74,8 @@ define(
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @param {View[]} an array of view definitions * @param {View[]} an array of view definitions
* @param $log Angular's logging service
* @implements {ViewService}
*/ */
function ViewProvider(views, $log) { function ViewProvider(views, $log) {
@ -79,6 +97,13 @@ define(
return key; return key;
} }
// Filter out any key-less views
this.views = views.filter(validate);
}
ViewProvider.prototype.getViews = function (domainObject) {
var type = domainObject.useCapability("type");
// Check if an object has all capabilities designated as `needs` // Check if an object has all capabilities designated as `needs`
// for a view. Exposing a capability via delegation is taken to // for a view. Exposing a capability via delegation is taken to
// satisfy this filter if `allowDelegation` is true. // satisfy this filter if `allowDelegation` is true.
@ -122,35 +147,16 @@ define(
return matches; return matches;
} }
function getViews(domainObject) {
var type = domainObject.useCapability("type");
// First, filter views by type (matched to domain object type.) // First, filter views by type (matched to domain object type.)
// Second, filter by matching capabilities. // Second, filter by matching capabilities.
return views.filter(function (view) { return this.views.filter(function (view) {
return viewMatchesType(view, type) && capabilitiesMatch( return viewMatchesType(view, type) && capabilitiesMatch(
domainObject, domainObject,
view.needs || [], view.needs || [],
view.delegation || false view.delegation || false
); );
}); });
}
// Filter out any key-less views
views = views.filter(validate);
return {
/**
* Get all views which are applicable to this domain object.
*
* @param {DomainObject} domainObject the domain object to view
* @returns {View[]} all views which can be used to visualize
* this domain object.
* @memberof platform/core.ViewProvider#
*/
getViews: getViews
}; };
}
return ViewProvider; return ViewProvider;
} }

View File

@ -25,7 +25,7 @@
* DomainObjectSpec. Created by vwoeltje on 11/6/14. * DomainObjectSpec. Created by vwoeltje on 11/6/14.
*/ */
define( define(
["../../src/objects/DomainObject"], ["../../src/objects/DomainObjectImpl"],
function (DomainObject) { function (DomainObject) {
"use strict"; "use strict";

View File

@ -23,7 +23,7 @@
define( define(
['../../src/types/TypeImpl'], ['../../src/types/TypeImpl'],
function (typeImpl) { function (TypeImpl) {
"use strict"; "use strict";
describe("Type definition wrapper", function () { describe("Type definition wrapper", function () {
@ -41,7 +41,7 @@ define(
properties: [ {} ], properties: [ {} ],
model: {someKey: "some value"} model: {someKey: "some value"}
}; };
type = typeImpl(testTypeDef); type = new TypeImpl(testTypeDef);
}); });
it("exposes key from definition", function () { it("exposes key from definition", function () {

View File

@ -128,7 +128,7 @@ define(
}); });
it("includes capabilities from undefined type in all types", function () { it("includes capabilities from undefined type in all types", function () {
captured.type = TypeProvider.instantiate( captured.type = new TypeProvider(
testTypeDefinitions.concat([ testTypeDefinitions.concat([
{ capabilities: ['a', 'b', 'c'] }, { capabilities: ['a', 'b', 'c'] },
{ capabilities: ['x', 'y', 'z'] } { capabilities: ['x', 'y', 'z'] }