Skip to content

Commit

Permalink
feat(ioc): small improvements and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kedrzu committed Aug 7, 2023
1 parent feab157 commit c504c7f
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 21 deletions.
17 changes: 9 additions & 8 deletions packages/ioc/src/Factory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { EmptyObject } from '@nzyme/types';

import { ResolveDeps, ResolveResult } from './Container.js';
import { Injectable, InjectableOptions } from './Injectable.js';
import { Resolvable } from './Resolvable.js';
import { Injectable } from './Injectable.js';
import { Resolvable, ResolvableOptions } from './Resolvable.js';

export class Factory<T, TDeps extends ResolveDeps = ResolveDeps> extends Resolvable<T, TDeps> {
constructor(private readonly def: FactoryDefinition<T, TDeps>) {
constructor(private readonly def: FactoryOptions<T, TDeps>) {
super(def);
}

Expand All @@ -20,14 +22,13 @@ export class Factory<T, TDeps extends ResolveDeps = ResolveDeps> extends Resolva
}
}

export interface FactoryDefinition<T, TDeps extends ResolveDeps = {}> extends InjectableOptions {
readonly deps?: TDeps;
readonly for?: Injectable<T>;
export interface FactoryOptions<T, TDeps extends ResolveDeps = EmptyObject>
extends ResolvableOptions<T, TDeps> {
readonly setup: (deps: ResolveResult<TDeps>, scope?: Injectable) => T;
}

export function defineFactory<T, TDeps extends ResolveDeps = {}>(
definition: FactoryDefinition<T, TDeps>,
export function defineFactory<T, TDeps extends ResolveDeps = EmptyObject>(
definition: FactoryOptions<T, TDeps>,
): Factory<T, TDeps> {
return new Factory(definition);
}
7 changes: 2 additions & 5 deletions packages/ioc/src/Service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { EmptyObject } from '@nzyme/types';

import { ResolveDeps, ResolveResult } from './Container.js';
import { Injectable, InjectableOptions } from './Injectable.js';
import { Resolvable } from './Resolvable.js';
import { Resolvable, ResolvableOptions } from './Resolvable.js';

export class Service<T, TDeps extends ResolveDeps = ResolveDeps> extends Resolvable<T, TDeps> {
constructor(private readonly def: ServiceOptions<T, TDeps>) {
Expand All @@ -23,9 +22,7 @@ export class Service<T, TDeps extends ResolveDeps = ResolveDeps> extends Resolva
}

export interface ServiceOptions<T, TDeps extends ResolveDeps = EmptyObject>
extends InjectableOptions {
readonly deps?: TDeps;
readonly for?: Injectable<T>;
extends ResolvableOptions<T, TDeps> {
readonly setup: (deps: ResolveResult<TDeps>) => T;
}

Expand Down
50 changes: 42 additions & 8 deletions packages/ioc/tests/Service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,20 @@ test('resolve service with deps', () => {
expect(service3Count).toBe(1);
});

test('register service as injectable', () => {
const injectable = defineInjectable<string>({
test('register service as injectable - resolve injectable first', () => {
const injectable = defineInjectable<{ foo: string }>({
name: 'foobar',
});
let serviceCount = 0;

const service = defineService({
for: injectable,
name: 'service',
setup({}) {
setup() {
serviceCount++;
return 'service';
return {
foo: 'bar',
};
},
});

Expand All @@ -110,9 +112,41 @@ test('register service as injectable', () => {
const serviceInstance1 = container.resolve(service);
const serviceInstance2 = container.resolve(service);

expect(injectableInstance1).toBe('service');
expect(injectableInstance2).toBe('service');
expect(serviceInstance1).toBe('service');
expect(serviceInstance2).toBe('service');
expect(injectableInstance1.foo).toBe('bar');
expect(injectableInstance2).toBe(injectableInstance1);
expect(serviceInstance1).toBe(injectableInstance1);
expect(serviceInstance2).toBe(injectableInstance1);
expect(serviceCount).toBe(1);
});

test('register service as injectable - resolve service first', () => {
const injectable = defineInjectable<{ foo: string }>({
name: 'foobar',
});
let serviceCount = 0;

const service = defineService({
for: injectable,
name: 'service',
setup() {
serviceCount++;
return {
foo: 'bar',
};
},
});

const container = new Container();

container.set(injectable, service);
const serviceInstance1 = container.resolve(service);
const serviceInstance2 = container.resolve(service);
const injectableInstance1 = container.resolve(injectable);
const injectableInstance2 = container.resolve(injectable);

expect(injectableInstance1.foo).toBe('bar');
expect(injectableInstance2).toBe(injectableInstance1);
expect(serviceInstance1).toBe(injectableInstance1);
expect(serviceInstance2).toBe(injectableInstance1);
expect(serviceCount).toBe(1);
});

0 comments on commit c504c7f

Please sign in to comment.