##// END OF EJS Templates
Working on IoC container configuration
cin -
r111:33420a95f637 ioc ts support
parent child
Show More
@@ -1,50 +1,37
1 import { Constructor } from "../interfaces";
1 import { Constructor } from "../interfaces";
2
2
3 export interface InjectOptions {
3 export interface InjectOptions {
4 lazy?: boolean;
4 lazy?: boolean;
5 }
5 }
6
6
7 type Setter<T = any> = (v: T) => void;
7 type Setter<T = any> = (v: T) => void;
8
8
9 type Injector<T> = {
10 [k in keyof T]: Setter;
11 };
12
13 type Compatible<T1, T2> = T1 extends T2 ? any : never;
9 type Compatible<T1, T2> = T1 extends T2 ? any : never;
14
10
15 type SetterType<T> = T extends (v: infer V) => void ? V : never;
11 type SetterType<T> = T extends (v: infer V) => void ? V : never;
16
12
17 type Tuple<T = any> = Parameters<(...args: T[]) => void>;
13 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
18
14
19 interface Newable<A extends Tuple, T> {
15 type ExtractDependency<D, S> = D extends { $dependency: infer K } ? D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> : VisitDependency<D, S>;
20 new (...params: A): T;
21 prototype: T;
22 }
23
16
24 type MapTuple<T, A extends (keyof T)[]> = { [K in keyof A] : K extends number ? T[ A[K] ] : A[K] };
17 type VisitDependency<D, S> = D extends {} ? { [K in keyof D]: ExtractDependency<D[K], S> } : D;
25
18
26 export class Builder<T, S> {
19 export class Builder<T, S> {
27 provides() {
20 consume<P extends any[]>(...args: P) {
28 return <C extends Constructor<T>>(constructor: C) => {
21 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
29 return constructor;
22 // return constructor;
30 };
23 };
31 }
24 }
32
25
33 inject<K extends keyof S>(dependency: K) {
26 inject<K extends keyof S>(dependency: K) {
34 // K = "bar"
27 // K = "bar"
35 // M = "setValue"
28 // M = "setValue"
36 // S[K] = Bar
29 // S[K] = Bar
37 // T[M] = (value: string) => void
30 // T[M] = (value: string) => void
38 // P[m] = (value: V) => void
31 // P[m] = (value: V) => void
39 return <P, M extends keyof (T | P)>(target: P, memberName: M, descriptor: TypedPropertyDescriptor<Compatible<T[M], Setter<S[K]>>>) => {
32 return <P, M extends keyof (T | P)>(target: P, memberName: M, descriptor: TypedPropertyDescriptor<Compatible<T[M], Setter<S[K]>>>) => {
40
33
41 };
34 };
42 }
35 }
43
36
44 dependencies<D extends (keyof S)[]>(...deps: D) {
45 return <C extends Constructor<T>>(constructor: MapTuple<S, D> extends ConstructorParameters<C> ? C : never) => {
46 return constructor;
47 } ;
48 }
49
50 }
37 }
@@ -1,111 +1,111
1 export interface Constructor<T = {}> {
1 export interface Constructor<T = {}> {
2 new(...args: any[]): T;
2 new(...args: any[]): T;
3 prototype: T;
3 prototype: T;
4 }
4 }
5
5
6 export type Factory<T = {}> = (...args: any[]) => T;
6 export type Factory<T = {}> = (...args: any[]) => T;
7
7
8 export type Predicate<T = any> = (x: T) => boolean;
8 export type Predicate<T = any> = (x: T) => boolean;
9
9
10 export interface MapOf<T> {
10 export interface MapOf<T> {
11 [key: string]: T;
11 [key: string]: T;
12 }
12 }
13
13
14 export interface IDestroyable {
14 export interface IDestroyable {
15 destroy(): void;
15 destroy(): void;
16 }
16 }
17
17
18 export interface IRemovable {
18 export interface IRemovable {
19 remove(): void;
19 remove(): void;
20 }
20 }
21
21
22 export interface ICancellation {
22 export interface ICancellation {
23 throwIfRequested(): void;
23 throwIfRequested(): void;
24 isRequested(): boolean;
24 isRequested(): boolean;
25 isSupported(): boolean;
25 isSupported(): boolean;
26 register(cb: (e: any) => void): IDestroyable;
26 register(cb: (e: any) => void): IDestroyable;
27 }
27 }
28
28
29 /**
29 /**
30 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ Π°ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ
30 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ Π°ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ
31 */
31 */
32 export interface IActivatable {
32 export interface IActivatable {
33 /**
33 /**
34 * @returns Boolean indicates the current state
34 * @returns Boolean indicates the current state
35 */
35 */
36 isActive(): boolean;
36 isActive(): boolean;
37
37
38 /**
38 /**
39 * Starts the component activation
39 * Starts the component activation
40 * @param ct cancellation token for this operation
40 * @param ct cancellation token for this operation
41 */
41 */
42 activate(ct?: ICancellation): Promise<void>;
42 activate(ct?: ICancellation): Promise<void>;
43
43
44 /**
44 /**
45 * Starts the component deactivation
45 * Starts the component deactivation
46 * @param ct cancellation token for this operation
46 * @param ct cancellation token for this operation
47 */
47 */
48 deactivate(ct?: ICancellation): Promise<void>;
48 deactivate(ct?: ICancellation): Promise<void>;
49
49
50 /**
50 /**
51 * Sets the activation controller for this component
51 * Sets the activation controller for this component
52 * @param controller The activation controller
52 * @param controller The activation controller
53 *
53 *
54 * Activation controller checks whether this component
54 * Activation controller checks whether this component
55 * can be activated and manages the active state of the
55 * can be activated and manages the active state of the
56 * component
56 * component
57 */
57 */
58 setActivationController(controller: IActivationController);
58 setActivationController(controller: IActivationController): void;
59
59
60 /**
60 /**
61 * Gets the current activation controller for this component
61 * Gets the current activation controller for this component
62 */
62 */
63 getActivationController(): IActivationController;
63 getActivationController(): IActivationController;
64 }
64 }
65
65
66 export interface IActivationController {
66 export interface IActivationController {
67 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
67 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
68
68
69 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
69 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
70
70
71 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
71 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
72
72
73 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
73 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
74
74
75 deactivate(ct?: ICancellation): Promise<void>;
75 deactivate(ct?: ICancellation): Promise<void>;
76
76
77 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
77 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
78
78
79 getActive(): IActivatable;
79 getActive(): IActivatable;
80 }
80 }
81
81
82 export interface IAsyncComponent {
82 export interface IAsyncComponent {
83 getCompletion(): Promise<void>;
83 getCompletion(): Promise<void>;
84 }
84 }
85
85
86 export interface ICancellable {
86 export interface ICancellable {
87 cancel(reason?: any): void;
87 cancel(reason?: any): void;
88 }
88 }
89
89
90 export interface IObservable<T> {
90 export interface IObservable<T> {
91 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
91 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
92 next(ct?: ICancellation): Promise<T>;
92 next(ct?: ICancellation): Promise<T>;
93 }
93 }
94
94
95 export interface IObserver<T> {
95 export interface IObserver<T> {
96 next(event: T): void;
96 next(event: T): void;
97
97
98 error(e: any): void;
98 error(e: any): void;
99
99
100 complete(): void;
100 complete(): void;
101 }
101 }
102
102
103 export interface TextWriter {
103 export interface TextWriter {
104 write(obj: any): void;
104 write(obj: any): void;
105 write(format: string, ...args: any[]): void;
105 write(format: string, ...args: any[]): void;
106
106
107 writeLine(obj?: any): void;
107 writeLine(obj?: any): void;
108 writeLine(format: string, ...args: any[]): void;
108 writeLine(format: string, ...args: any[]): void;
109
109
110 writeValue(value: any, spec?: string): void;
110 writeValue(value: any, spec?: string): void;
111 }
111 }
@@ -1,18 +1,33
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { config } from "./config";
2
3
3 export class Bar {
4 const service = config.build("bar");
4 name = "bar";
5
5
6 foo: Foo | undefined;
6 @service.consume({
7 f: config.dependency("foo"),
8 nested: {
9 lazy: config.lazy("foo")
10 }
11 })
12 export class Bar {
13 barName = "bar";
7
14
8 constructor(_opts?: { foo?: Foo; }) {
15 _v: Foo | undefined;
9 if (_opts && _opts.foo)
16
10 this.foo = _opts.foo;
17 constructor(_opts: {
18 f: Foo;
19 nested: {
20 lazy: () => Foo
21 }
22 }) {
23
24 if (_opts && _opts.f)
25 this._v = _opts.f;
11 }
26 }
12
27
13 getFoo() {
28 getFoo() {
14 if (this.foo === undefined)
29 if (this._v === undefined)
15 throw new Error("The foo isn't set");
30 throw new Error("The foo isn't set");
16 return this.foo;
31 return this._v;
17 }
32 }
18 }
33 }
@@ -1,29 +1,28
1 import { config } from "./config";
1 import { config } from "./config";
2
2
3 const service = config.service("barBox");
3 const service = config.build("barBox");
4
4
5 @service.provides()
5 @service.consume(config.dependency("bar"))
6 export class Box<T> {
6 export class Box<T> {
7 private _value: T | undefined;
7 private _value: T | undefined;
8
8
9 constructor(value: T) {
9 constructor(value: T) {
10 this._value = value;
10 this._value = value;
11 }
11 }
12
12
13 @service.inject("bar")
13 @service.inject("bar")
14 setValue(value: T) {
14 setValue(value: T) {
15 this._value = value;
15 this._value = value;
16 }
16 }
17
17
18 @service.inject("foo")
19 setObj(value: object) {
18 setObj(value: object) {
20
19
21 }
20 }
22
21
23 getValue() {
22 getValue() {
24 if (this._value === undefined)
23 if (this._value === undefined)
25 throw new Error("Trying to get a value from the empty box");
24 throw new Error("Trying to get a value from the empty box");
26
25
27 return this._value;
26 return this._value;
28 }
27 }
29 } No newline at end of file
28 }
@@ -1,3 +1,3
1 export class Foo {
1 export class Foo {
2 name = "foo";
2 fooName = "foo";
3 }
3 }
@@ -1,29 +1,67
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { Bar } from "./Bar";
2 import { Bar } from "./Bar";
3 import { ActivationType } from "../di/interfaces";
3 import { ActivationType } from "../di/interfaces";
4 import { Builder } from "../di/Annotations";
4 import { Builder } from "../di/Annotations";
5 import { Box } from "./Box";
5 import { Box } from "./Box";
6
6
7 interface RegistrationOptions {
7 interface RegistrationOptions {
8 activation?: ActivationType;
8 activation?: ActivationType;
9 }
9 }
10
10
11 interface Dependency<K extends keyof any> {
12 $dependency: K;
13
14 lazy?: boolean;
15 }
16
17 interface Lazy<K extends keyof any> extends Dependency<K> {
18 lazy: true;
19 }
20
21 type PromiseOrValue<T> = T | PromiseLike<T>;
22
11 interface ConfigBuilder<S> {
23 interface ConfigBuilder<S> {
12 service<K extends keyof S>(name: K): Builder<S[K], S>;
24 build<K extends keyof S, T = S[K]>(name: K): Builder<T, S>;
25
26 dependency<K extends keyof S>(name: K): Dependency<K>;
27
28 lazy<K extends keyof S>(name: K): Lazy<K>;
29
30 mapTo<K extends keyof S>(name: K, ctor: () => PromiseOrValue<new (...args: any[]) => S[K]>): ConfigBuilder<S>;
31
13 }
32 }
14
33
15 interface ContainerServices {
34 interface ContainerServices {
16 barBox: Box<Bar>;
35 barBox: Box<Bar>;
17
36
18 foo: Foo;
37 foo: Foo;
19
38
20 bar: Bar;
39 bar: Bar;
21
40
22 password: string;
41 password: string;
23
42
24 user: string;
43 user: string;
25
44
26 timeout: number;
45 timeout: number;
27 }
46 }
28
47
48 declare function load<M, C extends keyof M>(m: PromiseLike<M>, name: C): () => PromiseLike<M[C]>;
49
50 const t = {
51 barBox: load(import("./Box"), "Box"),
52
53 foo: async () => (await import("./Bar")).Bar,
54
55 bar: Bar,
56
57 password: String,
58
59 user: String,
60
61 timeout: Number
62 };
63
29 export declare const config: ConfigBuilder<ContainerServices>;
64 export declare const config: ConfigBuilder<ContainerServices>;
65 config
66 .mapTo("bar", )
67 .mapTo("barBox", ;
@@ -1,93 +1,93
1 import { test } from "./TestTraits";
1 import { test } from "./TestTraits";
2 import { Container } from "../di/Container";
2 import { Container } from "../di/Container";
3 import { ReferenceDescriptor } from "../di/ReferenceDescriptor";
3 import { ReferenceDescriptor } from "../di/ReferenceDescriptor";
4 import { AggregateDescriptor } from "../di/AggregateDescriptor";
4 import { AggregateDescriptor } from "../di/AggregateDescriptor";
5 import { ValueDescriptor } from "../di/ValueDescriptor";
5 import { ValueDescriptor } from "../di/ValueDescriptor";
6 import { Foo } from "../mock/Foo";
6 import { Foo } from "../mock/Foo";
7 import { Bar } from "../mock/Bar";
7 import { Bar } from "../mock/Bar";
8 import { isNull } from "../safe";
8 import { isNull } from "../safe";
9
9
10 test("Container register/resolve tests", async t => {
10 test("Container register/resolve tests", async t => {
11 const container = new Container();
11 const container = new Container();
12
12
13 const connection1 = "db://localhost";
13 const connection1 = "db://localhost";
14
14
15 t.throws(
15 t.throws(
16 () => container.register("bla-bla", "bla-bla"),
16 () => container.register("bla-bla", "bla-bla"),
17 "Do not allow to register anything other than descriptors"
17 "Do not allow to register anything other than descriptors"
18 );
18 );
19
19
20 t.doesNotThrow(
20 t.doesNotThrow(
21 () => container.register("connection", new ValueDescriptor(connection1)),
21 () => container.register("connection", new ValueDescriptor(connection1)),
22 "register ValueDescriptor"
22 "register ValueDescriptor"
23 );
23 );
24
24
25 t.equals(container.resolve("connection"), connection1, "resolve string value");
25 t.equals(container.resolve("connection"), connection1, "resolve string value");
26
26
27 t.doesNotThrow(
27 t.doesNotThrow(
28 () => container.register(
28 () => container.register(
29 "dbParams",
29 "dbParams",
30 new AggregateDescriptor({
30 new AggregateDescriptor({
31 timeout: 10,
31 timeout: 10,
32 connection: new ReferenceDescriptor({ name: "connection" })
32 connection: new ReferenceDescriptor({ name: "connection" })
33 })
33 })
34 ),
34 ),
35 "register AggregateDescriptor"
35 "register AggregateDescriptor"
36 );
36 );
37
37
38 const dbParams = container.resolve("dbParams");
38 const dbParams = container.resolve("dbParams");
39 t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
39 t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
40 });
40 });
41
41
42 test("Container configure/resolve tests", async t => {
42 test("Container configure/resolve tests", async t => {
43
43
44 const container = new Container();
44 const container = new Container();
45
45
46 await container.configure({
46 await container.configure({
47 foo: {
47 foo: {
48 $type: Foo
48 $type: Foo
49 },
49 },
50
50
51 box: {
51 box: {
52 $type: Bar,
52 $type: Bar,
53 params: {
53 params: {
54 $dependency: "foo"
54 $dependency: "foo"
55 }
55 }
56 },
56 },
57
57
58 bar: {
58 bar: {
59 $type: Bar,
59 $type: Bar,
60 params: {
60 params: {
61 db: {
61 db: {
62 provider: {
62 provider: {
63 $dependency: "db"
63 $dependency: "db"
64 }
64 }
65 }
65 }
66 }
66 }
67 }
67 }
68 });
68 });
69 t.pass("should configure from js object");
69 t.pass("should configure from js object");
70
70
71 const f1 = container.resolve("foo");
71 const f1 = container.resolve("foo");
72
72
73 t.assert(!isNull(f1), "foo should be not null");
73 t.assert(!isNull(f1), "foo should be not null");
74
74
75 t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
75 t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
76
76
77 });
77 });
78
78
79 test("Load configuration from module", async t => {
79 test("Load configuration from module", async t => {
80 const container = new Container();
80 const container = new Container();
81
81
82 await container.configure("../mock/config1", { contextRequire: require });
82 await container.configure("../mock/config1", { contextRequire: require });
83 t.pass("The configuration should load");
83 t.pass("The configuration should load");
84
84
85 const f1 = container.resolve("foo");
85 const f1 = container.resolve("foo");
86
86
87 t.assert(!isNull(f1), "foo should be not null");
87 t.assert(!isNull(f1), "foo should be not null");
88
88
89 const b1 = container.resolve("bar") as Bar;
89 const b1 = container.resolve("bar") as Bar;
90
90
91 t.assert(!isNull(b1), "bar should not be null");
91 t.assert(!isNull(b1), "bar should not be null");
92 t.assert(!isNull(b1.foo), "bar.foo should not be null");
92 t.assert(!isNull(b1._v), "bar.foo should not be null");
93 });
93 });
General Comments 0
You need to be logged in to leave comments. Login now