You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.6 KiB
111 lines
3.6 KiB
/**
|
|
* @module flitter-di/src/Injectable
|
|
*/
|
|
|
|
/** Base class for classes that support service injection. */
|
|
class Injectable {
|
|
/**
|
|
* If true, the injector will defer the class if the class
|
|
* requests any services that the container is missing. These
|
|
* services are filled in later and added to the prototype and
|
|
* any instances. True by default.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
static _di_allow_defer = true
|
|
|
|
/**
|
|
* List of services that were deferred and not provided at the time of injection.
|
|
* @type {Array<string>}
|
|
* @private
|
|
*/
|
|
static _di_deferred_services = []
|
|
|
|
/**
|
|
* Collection of instances of this class that need to have the deferred service
|
|
* instances injected into them when the deferred services are finally provided.
|
|
* @type {Array<module:flitter-di/src/Injectable~Injectable>}
|
|
* @private
|
|
*/
|
|
static _di_deferred_instances = []
|
|
|
|
/**
|
|
* Get the names of services required by this class.
|
|
* @returns {Array<string>}
|
|
*/
|
|
static get services() {
|
|
return []
|
|
}
|
|
|
|
/**
|
|
* Checks if the class has any missing deferred services.
|
|
* @returns {boolean} - true if there are deferred services
|
|
* @private
|
|
*/
|
|
static get __has_deferred_services() {
|
|
return this._di_deferred_services.length > 0
|
|
}
|
|
|
|
/**
|
|
* Inject the class with services from the specified container. These
|
|
* services are injected directly into this class' prototype. If deferral
|
|
* is enabled, services not in the container will be stored and the class
|
|
* will be deferred.
|
|
* @param {module:flitter-di/src/Container~Container} container
|
|
* @private
|
|
*/
|
|
static __inject(container) {
|
|
this.services.forEach(name => {
|
|
if ( !this._di_allow_defer ) {
|
|
// If this class' services aren't deferrable, then just fetch their instances
|
|
this.prototype[name] = container.get(name)
|
|
} else {
|
|
// Otherwise, grab them if they exist, or put in deferral requests
|
|
if ( container.has(name) ) {
|
|
this.prototype[name] = container.get(name)
|
|
} else {
|
|
this._di_deferred_services.push(name)
|
|
}
|
|
}
|
|
})
|
|
|
|
if ( this._di_allow_defer && this.__has_deferred_services ) {
|
|
container.defer(this)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when a deferred service is registered with the container.
|
|
* Injects the missing service into the prototype and any instances of this
|
|
* class.
|
|
* @param {string} service_name - the deferred service name
|
|
* @param {module:flitter-di/src/Service~Service} service_instance - the instance of the service
|
|
* @private
|
|
*/
|
|
static __deferral_callback(service_name, service_instance) {
|
|
if ( this._di_deferred_services.includes(service_name) ) {
|
|
for ( const instance of this._di_deferred_instances ) {
|
|
instance[service_name] = service_instance
|
|
}
|
|
|
|
this.prototype[service_name] = service_instance
|
|
|
|
this._di_deferred_services = this._di_deferred_services.filter(x => x !== service_name)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Instantiates the class. If deferral is enabled and the static class
|
|
* has deferred services, register this instance as needing to be injected
|
|
* with those services when they become available.
|
|
*/
|
|
constructor() {
|
|
const Class = this.constructor
|
|
if ( Class._di_allow_defer && Class.__has_deferred_services ) {
|
|
Class._di_deferred_instances.push(this)
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = exports = Injectable
|