Merge remote-tracking branch 'github/master' into mobile

This commit is contained in:
Victor Woeltjen 2015-09-17 16:41:13 -07:00
commit 411a4ebfbf
11 changed files with 438 additions and 14 deletions

View File

@ -147,7 +147,7 @@
{
"key": "composition",
"implementation": "capabilities/CompositionCapability.js",
"depends": [ "$injector" ]
"depends": [ "$injector", "contextualize" ]
},
{
"key": "relationship",
@ -202,6 +202,11 @@
{
"key": "topic",
"implementation": "services/Topic.js"
},
{
"key": "contextualize",
"implementation": "services/Contextualize.js",
"depends": [ "$log" ]
}
],
"roots": [

View File

@ -25,8 +25,7 @@
* Module defining CompositionCapability. Created by vwoeltje on 11/7/14.
*/
define(
["./ContextualDomainObject"],
function (ContextualDomainObject) {
function () {
"use strict";
/**
@ -41,12 +40,13 @@ define(
* @constructor
* @implements {Capability}
*/
function CompositionCapability($injector, domainObject) {
function CompositionCapability($injector, contextualize, domainObject) {
// Get a reference to the object service from $injector
this.injectObjectService = function () {
this.objectService = $injector.get("objectService");
};
this.contextualize = contextualize;
this.domainObject = domainObject;
}
@ -58,19 +58,17 @@ define(
CompositionCapability.prototype.invoke = function () {
var domainObject = this.domainObject,
model = domainObject.getModel(),
contextualize = this.contextualize,
ids;
// Then filter out non-existent objects,
// and wrap others (such that they expose a
// "context" capability)
function contextualize(objects) {
function contextualizeObjects(objects) {
return ids.filter(function (id) {
return objects[id];
}).map(function (id) {
return new ContextualDomainObject(
objects[id],
domainObject
);
return contextualize(objects[id], domainObject);
});
}
@ -86,7 +84,7 @@ define(
this.lastModified = model.modified;
// Load from the underlying object service
this.lastPromise = this.objectService.getObjects(ids)
.then(contextualize);
.then(contextualizeObjects);
}
return this.lastPromise;

View File

@ -84,7 +84,7 @@ define(
parentContext =
parentObject && parentObject.getCapability('context'),
parentPath = parentContext ?
parentContext.getPath() : [ this.parentObject ];
parentContext.getPath() : [ this.parentObject ];
return parentPath.concat([this.domainObject]);
};

View File

@ -0,0 +1,78 @@
/*****************************************************************************
* 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*/
define(
['../capabilities/ContextualDomainObject'],
function (ContextualDomainObject) {
"use strict";
/**
* Wrap a domain object such that it has a `context` capability
* referring to a specific parent.
*
* Usage:
*
* contextualize(domainObject, parentObject)
*
* Attempting to contextualize an object with a parent that does
* not include that object in its composition may have
* unpredictable results; a warning will be logged if this occurs.
*
* @returns {Function}
* @memberof platform/core
*/
function Contextualize($log) {
function validate(id, parentObject) {
var model = parentObject && parentObject.getModel(),
composition = (model || {}).composition || [];
if (composition.indexOf(id) === -1) {
$log.warn([
"Attempted to contextualize",
id,
"in",
parentObject && parentObject.getId(),
"but that object does not contain",
id,
"in its composition.",
"Unexpected behavior may follow."
].join(" "));
}
}
/**
* Contextualize this domain object.
* @param {DomainObject} domainObject the domain object
* to wrap with a context
* @param {DomainObject} parentObject the domain object
* which should appear as the contextual parent
*/
return function (domainObject, parentObject) {
validate(domainObject.getId(), parentObject);
return new ContextualDomainObject(domainObject, parentObject);
};
}
return Contextualize;
}
);

View File

@ -25,8 +25,11 @@
* CompositionCapabilitySpec. Created by vwoeltje on 11/6/14.
*/
define(
["../../src/capabilities/CompositionCapability"],
function (CompositionCapability) {
[
"../../src/capabilities/CompositionCapability",
"../../src/capabilities/ContextualDomainObject"
],
function (CompositionCapability, ContextualDomainObject) {
"use strict";
var DOMAIN_OBJECT_METHODS = [
@ -40,6 +43,7 @@ define(
describe("The composition capability", function () {
var mockDomainObject,
mockInjector,
mockContextualize,
mockObjectService,
composition;
@ -70,11 +74,19 @@ define(
return (name === "objectService") && mockObjectService;
}
};
mockContextualize = jasmine.createSpy('contextualize');
// Provide a minimal (e.g. no error-checking) implementation
// of contextualize for simplicity
mockContextualize.andCallFake(function (domainObject, parentObject) {
return new ContextualDomainObject(domainObject, parentObject);
});
mockObjectService.getObjects.andReturn(mockPromise([]));
composition = new CompositionCapability(
mockInjector,
mockContextualize,
mockDomainObject
);
});
@ -113,4 +125,4 @@ define(
});
}
);
);

View File

@ -0,0 +1,90 @@
/*****************************************************************************
* 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,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../../src/services/Contextualize"],
function (Contextualize) {
"use strict";
var DOMAIN_OBJECT_METHODS = [
'getId',
'getModel',
'getCapability',
'hasCapability',
'useCapability'
];
describe("The 'contextualize' service", function () {
var mockLog,
mockDomainObject,
mockParentObject,
testParentModel,
contextualize;
beforeEach(function () {
testParentModel = { composition: ["abc"] };
mockLog = jasmine.createSpyObj(
"$log",
[ "error", "warn", "info", "debug" ]
);
mockDomainObject =
jasmine.createSpyObj('domainObject', DOMAIN_OBJECT_METHODS);
mockParentObject =
jasmine.createSpyObj('parentObject', DOMAIN_OBJECT_METHODS);
mockDomainObject.getId.andReturn("abc");
mockDomainObject.getModel.andReturn({});
mockParentObject.getId.andReturn("parent");
mockParentObject.getModel.andReturn(testParentModel);
contextualize = new Contextualize(mockLog);
});
it("attaches a context capability", function () {
var contextualizedObject =
contextualize(mockDomainObject, mockParentObject);
expect(contextualizedObject.getId()).toEqual("abc");
expect(contextualizedObject.getCapability("context"))
.toBeDefined();
expect(contextualizedObject.getCapability("context").getParent())
.toBe(mockParentObject);
});
it("issues a warning if composition does not match", function () {
// Precondition - normally it should not issue a warning
contextualize(mockDomainObject, mockParentObject);
expect(mockLog.warn).not.toHaveBeenCalled();
testParentModel.composition = ["xyz"];
contextualize(mockDomainObject, mockParentObject);
expect(mockLog.warn).toHaveBeenCalled();
});
});
}
);

View File

@ -24,6 +24,7 @@
"objects/DomainObject",
"objects/DomainObjectProvider",
"services/Contextualize",
"services/Now",
"services/Throttle",
"services/Topic",

View File

@ -37,6 +37,12 @@
"type": "decorator",
"provides": "creationService",
"implementation": "services/LocatingCreationDecorator.js"
},
{
"type": "decorator",
"provides": "objectService",
"implementation": "services/LocatingObjectDecorator.js",
"depends": ["contextualize", "$q", "$log"]
}
],
"controllers": [

View File

@ -0,0 +1,98 @@
/*****************************************************************************
* 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 */
define(
function () {
"use strict";
/**
* Ensures that domain objects are loaded with a context capability
* that reflects their location.
* @constructor
* @implements {ObjectService}
* @memberof platform/entanglement
*/
function LocatingObjectDecorator(contextualize, $q, $log, objectService) {
this.contextualize = contextualize;
this.$log = $log;
this.objectService = objectService;
this.$q = $q;
}
LocatingObjectDecorator.prototype.getObjects = function (ids) {
var $q = this.$q,
$log = this.$log,
contextualize = this.contextualize,
objectService = this.objectService,
result = {};
// Load a single object using location to establish a context
function loadObjectInContext(id, exclude) {
function attachContext(objects) {
var domainObject = (objects || {})[id],
model = domainObject && domainObject.getModel(),
location = (model || {}).location;
// If no location is defined, we can't look up a context.
if (!location) {
return domainObject;
}
// Avoid looping indefinitely on cyclical locations
if (exclude[id]) {
$log.warn([
"LocatingObjectDecorator detected a cycle",
"while attempted to define a context for",
id + ";",
"no context will be added and unexpected behavior",
"may follow."
].join(" "));
return domainObject;
}
// Record that we've visited this ID to detect cycles.
exclude[id] = true;
// Do the recursive step to get the parent...
return loadObjectInContext(location, exclude)
.then(function (parent) {
// ...and then contextualize with it!
return contextualize(domainObject, parent);
});
}
return objectService.getObjects([id]).then(attachContext);
}
ids.forEach(function (id) {
result[id] = loadObjectInContext(id, {});
});
return $q.all(result);
};
return LocatingObjectDecorator;
}
);

View File

@ -0,0 +1,135 @@
/*****************************************************************************
* 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,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/services/LocatingObjectDecorator'
],
function (LocatingObjectDecorator) {
"use strict";
describe("LocatingObjectDecorator", function () {
var mockContextualize,
mockQ,
mockLog,
mockObjectService,
mockCallback,
testObjects,
testModels,
decorator;
function testPromise(v) {
return (v || {}).then ? v : {
then: function (callback) {
return testPromise(callback(v));
}
};
}
beforeEach(function () {
// A <- B <- C
// D <-> E, to verify cycle detection
testModels = {
a: { name: "A" },
b: { name: "B", location: "a" },
c: { name: "C", location: "b" },
d: { name: "D", location: "e" },
e: { name: "E", location: "d" }
};
testObjects = {};
mockContextualize = jasmine.createSpy("contextualize");
mockQ = jasmine.createSpyObj("$q", ["when", "all"]);
mockLog =
jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
mockObjectService =
jasmine.createSpyObj("objectService", ["getObjects"]);
mockContextualize.andCallFake(function (domainObject, parentObject) {
// Not really what contextualize does, but easy to test!
return {
testObject: domainObject,
testParent: parentObject
};
});
mockQ.when.andCallFake(testPromise);
mockQ.all.andCallFake(function (promises) {
var result = {};
Object.keys(promises).forEach(function (k) {
promises[k].then(function (v) { result[k] = v; });
});
return testPromise(result);
});
mockObjectService.getObjects.andReturn(testPromise(testObjects));
mockCallback = jasmine.createSpy("callback");
Object.keys(testModels).forEach(function (id) {
testObjects[id] = jasmine.createSpyObj(
"domainObject-" + id,
[ "getId", "getModel", "getCapability" ]
);
testObjects[id].getId.andReturn(id);
testObjects[id].getModel.andReturn(testModels[id]);
});
decorator = new LocatingObjectDecorator(
mockContextualize,
mockQ,
mockLog,
mockObjectService
);
});
it("contextualizes domain objects by location", function () {
decorator.getObjects(['b', 'c']).then(mockCallback);
expect(mockCallback).toHaveBeenCalledWith({
b: {
testObject: testObjects.b,
testParent: testObjects.a
},
c: {
testObject: testObjects.c,
testParent: {
testObject: testObjects.b,
testParent: testObjects.a
}
}
});
});
it("warns on cycle detection", function () {
// Base case, no cycle, no warning
decorator.getObjects(['a', 'b', 'c']);
expect(mockLog.warn).not.toHaveBeenCalled();
decorator.getObjects(['e']);
expect(mockLog.warn).toHaveBeenCalled();
});
});
}
);

View File

@ -5,5 +5,6 @@
"services/MoveService",
"services/LocationService",
"services/LocatingCreationDecorator",
"services/LocatingObjectDecorator",
"capabilities/LocationCapability"
]