Commit 4d210869 authored by Robert Knight's avatar Robert Knight

Use `Injector` to run initialization functions

Replace Angular's `run` function which is used to run initialization
logic once all services are constructed with a new `Injector#run`
method.

This allows us to remove the dependency on Angular for running app
initialization logic which requires access to service instances. As a
result, services that are not used by remaining Angular components no
longer need to be registered with Angular at all.
parent d8157154
......@@ -34,7 +34,11 @@ function isValidProvider(provider) {
*
* To construct an object, call the `register` method with the name and provider
* for the object and each of its dependencies, and then call
* the `get` method to construct the object and its dependencies return it.
* the `get` method to construct the object and its dependencies and return it.
*
* To run a function with arguments provided by the container, without registering
* the function in the container for use by other factories or classes,
* use the `run` method.
*/
export class Injector {
constructor() {
......@@ -134,4 +138,25 @@ export class Injector {
this._providers.set(name, provider);
return this;
}
/**
* Run a function which uses one or more dependencies provided by the
* container.
*
* @param {Function} -
* A callback to run, with dependencies annotated in the same way as
* functions or classes passed to `register`.
* @return {any} - Returns the result of running the function.
*/
run(callback) {
const tempName = 'Injector.run';
this.register(tempName, { factory: callback });
try {
return this.get(tempName);
} finally {
this._instances.delete(tempName);
this._providers.delete(tempName);
}
}
}
......@@ -153,4 +153,52 @@ describe('Injector', () => {
});
});
});
describe('#run', () => {
const FIRST_VALUE = 3;
const SECOND_VALUE = 5;
function createContainer() {
const container = new Injector();
container
.register('first', { value: FIRST_VALUE })
.register('second', { value: SECOND_VALUE });
return container;
}
it('calls function with resolved dependencies as arguments', () => {
const stub = sinon.stub();
stub.$inject = ['first', 'first', 'second'];
const container = createContainer();
container.run(stub);
assert.calledWith(stub, FIRST_VALUE, FIRST_VALUE, SECOND_VALUE);
});
it("returns the function's result", () => {
function add(first, second) {
return first + second;
}
add.$inject = ['first', 'second'];
const container = createContainer();
const result = container.run(add);
assert.equal(result, FIRST_VALUE + SECOND_VALUE);
});
it('can be called multiple times', () => {
const container = createContainer();
let total = 0;
function increment(first) {
total += first;
}
container.run(increment);
container.run(increment);
assert.equal(total, FIRST_VALUE * 2);
});
});
});
......@@ -231,6 +231,18 @@ function startAngularApp(config) {
.register('$rootScope', { value: $rootScope });
}
// Run initialization logic that uses constructed services.
//
// @ngInject
function initServices() {
container.run(persistDefaults);
container.run(autosave);
container.run(sendPageView);
container.run(setupApi);
container.run(setupRoute);
container.run(startRPCServer);
}
const wrapComponent = component => wrapReactComponent(component, container);
angular
......@@ -262,10 +274,7 @@ function startAngularApp(config) {
// Register services, the store and utilities with Angular, so that
// Angular components can use them.
.service('analytics', () => container.get('analytics'))
.service('annotationsService', () => container.get('annotationsService'))
.service('api', () => container.get('api'))
.service('auth', () => container.get('auth'))
.service('autosaveService', () => container.get('autosaveService'))
.service('bridge', () => container.get('bridge'))
.service('features', () => container.get('features'))
.service('flash', () => container.get('flash'))
......@@ -274,16 +283,12 @@ function startAngularApp(config) {
.service('loadAnnotationsService', () =>
container.get('loadAnnotationsService')
)
.service('persistedDefaults', () => container.get('persistedDefaults'))
.service('rootThread', () => container.get('rootThread'))
.service('router', () => container.get('router'))
.service('searchFilter', () => container.get('searchFilter'))
.service('serviceUrl', () => container.get('serviceUrl'))
.service('session', () => container.get('session'))
.service('streamer', () => container.get('streamer'))
.service('streamFilter', () => container.get('streamFilter'))
.service('threadsService', () => container.get('threadsService'))
.service('toastMessenger', () => container.get('toastMessenger'))
// Redux store
.service('store', () => container.get('store'))
......@@ -296,13 +301,7 @@ function startAngularApp(config) {
// Make Angular built-ins available to services constructed by `container`.
.run(registerAngularServices)
.run(persistDefaults)
.run(autosave)
.run(sendPageView)
.run(setupApi)
.run(setupRoute)
.run(startRPCServer);
.run(initServices);
// Work around a check in Angular's $sniffer service that causes it to
// incorrectly determine that Firefox extensions are Chrome Packaged Apps which
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment