Files
openmct/platform/framework/src/register/ServiceCompositor.js
Victor Woeltjen 9e61e89da4 [Framework] Wire in service compositor
Use service compositor as a form of custom registrar.
Also, add clarifying comments. WTD-518.
2014-11-05 16:42:15 -08:00

204 lines
7.4 KiB
JavaScript

/*global define,Promise*/
/**
* Module defining ServiceCompositor. Created by vwoeltje on 11/5/14.
*/
define(
[],
function () {
"use strict";
/**
* Handles service compositing; that is, building up services
* from provider, aggregator, and decorator components.
*
* @constructor
*/
function ServiceCompositor(app, $log) {
var latest = {},
providerLists = {}; // Track latest services registered
// Log a warning; defaults to "no service provided by"
function warn(extension, category, message) {
var msg = message || "No service provided by";
$log.warn([
msg,
" ",
category,
" ",
extension.key,
" from bundle ",
extension.bundle.path,
"; skipping."
].join(""));
}
// Echo arguments; used to represent groups of non-built-in
// extensions as a single dependency.
function echoMany() {
return Array.prototype.slice.call(arguments);
}
// Echo arguments; used to represent groups of non-built-in
// extensions as a single dependency.
function echoSingle(value) {
return value;
}
// Generates utility functions to match types (one of
// provider, aggregator, or decorator) of component. Used
// to filter down to specific types, which are handled
// in order.
function hasType(type) {
return function (extension) {
return extension.type === type;
};
}
// Make a unique name for a service component.
function makeName(category, service, index) {
return [
service,
"[",
category,
"#",
index,
"]"
].join("");
}
// Register a specific provider instance with Angular, and
// record its name for subsequent stages.
function registerProvider(provider, index) {
var service = provider.provides,
dependencies = provider.depends || [],
name = makeName("provider", service, index);
if (!service) {
return warn(provider, "provider");
}
providerLists[service] = providerLists[service] || [];
providerLists[service].push(name);
// This provider is the latest candidate for resolving
// the composite service.
latest[service] = name;
app.service(name, dependencies.concat([provider]));
}
// Register an array of providers as a single dependency;
// aggregators will then depend upon this to consume all
// aggregated providers as a single dependency.
function registerProviderSets() {
Object.keys(providerLists).forEach(function (service) {
var name = makeName("provider", service, "*"),
list = providerLists[service];
app.service(name, list.concat([echoMany]));
});
}
// Registers an aggregator via Angular, including both
// its declared dependencies and the additional, implicit
// dependency upon the array of all providers.
function registerAggregator(aggregator, index) {
var service = aggregator.provides,
dependencies = aggregator.depends || [],
providerSetName = makeName("provider", service, "*"),
name = makeName("aggregator", service, index);
if (!service) {
return warn(aggregator, "aggregator");
}
// Aggregators need other services to aggregate, otherwise they
// do nothing.
if (!latest[service]) {
return warn(
aggregator,
"aggregator",
"No services to aggregate for"
);
}
dependencies = dependencies.concat([providerSetName]);
latest[service] = name;
app.service(name, dependencies.concat([aggregator]));
}
// Registers a decorator via Angular, including its implicit
// dependency on the latest service component which has come
// before it.
function registerDecorator(decorator, index) {
var service = decorator.provides,
dependencies = decorator.depends || [],
name = makeName("decorator", service, index);
if (!service) {
return warn(decorator, "decorator");
}
// Decorators need other services to decorate, otherwise they
// do nothing.
if (!latest[service]) {
return warn(
decorator,
"decorator",
"No services to decorate for"
);
}
dependencies = dependencies.concat([latest[service]]);
latest[service] = name;
app.service(name, dependencies.concat([decorator]));
}
// Alias the latest services of various types back to the
// more general service declaration.
function registerLatest() {
Object.keys(latest).forEach(function (service) {
app.service(service, [latest[service], echoSingle]);
});
}
function registerComposites(providers, aggregators, decorators) {
providers.forEach(registerProvider);
registerProviderSets();
aggregators.forEach(registerAggregator);
decorators.forEach(registerDecorator);
registerLatest();
}
function registerCompositeServices(components) {
registerComposites(
components.filter(hasType("provider")),
components.filter(hasType("aggregator")),
components.filter(hasType("decorator"))
);
}
return {
/**
* Register composite services with Angular. This will build
* up a dependency hierarchy between providers, aggregators,
* and/or decorators, such that a dependency upon the service
* type they expose shall be satisfied by their fully-wired
* whole.
*
* Note that this method assumes that a complete set of
* components shall be provided. Multiple calls to this
* method may not behave as expected.
*
* @param {Array} components extensions of category component
*/
registerCompositeServices: registerCompositeServices
};
}
return ServiceCompositor;
}
);