/**
* @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