Centralize configure-able factory classes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import 'reflect-metadata'
|
||||
import {collect, Collection} from '../../util'
|
||||
import {collect, Collection} from '../../util/collection/Collection'
|
||||
import {logIfDebugging} from '../../util/support/debug'
|
||||
import {
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {DependencyKey, DependencyRequirement, PropertyDependency} from '../types'
|
||||
import { Collection } from '../../util'
|
||||
import {DependencyKey, DependencyRequirement, Instantiable, PropertyDependency} from '../types'
|
||||
import {Collection, logIfDebugging} from '../../util'
|
||||
import {getPropertyInjectionMetadata} from '../decorator/getPropertyInjectionMetadata'
|
||||
|
||||
/**
|
||||
* Abstract base class for dependency container factories.
|
||||
@@ -42,6 +43,22 @@ export abstract class AbstractFactory<T> {
|
||||
*/
|
||||
abstract getInjectedProperties(): Collection<PropertyDependency>
|
||||
|
||||
/** Helper method that returns all `@Inject()`'ed properties for a token and its prototypical ancestors. */
|
||||
protected getInjectedPropertiesForPrototypeChain(token: Instantiable<any>): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(token)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
token = Object.getPrototypeOf(token)
|
||||
logIfDebugging('extollo.di.injection', 'next currentToken:', token)
|
||||
} while (token !== Function.prototype && token !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a human-readable name of the token this factory produces.
|
||||
* This is meant for debugging output only.
|
||||
|
||||
84
src/di/factory/ConfiguredSingletonFactory.ts
Normal file
84
src/di/factory/ConfiguredSingletonFactory.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {AbstractFactory} from './AbstractFactory'
|
||||
import {Inject, Injectable} from '../decorator/injection'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Config} from '../../service/Config'
|
||||
import {
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
DependencyRequirement,
|
||||
Instantiable,
|
||||
isInstantiable,
|
||||
PropertyDependency,
|
||||
} from '../types'
|
||||
import {Collection, ErrorWithContext, Maybe} from '../../util'
|
||||
import 'reflect-metadata'
|
||||
|
||||
@Injectable()
|
||||
export abstract class ConfiguredSingletonFactory<T> extends AbstractFactory<T> {
|
||||
protected static loggedDefaultImplementationWarningOnce = false
|
||||
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
@Inject()
|
||||
protected readonly config!: Config
|
||||
|
||||
constructor() {
|
||||
super({})
|
||||
}
|
||||
|
||||
protected abstract getConfigKey(): string
|
||||
|
||||
protected abstract getDefaultImplementation(): Instantiable<T>
|
||||
|
||||
protected abstract getAbstractImplementation(): any
|
||||
|
||||
protected getDefaultImplementationWarning(): Maybe<string> {
|
||||
return undefined
|
||||
}
|
||||
|
||||
produce(dependencies: any[], parameters: any[]): T {
|
||||
return new (this.getImplementation())(...dependencies, ...parameters)
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === this.getAbstractImplementation()
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getImplementation())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
return this.getInjectedPropertiesForPrototypeChain(this.getImplementation())
|
||||
}
|
||||
|
||||
protected getImplementation(): Instantiable<T> {
|
||||
const ctor = this.constructor as typeof ConfiguredSingletonFactory
|
||||
const ImplementationClass = this.config.get(this.getConfigKey(), this.getDefaultImplementation())
|
||||
if ( ImplementationClass === this.getDefaultImplementation() ) {
|
||||
const warning = this.getDefaultImplementationWarning()
|
||||
if ( warning && !ctor.loggedDefaultImplementationWarningOnce ) {
|
||||
this.logging.warn(warning)
|
||||
ctor.loggedDefaultImplementationWarningOnce = true
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!isInstantiable(ImplementationClass)
|
||||
|| !(ImplementationClass.prototype instanceof this.getAbstractImplementation())
|
||||
) {
|
||||
throw new ErrorWithContext('Configured service clas does not properly extend from implementation base class.', {
|
||||
configKey: this.getConfigKey(),
|
||||
class: `${ImplementationClass}`,
|
||||
mustExtendBase: `${this.getAbstractImplementation()}`,
|
||||
})
|
||||
}
|
||||
|
||||
return ImplementationClass as Instantiable<T>
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
Instantiable,
|
||||
PropertyDependency,
|
||||
} from '../types'
|
||||
import {Collection, logIfDebugging} from '../../util'
|
||||
import {Collection} from '../../util'
|
||||
import 'reflect-metadata'
|
||||
import {getPropertyInjectionMetadata} from '../decorator/getPropertyInjectionMetadata'
|
||||
|
||||
/**
|
||||
* Standard static-class factory. The token of this factory is a reference to a
|
||||
@@ -53,18 +52,6 @@ export class Factory<T> extends AbstractFactory<T> {
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.token
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
logIfDebugging('extollo.di.injection', 'Factory.getInjectedProperties() target:', currentToken, 'loaded:', loadedMeta)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
return this.getInjectedPropertiesForPrototypeChain(this.token)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ export * from './factory/Factory'
|
||||
export * from './factory/NamedFactory'
|
||||
export * from './factory/SingletonFactory'
|
||||
|
||||
export * from './types'
|
||||
export * from './ContainerBlueprint'
|
||||
export * from './decorator/getPropertyInjectionMetadata'
|
||||
export * from './decorator/injection'
|
||||
|
||||
export * from './Container'
|
||||
export * from './ScopedContainer'
|
||||
export * from './types'
|
||||
|
||||
export * from './decorator/injection'
|
||||
export * from './decorator/getPropertyInjectionMetadata'
|
||||
export * from './InjectionAware'
|
||||
export * from './constructable'
|
||||
|
||||
Reference in New Issue
Block a user