finalize hook swap, actually delete from hook list

This commit is contained in:
Jacob Nguyen
2024-06-13 10:59:40 -05:00
parent 2ffad764a3
commit 0a13cd4418
2 changed files with 48 additions and 19 deletions

View File

@@ -11,13 +11,14 @@ function hasCallableMethod(obj: object, name: PropertyKey) {
*/
export class Container {
private __singletons = new Map<PropertyKey, any>();
private hooks= new Map<string, Function[]>();
//hooks are Maps of string -> object, where object is a reference to an object in __singletons
private hooks= new Map<string, object[]>();
private finished_init = false;
constructor(options: { autowire: boolean; path?: string }) {
if(options.autowire) { /* noop */ }
}
addHook(name: string, callback: Function) {
addHook(name: string, callback: object) {
if (!this.hooks.has(name)) {
this.hooks.set(name, []);
}
@@ -27,7 +28,7 @@ export class Container {
if(hasCallableMethod(insert, hookname)) {
//@ts-ignore
this.addHook(hookname, () => insert[hookname]())
this.addHook(hookname, insert)
}
}
@@ -68,10 +69,11 @@ export class Container {
return Object.fromEntries(this.__singletons) as T
}
async executeHooks(name: string) {
private async executeHooks(name: string) {
const hookFunctions = this.hooks.get(name) || [];
for (const hookFunction of hookFunctions) {
await hookFunction();
for (const hookObject of hookFunctions) {
//@ts-ignore .registerHooks verifies the hookObject hasCallableMethod
await hookObject[name]();
}
}
@@ -88,7 +90,7 @@ export class Container {
if (hasCallableMethod(existing, 'dispose')) {
existing.dispose();
// get the index of the existing singleton, now delete the dispose hook at that index
const hookIndex = this.hooks.get('dispose')!.indexOf(existing.dispose);
const hookIndex = this.hooks.get('dispose')!.indexOf(existing);
if (hookIndex > -1) {
this.hooks.get('dispose')!.splice(hookIndex, 1);
}

View File

@@ -1,23 +1,30 @@
import { Container } from '../src/container';
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, Mock, afterEach } from 'vitest';
class SingletonCheese {
dispose() {
return this.value
}
constructor(public value: string){}
}
describe('CoreContainer Tests', () => {
let coreContainer: Container;
let singletonWInit: { init: Mock<any, any>; value: string }
let singletonWDispose: { dispose: Mock<any, any>; value: string }
let singletonWDispose: SingletonCheese
beforeEach(() => {
coreContainer = new Container({ autowire: false });
singletonWInit = {
value: 'singletonWithInit',
init: vi.fn()
value: 'singletonWithInit',
init: vi.fn()
};
singletonWDispose = {
value: 'singletonWithDispose',
dispose: vi.fn()
}
singletonWDispose = new SingletonCheese('singletonWithDispose')
});
afterEach(() => {
vi.clearAllMocks()
})
it('Adding and getting singletons', () => {
coreContainer.addSingleton('singletonKey', { value: 'singletonValue' });
const singleton = coreContainer.get('singletonKey');
@@ -125,11 +132,19 @@ describe('CoreContainer Tests', () => {
expect(singletonWInit.init).toHaveBeenCalledOnce();
});
it('should be false because not swapping anything', () => {
it('should return false because not swapping anything', () => {
const swap = coreContainer.swap('singletonKeyWithInit', singletonWInit);
expect(swap).toBe(false);
})
it('should return true because not swapping anything', () => {
coreContainer.addSingleton('singletonKeyWithInit', singletonWInit);
const singletonWithInit2 = {
value: 'singletonValueWithInit2',
init: vi.fn()
};
const swap = coreContainer.swap('singletonKeyWithInit', singletonWithInit2);
expect(swap).toBe(true);
})
it('should swap object with another', () => {
coreContainer.addSingleton('singleton', singletonWInit)
const singletonWithInit2 = {
@@ -153,12 +168,24 @@ describe('CoreContainer Tests', () => {
};
coreContainer.addSingleton('singletonWithDispose3', singletonWithDispose3);
const swapped = coreContainer.swap('singleton', singletonWithDispose2);
vi.spyOn(singletonWDispose, 'dispose')
coreContainer.swap('singleton', singletonWithDispose2);
expect(singletonWDispose.dispose).toHaveBeenCalledOnce();
expect(coreContainer.get<Record<string, unknown>>('singleton')).toBe(singletonWithDispose2);
expect(singletonWithDispose2.dispose).not.toHaveBeenCalledOnce();
expect(singletonWithDispose3.dispose).not.toHaveBeenCalledOnce();
expect(swapped).toBe(true);
})
it('should swap object, maintaining reference to `this`', () => {
coreContainer.addSingleton('singleton', singletonWDispose);
const singletonWithDispose2 = {
value: 'singletonValueWithDispose2',
dispose: vi.fn()
};
const spiedDispose = vi.spyOn(singletonWDispose, 'dispose')
const swapped = coreContainer.swap('singleton', singletonWithDispose2);
expect(spiedDispose.mock.results[0].value).toEqual('singletonWithDispose');
expect(coreContainer.get<Record<string, unknown>>('singleton')).toBe(singletonWithDispose2);
expect(singletonWithDispose2.dispose).not.toHaveBeenCalledOnce();
})
})