Containers - add ability to purge/release factories; override factories in scoped

This commit is contained in:
Garrett Mills 2021-06-04 01:03:10 -05:00
parent cd9bec7c5e
commit dab3d006c8
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
2 changed files with 100 additions and 5 deletions

View File

@ -47,6 +47,25 @@ export class Container {
this.registerSingleton('injector', this) this.registerSingleton('injector', this)
} }
/**
* Purge all factories and instances of the given key from this container.
* @param key
*/
purge(key: DependencyKey): this {
this.factories = this.factories.filter(x => !x.match(key))
this.release(key)
return this
}
/**
* Remove all stored instances of the given key from this container.
* @param key
*/
release(key: DependencyKey): this {
this.instances = this.instances.filter(x => x.key !== key)
return this
}
/** /**
* Register a basic instantiable class as a standard Factory with this container. * Register a basic instantiable class as a standard Factory with this container.
* @param {Instantiable} dependency * @param {Instantiable} dependency

View File

@ -1,5 +1,6 @@
import {Container, MaybeDependency, MaybeFactory} from './Container' import {Container, MaybeDependency, MaybeFactory} from './Container'
import {DependencyKey} from './types' import {DependencyKey, Instantiable} from './types'
import {AbstractFactory} from './factory/AbstractFactory'
/** /**
* A container that uses some parent container as a base, but * A container that uses some parent container as a base, but
@ -30,6 +31,8 @@ export class ScopedContainer extends Container {
return new ScopedContainer(container) return new ScopedContainer(container)
} }
private resolveParentScope = true
constructor( constructor(
private parentContainer: Container, private parentContainer: Container,
) { ) {
@ -38,11 +41,11 @@ export class ScopedContainer extends Container {
} }
hasInstance(key: DependencyKey): boolean { hasInstance(key: DependencyKey): boolean {
return super.hasInstance(key) || this.parentContainer.hasInstance(key) return super.hasInstance(key) || (this.resolveParentScope && this.parentContainer.hasInstance(key))
} }
hasKey(key: DependencyKey): boolean { hasKey(key: DependencyKey): boolean {
return super.hasKey(key) || this.parentContainer.hasKey(key) return super.hasKey(key) || (this.resolveParentScope && this.parentContainer.hasKey(key))
} }
getExistingInstance(key: DependencyKey): MaybeDependency { getExistingInstance(key: DependencyKey): MaybeDependency {
@ -51,7 +54,9 @@ export class ScopedContainer extends Container {
return inst return inst
} }
return this.parentContainer.getExistingInstance(key) if ( this.resolveParentScope ) {
return this.parentContainer.getExistingInstance(key)
}
} }
resolve(key: DependencyKey): MaybeFactory<any> { resolve(key: DependencyKey): MaybeFactory<any> {
@ -60,6 +65,77 @@ export class ScopedContainer extends Container {
return factory return factory
} }
return this.parentContainer?.resolve(key) if ( this.resolveParentScope ) {
return this.parentContainer.resolve(key)
}
}
/**
* Register a basic instantiable class as a standard Factory with this container.
* @param {Instantiable} dependency
*/
register(dependency: Instantiable<any>): this {
return this.withoutParentScopes(() => super.register(dependency))
}
/**
* Register the given function as a factory within the container.
* @param {string} name - unique name to identify the factory in the container
* @param {function} producer - factory to produce a value
*/
registerProducer(name: DependencyKey, producer: () => any): this {
return this.withoutParentScopes(() => super.registerProducer(name, producer))
}
/**
* Register a basic instantiable class as a standard Factory with this container,
* identified by a string name rather than static class.
* @param {string} name - unique name to identify the factory in the container
* @param {Instantiable} dependency
*/
registerNamed(name: string, dependency: Instantiable<any>): this {
return this.withoutParentScopes(() => super.registerNamed(name, dependency))
}
/**
* Register a value as a singleton in the container. It will not be instantiated, but
* can be injected by its unique name.
* @param {string} key - unique name to identify the singleton in the container
* @param value
*/
registerSingleton<T>(key: DependencyKey, value: T): this {
return this.withoutParentScopes(() => super.registerSingleton(key, value))
}
/**
* Register a static class to the container along with its already-instantiated
* instance that will be used to resolve the class.
* @param staticClass
* @param instance
*/
registerSingletonInstance<T>(staticClass: Instantiable<T>, instance: T): this {
return this.withoutParentScopes(() => super.registerSingletonInstance(staticClass, instance))
}
/**
* Register a given factory with the container.
* @param {AbstractFactory} factory
*/
registerFactory(factory: AbstractFactory<unknown>): this {
return this.withoutParentScopes(() => super.registerFactory(factory))
}
/**
* Execute a closure on this container, disabling parent-resolution.
* Effectively, the closure will have access to this container as if
* it were NOT a scoped container, and only contained its factories.
* @param closure
*/
withoutParentScopes<T>(closure: () => T): T {
const oldResolveParentScope = this.resolveParentScope
this.resolveParentScope = false
const value: T = closure()
this.resolveParentScope = oldResolveParentScope
return value
} }
} }