Add push$ to Collection; make Container listen for retroactive blueprint changes
This commit is contained in:
parent
a173393697
commit
f1791b1d76
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@extollo/lib",
|
||||
"version": "0.14.2",
|
||||
"version": "0.14.3",
|
||||
"description": "The framework library that lifts up your code.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
Collection,
|
||||
globalRegistry,
|
||||
hasOwnProperty,
|
||||
logIfDebugging,
|
||||
logIfDebugging, Unsubscribe,
|
||||
} from '../util'
|
||||
import {ErrorWithContext, withErrorContext} from '../util/error/ErrorWithContext'
|
||||
import {Factory} from './factory/Factory'
|
||||
@ -100,6 +100,8 @@ export class Container {
|
||||
.then(value => listener.callback(value))
|
||||
})
|
||||
|
||||
container.subscribeToBlueprintChanges(ContainerBlueprint.getContainerBlueprint())
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
@ -153,11 +155,33 @@ export class Container {
|
||||
*/
|
||||
protected waitingLifecycleCallbacks: Collection<WeakRef<AwareOfContainerLifecycle>> = new Collection()
|
||||
|
||||
/**
|
||||
* Collection of subscriptions to ContainerBlueprint events.
|
||||
* We keep this around so we can remove the subscriptions when the container is destroyed.
|
||||
* @protected
|
||||
*/
|
||||
protected blueprintSubscribers: Collection<Unsubscribe> = new Collection()
|
||||
|
||||
constructor() {
|
||||
this.registerSingletonInstance<Container>(Container, this)
|
||||
this.registerSingleton('injector', this)
|
||||
}
|
||||
|
||||
/** Make the container listen to changes in the given blueprint. */
|
||||
private subscribeToBlueprintChanges(blueprint: ContainerBlueprint): void {
|
||||
this.blueprintSubscribers.push(
|
||||
blueprint.resolve$(factory => this.registerFactory(factory())),
|
||||
)
|
||||
|
||||
this.blueprintSubscribers.push(
|
||||
blueprint.resolveConstructable$(factoryClass => this.registerFactory(this.make(factoryClass))),
|
||||
)
|
||||
|
||||
this.blueprintSubscribers.push(
|
||||
blueprint.resolveResolutionCallbacks$(listener => this.onResolve(listener.key).then(value => listener.callback(value))),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all factories and instances of the given key from this container.
|
||||
* @param key
|
||||
@ -622,6 +646,8 @@ export class Container {
|
||||
* Perform any cleanup necessary to destroy this container instance.
|
||||
*/
|
||||
destroy(): void {
|
||||
this.blueprintSubscribers.mapCall('unsubscribe')
|
||||
|
||||
this.waitingLifecycleCallbacks
|
||||
.mapCall('deref')
|
||||
.whereDefined()
|
||||
|
@ -3,6 +3,8 @@ import NamedFactory from './factory/NamedFactory'
|
||||
import {AbstractFactory} from './factory/AbstractFactory'
|
||||
import {Factory} from './factory/Factory'
|
||||
import {ClosureFactory} from './factory/ClosureFactory'
|
||||
import {Collection, collect} from '../util/collection/Collection'
|
||||
import {Subscription, Unsubscribe} from '../util/support/BehaviorSubject'
|
||||
|
||||
/** Simple type alias for a callback to a container's onResolve method. */
|
||||
export type ContainerResolutionCallback<T> = (() => unknown) | ((t: T) => unknown)
|
||||
@ -25,11 +27,11 @@ export class ContainerBlueprint {
|
||||
return this.instance
|
||||
}
|
||||
|
||||
protected factories: (() => AbstractFactory<any>)[] = []
|
||||
protected factories: Collection<(() => AbstractFactory<any>)> = collect()
|
||||
|
||||
protected constructableFactories: StaticClass<AbstractFactory<any>, any>[] = []
|
||||
protected constructableFactories: Collection<StaticClass<AbstractFactory<any>, any>> = collect()
|
||||
|
||||
protected resolutionCallbacks: ({key: TypedDependencyKey<any>, callback: ContainerResolutionCallback<any>})[] = []
|
||||
protected resolutionCallbacks: Collection<{key: TypedDependencyKey<any>, callback: ContainerResolutionCallback<any>}> = collect()
|
||||
|
||||
/**
|
||||
* Register some factory class with the container. Should take no construction params.
|
||||
@ -74,7 +76,16 @@ export class ContainerBlueprint {
|
||||
* Get an array of factory instances in the blueprint.
|
||||
*/
|
||||
resolve(): AbstractFactory<any>[] {
|
||||
return this.factories.map(x => x())
|
||||
return this.factories.map(x => x()).all()
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to new factories being registered.
|
||||
* Used by `Container` implementations to listen for factories being registered after the container is realized.
|
||||
* @param sub
|
||||
*/
|
||||
resolve$(sub: Subscription<() => AbstractFactory<any>>): Unsubscribe {
|
||||
return this.factories.push$(sub)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,14 +105,32 @@ export class ContainerBlueprint {
|
||||
* Get an array of static Factory classes that need to be instantiated by
|
||||
* the container itself.
|
||||
*/
|
||||
resolveConstructable(): StaticClass<AbstractFactory<any>, any> {
|
||||
return [...this.constructableFactories]
|
||||
resolveConstructable(): StaticClass<AbstractFactory<any>, any>[] {
|
||||
return this.constructableFactories.all()
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to new constructable factories being registered.
|
||||
* Used by `Container` implementations to listen for factories registered after the container is realized.
|
||||
* @param sub
|
||||
*/
|
||||
resolveConstructable$(sub: Subscription<StaticClass<AbstractFactory<any>, any>>): Unsubscribe {
|
||||
return this.constructableFactories.push$(sub)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of DependencyKey-callback pairs to register with new containers.
|
||||
*/
|
||||
resolveResolutionCallbacks(): ({key: TypedDependencyKey<any>, callback: ContainerResolutionCallback<any>})[] {
|
||||
return [...this.resolutionCallbacks]
|
||||
return this.resolutionCallbacks.all()
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to new resolution callbacks being registered.
|
||||
* Used by `Container` implementations to listen for callbacks registered after the container is realized.
|
||||
* @param sub
|
||||
*/
|
||||
resolveResolutionCallbacks$(sub: Subscription<{key: TypedDependencyKey<any>, callback: ContainerResolutionCallback<any>}>): Unsubscribe {
|
||||
return this.resolutionCallbacks.push$(sub)
|
||||
}
|
||||
}
|
||||
|
@ -166,6 +166,7 @@ export const Singleton = (name?: string): ClassDecorator => {
|
||||
...(name ? { name } : {}),
|
||||
}
|
||||
|
||||
logIfDebugging('extollo.di.singleton', 'Registering singleton target:', target, 'injectionType:', injectionType)
|
||||
Reflect.defineMetadata(DEPENDENCY_KEYS_SERVICE_TYPE_KEY, injectionType, target)
|
||||
Injectable()(target)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {AsyncPipe, Pipeline} from '../support/Pipe'
|
||||
import {Unsubscribe, Subscription} from '../support/BehaviorSubject'
|
||||
|
||||
type CollectionItem<T> = T
|
||||
type MaybeCollectionItem<T> = CollectionItem<T> | undefined
|
||||
@ -50,6 +51,8 @@ export {
|
||||
class Collection<T> {
|
||||
private storedItems: CollectionItem<T>[] = []
|
||||
|
||||
private pushSubscribers: Subscription<T>[] = []
|
||||
|
||||
/**
|
||||
* Create a new collection from an array of items.
|
||||
* @param items
|
||||
@ -966,6 +969,7 @@ class Collection<T> {
|
||||
*/
|
||||
prepend(item: CollectionItem<T>): Collection<T> {
|
||||
this.storedItems = [item, ...this.storedItems]
|
||||
this.callPushSubscribers(item)
|
||||
return this
|
||||
}
|
||||
|
||||
@ -975,9 +979,33 @@ class Collection<T> {
|
||||
*/
|
||||
push(item: CollectionItem<T>): Collection<T> {
|
||||
this.storedItems.push(item)
|
||||
this.callPushSubscribers(item)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to listen for items being added to the collection.
|
||||
* @param sub
|
||||
*/
|
||||
push$(sub: Subscription<T>): Unsubscribe {
|
||||
this.pushSubscribers.push(sub)
|
||||
return {
|
||||
unsubscribe: () => this.pushSubscribers = this.pushSubscribers.filter(x => x !== sub),
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper to notify subscribers that an item has been pushed to the collection. */
|
||||
private callPushSubscribers(item: T): void {
|
||||
this.pushSubscribers
|
||||
.forEach(sub => {
|
||||
if ( typeof sub === 'object' ) {
|
||||
sub?.next?.(item)
|
||||
} else {
|
||||
sub(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the given items to the end of this collection.
|
||||
* Unlike `merge()`, this mutates the current collection's items.
|
||||
@ -987,6 +1015,7 @@ class Collection<T> {
|
||||
const concats = items instanceof Collection ? items.all() : items
|
||||
for ( const item of concats ) {
|
||||
this.storedItems.push(item)
|
||||
this.callPushSubscribers(item)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user