mirror of
https://github.com/nasa/openmct.git
synced 2025-06-27 11:32:13 +00:00
Compare commits
13 Commits
vue-table-
...
open462pro
Author | SHA1 | Date | |
---|---|---|---|
2939267f2f | |||
fda6608264 | |||
60784aa56f | |||
7326320954 | |||
5dbb7658ac | |||
336b76f28c | |||
5bb255dabf | |||
6a0bc1c48e | |||
28741297c1 | |||
585355cd66 | |||
1adbacd70b | |||
366d467315 | |||
70a4c7d624 |
424
docs/src/design/proposals/Registration.md
Normal file
424
docs/src/design/proposals/Registration.md
Normal file
@ -0,0 +1,424 @@
|
|||||||
|
# Developer Use Cases
|
||||||
|
|
||||||
|
1. Extending and maintaining Open MCT itself.
|
||||||
|
2. Adapting and customizing Open MCT for use in specific missions.
|
||||||
|
3. Developing features for use with Open MCT across multiple different
|
||||||
|
missions.
|
||||||
|
|
||||||
|
# Scope
|
||||||
|
|
||||||
|
As demonstrated by the existing APIs, writing plugins is sufficient to
|
||||||
|
satisfy the three use cases above in the majority of cases. The only feature
|
||||||
|
which is known to be unsatisfiable by plugins is plugin support itself.
|
||||||
|
|
||||||
|
As such, prefer to keep "plugin-external" components small and simple, to
|
||||||
|
keep the majority of development in plugins (or plugin-like components.)
|
||||||
|
|
||||||
|
The "registration API" described in this document is limited to that scope:
|
||||||
|
It describes classes and patterns that can allow plugins to interact,
|
||||||
|
while making minimal assumptions about what specific functionality is to
|
||||||
|
be implemented in these plugins.
|
||||||
|
|
||||||
|
# Problems to Address
|
||||||
|
|
||||||
|
1. Dependencies between plugins are implicit; a plugin may fail if its
|
||||||
|
required dependencies are not included, without any clear indication
|
||||||
|
of why this failure has occurred. Significant familiarity with
|
||||||
|
Open MCT is typically required to debug in these circumstances.
|
||||||
|
2. Extension points are often implicit; no specific plugin is
|
||||||
|
identifiably responsible for defining any specific extension category
|
||||||
|
or composite service. These are instead paired by string matching.
|
||||||
|
This makes it difficult to follow how the application is initialized
|
||||||
|
and how objects are passed around at run-time, and creates an
|
||||||
|
additional documentation burden, as these named extension points do not
|
||||||
|
fit into more standard API documentation.
|
||||||
|
3. Reuse of components between plugins is limited. Exposing base classes
|
||||||
|
from one plugin and reusing them from another is overly-difficult.
|
||||||
|
|
||||||
|
# Principles
|
||||||
|
|
||||||
|
1. The Registration API is exposed as a set of classes in one or more
|
||||||
|
namespaces. This supports reuse and extension using standard,
|
||||||
|
well-known object-oriented patterns. A composition-oriented style
|
||||||
|
is still supported and encouraged, but not enforced.
|
||||||
|
2. Extension points should be expressed as objects with known interfaces.
|
||||||
|
(More specifically, they should be _expressible_ in this fashion;
|
||||||
|
it is out of scope for the Registration API to expose any specific
|
||||||
|
extension points.)
|
||||||
|
3. Dependencies that are intended for injection should be expressed as
|
||||||
|
arguments to a constructor. The Registration API does not need to
|
||||||
|
stipulate this, but should at least be compatible with this. This
|
||||||
|
is both to allow for compatibility with existing code, and to allow
|
||||||
|
for clear documentation of the dependencies of specific components
|
||||||
|
("to construct one of these, you need one of those.")
|
||||||
|
4. The Registration API should accept minimal responsibility in order
|
||||||
|
to impose minimal constraints. For instance, it should not be
|
||||||
|
responsible for performing script-loading. Its sole responsibility
|
||||||
|
is to facilitate communication between components.
|
||||||
|
5. The Registration API should continue to support ubiquitous use of
|
||||||
|
patterns that have proven useful for plugin-based extensibility
|
||||||
|
(the Registry, Composite, and Decorator patterns, specifically.)
|
||||||
|
However, it should be designed such that knowledge of these patterns
|
||||||
|
is only required when it is appropriate to the specific task or
|
||||||
|
activity at hand. (For instance, you should not need to be familiar
|
||||||
|
with decorators in order to simply register something.)
|
||||||
|
|
||||||
|
# Interfaces
|
||||||
|
|
||||||
|
In keeping with the scope of the Registration API, the interfaces
|
||||||
|
described here are sufficient to:
|
||||||
|
|
||||||
|
* Describe the set of plugins in use for a particular instance of
|
||||||
|
Open MCT, and initiate their behavior.
|
||||||
|
* Support common patterns by which plugins can utilize and expose
|
||||||
|
defined extension points.
|
||||||
|
|
||||||
|
Notably, there is no interdependency between these two sets of
|
||||||
|
behavior; one could use the base classes for extension points
|
||||||
|
independently of the plugin mechanism, and vice versa. This both
|
||||||
|
ensures loose coupling within the Registration API, and also
|
||||||
|
allows for greater flexibility for developers implementing plugins.
|
||||||
|
|
||||||
|
## Application-level Interfaces
|
||||||
|
|
||||||
|
```nomnoml
|
||||||
|
[Application |
|
||||||
|
install(plugin : Plugin)
|
||||||
|
uninstall(plugin : Plugin)
|
||||||
|
run()
|
||||||
|
]
|
||||||
|
|
||||||
|
[Plugin |
|
||||||
|
initialize()
|
||||||
|
start()
|
||||||
|
]
|
||||||
|
|
||||||
|
[Application]<:-[MCT |
|
||||||
|
core : Plugin
|
||||||
|
ui : Plugin
|
||||||
|
policy : Plugin
|
||||||
|
...etc
|
||||||
|
]
|
||||||
|
[Application]-o[Plugin]
|
||||||
|
```
|
||||||
|
|
||||||
|
Summary of interfaces:
|
||||||
|
|
||||||
|
* `Application` represents a complete piece of software that has been
|
||||||
|
composed of plugins.
|
||||||
|
* `install(plugin)` adds a plugin to this application.
|
||||||
|
* `uninstall(plugin)` removes a plugin from this application.
|
||||||
|
* `run()` starts the application. This will first initialize all
|
||||||
|
plugins, then start all plugins.
|
||||||
|
* `Plugin` represents a unit of functionality available for use within
|
||||||
|
applications. It exposes methods to be triggered at various points
|
||||||
|
in the application lifecycle. A plugin is meant to be "single-use";
|
||||||
|
multiple calls to `initialize()` and/or `start()` should have no
|
||||||
|
effect.
|
||||||
|
* `initialize()` performs any configuration and/or registration
|
||||||
|
associated with this plugin.
|
||||||
|
* `start()` initiates any behavior associated with this plugin that
|
||||||
|
needs to run immediately after the application has started. (Useful
|
||||||
|
for a "bootstrap" plugin.)
|
||||||
|
* `MCT` is an instance of an `Application` that self-installs various
|
||||||
|
plugins during its constructor call. It also exposes these same
|
||||||
|
plugins as public fields such that other applications may access
|
||||||
|
them (to `uninstall` them, for instance, or to pass them into other
|
||||||
|
plugins.)
|
||||||
|
|
||||||
|
Rationale for various interfaces:
|
||||||
|
|
||||||
|
* `Application` separates out a core responsibility of Open MCT (plugin
|
||||||
|
composition) from the specific details of Open MCT (the set of plugins
|
||||||
|
which compose it.)
|
||||||
|
* `install` allows plugins to be added (central to plugin support.)
|
||||||
|
* `uninstall` allows plugins to be removed in circumstances where they
|
||||||
|
are unwanted; have observed practical cases where this is desirable.
|
||||||
|
* `run` separates instantiation of the application from the initiation
|
||||||
|
of its behavior.
|
||||||
|
* `Plugin` provides an interface for `Application` to use when accepting
|
||||||
|
plugins, and a base class for plugin developers to extend from.
|
||||||
|
* `initialize` is useful to `Application`, which wants to implement
|
||||||
|
`run` in a manner which wholly separates initialization (the wiring
|
||||||
|
up of various services/registries) from bootstrapping.
|
||||||
|
* `start` is useful to `Application`, to start any run-time behavior
|
||||||
|
once the application is fully-configured.
|
||||||
|
* `MCT` is useful to producers of software built on Open MCT, who would
|
||||||
|
like a baseline set of functionality to build upon.
|
||||||
|
|
||||||
|
Applications built on Open MCT are expected to be exposed as classes
|
||||||
|
which extend `MCT` and add/remove plugins during the constructor
|
||||||
|
call. (This is a recommended pattern of use only; other, more
|
||||||
|
imperative usage of this API is equally viable.)
|
||||||
|
|
||||||
|
## Extension Points
|
||||||
|
|
||||||
|
```nomnoml
|
||||||
|
[Provider<X,S> |
|
||||||
|
get() : S
|
||||||
|
register(factory : (function () : S))
|
||||||
|
decorate(decorator : (function (S) : S))
|
||||||
|
compose(compositor : (function (Array<X>) : S))
|
||||||
|
]
|
||||||
|
|
||||||
|
[Provider<X,S>]<:-[Provider<T,Array<T>>]
|
||||||
|
[Provider<T,Array<T>>]<:-[Registry<T>]
|
||||||
|
[Provider<X,S>]<:-[Provider<S,S>]
|
||||||
|
[Provider<S,S>]<:-[ServiceProvider<S>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Omitted from this diagram (for clarity) are `options` arguments to
|
||||||
|
`register`, `decorate`, and `compose`. This argument should allow,
|
||||||
|
at minimum, a `priority` to be specified, in order to control ordering
|
||||||
|
of registered extensions.
|
||||||
|
|
||||||
|
* `Provider<X, S>` is responsible for providing objects of type `S` based
|
||||||
|
on zero or more instances of objects of type `X` which have been registered.
|
||||||
|
In practice, a `Provider` instance is an extension point within the
|
||||||
|
architecture.
|
||||||
|
* `get() : S` provides an instance of the architectural component, as
|
||||||
|
constructed using the registered objects, along with the highest-priority
|
||||||
|
compositor and and decorators.
|
||||||
|
* `register(factory : (function () : X), [options] : RegistrationOptions)`
|
||||||
|
registers an object (as returned by the provided `factory` function)
|
||||||
|
with this provider. Evaluation of the provided `factory` will be
|
||||||
|
deferred until the first `get()` call to the `Provider`.
|
||||||
|
* `compose(compositor : (function (X[]) : S), [options] : RegistrationOptions)`
|
||||||
|
introduces a new strategy for converting an array of registered objects
|
||||||
|
of type `X` into a single instance of an object of type `S`. The
|
||||||
|
highest-priority `compositor` that has been registered in this fashion
|
||||||
|
will be used to assemble the provided object (before decoration)
|
||||||
|
* `decorate(decorator : (function (S) : S), [options] : RegistrationOptions)`
|
||||||
|
augments behavior of objects provided by `get`, in priority order.
|
||||||
|
* `ServiceProvider<S>` provides analogous support for the _composite services_
|
||||||
|
pattern used throughout Open MCT (which, in turn, is a superset of the
|
||||||
|
functionality needed for plain services.)
|
||||||
|
* `Registry<T>` provides analogous support for _extension categories_, also
|
||||||
|
used ubiquitously through Open MCT.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
The following examples are provided to illustrate the intended usage of
|
||||||
|
the Registration API. Particular attention is given to obeying and
|
||||||
|
utilizing the "dependency injection as code style"
|
||||||
|
[decision from the API Redesign](APIRedesign.md#decisions).
|
||||||
|
|
||||||
|
## Building Applications on Open MCT
|
||||||
|
|
||||||
|
Applications built using Open MCT are expected to extend the `MCT` base
|
||||||
|
class and should typically self-install distinguishing plugins during
|
||||||
|
the constructor call. Any pre-installed plugins that are undesirable
|
||||||
|
should also be uninstalled at this point (to support such usage,
|
||||||
|
`MCT` should expose instances of any installed plugins that are
|
||||||
|
considered optional.)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
define(['mct', './plugins'], function (mct, plugins) {
|
||||||
|
var MCT = mct.MCT;
|
||||||
|
|
||||||
|
function Variant() {
|
||||||
|
MCT.call(this);
|
||||||
|
|
||||||
|
this.install(new plugins.SomePlugin());
|
||||||
|
this.install(new plugins.SomeOtherPlugin(this.core));
|
||||||
|
|
||||||
|
this.uninstall(this.plugins.persistence.localStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant.prototype = Object.create(MCT.prototype);
|
||||||
|
|
||||||
|
return Variant;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Running an application build using Open MCT then typically looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
define(['./Variant], function (Variant) {
|
||||||
|
new Variant().run();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing Plugins
|
||||||
|
|
||||||
|
A plugin for Open MCT should inherit from the `Plugin` base class.
|
||||||
|
|
||||||
|
Plugins will typically use extension points exposed by other plugins;
|
||||||
|
put another way, plugins will typically _depend_ upon other plugins.
|
||||||
|
Consistent with "dependency injection as a code style," the preferred
|
||||||
|
way for plugins to acquire these references is via constructor
|
||||||
|
arguments. (Put another way, in order to use a plugin, you are expected
|
||||||
|
to supply its dependencies.) Note that this is not a requirement,
|
||||||
|
as Open MCT only ever interacts directly with plugin _instances_;
|
||||||
|
any other way of assembling an object with the `Plugin` interface
|
||||||
|
should be compatible.
|
||||||
|
|
||||||
|
Plugins will also typically expose extension points. The preferred
|
||||||
|
way to do this is to expose `Provider` instances as public fields
|
||||||
|
of plugins, but this is a matter of code style, and is not enforced
|
||||||
|
or expected by the Registration API.
|
||||||
|
|
||||||
|
For example, if a plugin depends on the `core` plugin of MCT:
|
||||||
|
|
||||||
|
```
|
||||||
|
define(['mct', './SomeAction'], function (mct, SomeAction) {
|
||||||
|
var Plugin = mct.Plugin,
|
||||||
|
ServiceProvider = mct.ServiceProvider;
|
||||||
|
|
||||||
|
function ExamplePlugin(core) {
|
||||||
|
Plugin.call(this, function () {
|
||||||
|
core.actionRegistry.register(function () {
|
||||||
|
return new SomeAction();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.someServiceProvider = new ServiceProvider();
|
||||||
|
this.someServiceProvider.register(function () {
|
||||||
|
return new SomeService();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ExamplePlugin.prototype = Object.create(Plugin.prototype);
|
||||||
|
|
||||||
|
return ExamplePlugin;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Using this plugin then looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
define([
|
||||||
|
'mct',
|
||||||
|
'./ExamplePlugin',
|
||||||
|
'./OtherPlugin'
|
||||||
|
], function (mct, ExamplePlugin, OtherPlugin) {
|
||||||
|
var MCT = mct.MCT;
|
||||||
|
|
||||||
|
function MyApplication() {
|
||||||
|
MCT.call(this);
|
||||||
|
|
||||||
|
this.examplePlugin = new ExamplePlugin(this.core);
|
||||||
|
this.otherPlugin = new OtherPlugin(this.examplePlugin);
|
||||||
|
|
||||||
|
this.install(this.examplePlugin);
|
||||||
|
this.install(this.otherPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
MyApplication.prototype = Object.create(MCT.prototype);
|
||||||
|
|
||||||
|
return MyApplication;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Extension Points
|
||||||
|
|
||||||
|
The services and extensions exposed by providers are retrieved via
|
||||||
|
`get` calls to those providers. These calls are expected to occur
|
||||||
|
during registration of other extensions, _or_ in the `start` phase
|
||||||
|
of the plugin lifecycle.
|
||||||
|
|
||||||
|
There are effectively four distinct stages for a plugin:
|
||||||
|
|
||||||
|
* __Pre-initialization__. This is the plugin immediately after its
|
||||||
|
constructor call. Any extension points exposed by the plugin are
|
||||||
|
expected to be defined during this stage.
|
||||||
|
* __Initialization__. Triggered by calling `initialize`; invokes
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
define(['mct', './SomeAction'], function (mct, SomeAction) {
|
||||||
|
var Plugin = mct.Plugin;
|
||||||
|
|
||||||
|
function MyPlugin(core, notificationPlugin) {
|
||||||
|
var notificationServiceProvider =
|
||||||
|
notificationPlugin.notificationServiceProvider;
|
||||||
|
|
||||||
|
Plugin.call(this, function initialize() {
|
||||||
|
// During this stage, extensions may be installed and other
|
||||||
|
// general plugin configuration should occur.
|
||||||
|
// Calls to get should be avoided at this stage, as
|
||||||
|
// providers may not be fully configured.
|
||||||
|
core.actionRegistry.register(function () {
|
||||||
|
// In factory functions, however, get calls are expected;
|
||||||
|
// this is when dependencies actually get injected.
|
||||||
|
// Calls which register/configure extensions should be
|
||||||
|
// avoided at this point.
|
||||||
|
return new SomeAction(notificationServiceProvider.get());
|
||||||
|
});
|
||||||
|
}, function start() {
|
||||||
|
// Any behavior that should occur when the application starts.
|
||||||
|
// All providers should be fully-configured at this point; `get`
|
||||||
|
// calls may be issued freely at this point, and no more
|
||||||
|
// registration should occur. This stage is not useful to most
|
||||||
|
// plugins and this argument would typically be omitted.
|
||||||
|
|
||||||
|
notifications.notificationServiceProvider.get()
|
||||||
|
.notify("Hello world!");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Code in the constructor is run when the plugin is instantiated;
|
||||||
|
// any extension points exposed by the plugin should be declared
|
||||||
|
// here, typically as providers.
|
||||||
|
this.someRegistry = new mct.Registry();
|
||||||
|
}
|
||||||
|
|
||||||
|
MyPlugin.prototype = Object.create(Plugin.prototype);
|
||||||
|
|
||||||
|
return MyPlugin;
|
||||||
|
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
# Evaluation
|
||||||
|
|
||||||
|
[Identified problems](#problems-to-address) are addressed by this solution:
|
||||||
|
|
||||||
|
1. Dependencies between plugins can be made explicit; a `Plugin` may
|
||||||
|
impose dependencies on other specific `Plugin` subclasses as constructor
|
||||||
|
arguments, disambiguated with JSDoc. The software does not take part
|
||||||
|
in dependency management among plugins; rather, this responsibility is
|
||||||
|
plainly communicated to developers.
|
||||||
|
2. Extension points are made explicit; `Provider` instances must be
|
||||||
|
reachable for plugins to configure, and may be made available as
|
||||||
|
public fields of `Plugin`s. Their types can be clearly documented,
|
||||||
|
usages and interactions can be followed with standard developer tools
|
||||||
|
(e.g. breakpoints), and so on.
|
||||||
|
3. Reuse of classes between plugins is neither facilitated nor impeded
|
||||||
|
by the registration API. If, however, plugins are written following the
|
||||||
|
"expose classes in namespaces" approach, then it is trivially to
|
||||||
|
expose additional classes in these same namespaces.
|
||||||
|
|
||||||
|
This solution offers further benefits:
|
||||||
|
|
||||||
|
* The `Provider` API is robust enough to support the various existing
|
||||||
|
extensions of Open MCT, but its usage is opt-in; plugins are free
|
||||||
|
to expose other (potentially wildly different) means of extension.
|
||||||
|
Usage of `Provider`s is _encouraged_ to promote ubiquitous
|
||||||
|
extensibility, but no limitation is exposed.
|
||||||
|
* By moving everything into classes which accept dependencies, a
|
||||||
|
degree of inflexibility is removed from the architecture. In principle,
|
||||||
|
it should be possible to run multiple instances of `MCT` (with
|
||||||
|
their own service instances, etc.) within the same environment. While
|
||||||
|
this is not specifically desirable, it reflects a generally looser
|
||||||
|
coupling between the software and it environment (no expectation of a
|
||||||
|
`bundles.json`, no usage of global state at the language level or
|
||||||
|
effectively-global state at the RequireJS level, etc.) and implies
|
||||||
|
greater flexibility of the application's components.
|
||||||
|
|
||||||
|
There are some problems with this approach:
|
||||||
|
|
||||||
|
* It is highly sensitive to ordering; does not address the problem of
|
||||||
|
[separating configuration from use](http://www.martinfowler.com/articles/injection.html#SeparatingConfigurationFromUse),
|
||||||
|
but instead leaves this as a problem to solve with code style
|
||||||
|
(requiring familiarity with the system.) This is particularly
|
||||||
|
true with `Provider#get` (don't want to invoke before configuration
|
||||||
|
is finished), but also true for `Application` and `Plugin`.
|
||||||
|
* One approach to mitigate this would be to throw `Error`s when
|
||||||
|
calls are made out-of-order (e.g. configuration after use.)
|
||||||
|
* Some redundancy among interfaces (`Plugin` and `Application` both
|
||||||
|
look a lot like a `Provider`, but of what?)
|
||||||
|
* But, don't want to over-exercise commonalities and end up with
|
||||||
|
unclear interfaces.
|
28
src/mct/Application.js
Normal file
28
src/mct/Application.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
define([], function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function Application(plugins) {
|
||||||
|
this.plugins = plugins || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.prototype.install = function (plugin) {
|
||||||
|
this.plugins.push(plugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
Application.prototype.uninstall = function (plugin) {
|
||||||
|
this.plugins = this.plugins.filter(function (p ) {
|
||||||
|
return p !== plugin;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Application.prototype.run = function () {
|
||||||
|
this.plugins.forEach(function (plugin) {
|
||||||
|
plugin.initialize();
|
||||||
|
});
|
||||||
|
this.plugins.forEach(function (plugin) {
|
||||||
|
plugin.activate();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Application;
|
||||||
|
});
|
38
src/mct/LegacyPlugin.js
Normal file
38
src/mct/LegacyPlugin.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
define(['./Plugin'], function (Plugin) {
|
||||||
|
function LegacyPlugin(
|
||||||
|
bundleDefinition,
|
||||||
|
registryLocator,
|
||||||
|
serviceLocator
|
||||||
|
) {
|
||||||
|
function resolve(extension) {
|
||||||
|
var depends = extension.depends || [],
|
||||||
|
dependencies = depends.map(function (name) {
|
||||||
|
return serviceLocator.locate(name);
|
||||||
|
});
|
||||||
|
return extension.implementation ?
|
||||||
|
_.spread(_.partial)(
|
||||||
|
[extension.implementation].concat(dependencies)
|
||||||
|
) : extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializer() {
|
||||||
|
var extensions = bundleDefinition.extensions || {};
|
||||||
|
|
||||||
|
Object.keys(extensions).forEach(function (category) {
|
||||||
|
var registry = registryLocator.locate(category);
|
||||||
|
|
||||||
|
function register(extension) {
|
||||||
|
registry.register(function () {
|
||||||
|
return resolve(extension);
|
||||||
|
}, { priority: extension.priority });
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions[category].forEach(extension);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin.call(this, initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LegacyPlugin;
|
||||||
|
});
|
10
src/mct/Plugin.js
Normal file
10
src/mct/Plugin.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
define(['lodash'], function (_) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function Plugin(initializer, activator) {
|
||||||
|
this.initialize = _.once(initializer || _.noop);
|
||||||
|
this.activate = _.once(activator || _.noop);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Plugin;
|
||||||
|
});
|
34
src/mct/Provider.js
Normal file
34
src/mct/Provider.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
define(['lodash'], function (_) {
|
||||||
|
function Provider(compositor) {
|
||||||
|
function invoke(factory) {
|
||||||
|
return factory();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.compositor = compositor;
|
||||||
|
this.instantiated = false;
|
||||||
|
this.factories = [];
|
||||||
|
this.decorators = [];
|
||||||
|
|
||||||
|
this.get = _.memoize(function () {
|
||||||
|
return this.decorators.reduce(function (instance, decorator) {
|
||||||
|
return decorator(instance);
|
||||||
|
}, this.compositor(this.factories.map(function (factory) {
|
||||||
|
return factory();
|
||||||
|
})));
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
Provider.prototype.register = function (factory, options) {
|
||||||
|
this.factories.push(factory);
|
||||||
|
};
|
||||||
|
|
||||||
|
Provider.prototype.decorate = function (decorator, options) {
|
||||||
|
this.decorators.push(decorator);
|
||||||
|
};
|
||||||
|
|
||||||
|
Provider.prototype.compose = function (compositor, options) {
|
||||||
|
this.compositor = compositor;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Provider;
|
||||||
|
});
|
9
src/mct/Registry.js
Normal file
9
src/mct/Registry.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
define(['./Provider', 'lodash'], function (Provider, _) {
|
||||||
|
function Registry() {
|
||||||
|
Provider.call(this, _.identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
Registry.prototype = Object.create(Provider.prototype);
|
||||||
|
|
||||||
|
return Registry;
|
||||||
|
});
|
9
src/mct/ServiceProvider.js
Normal file
9
src/mct/ServiceProvider.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
define(['./Provider', 'lodash'], function (Provider, _) {
|
||||||
|
function ServiceProvider(compositor) {
|
||||||
|
Provider.call(this, compositor || _.head);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceProvider.prototype = Object.create(Provider.prototype);
|
||||||
|
|
||||||
|
return ServiceProvider;
|
||||||
|
});
|
Reference in New Issue
Block a user