##// END OF EJS Templates
working on fluent container configuration
cin -
r125:a19ad0acfd39 ioc ts support
parent child
Show More
@@ -0,0 +1,38
1 import { primitive } from "../../safe";
2 import { ActivationType } from "../interfaces";
3
4 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
5
6 type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
7 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
8 D extends { $type: new (...args: any[]) => infer I } ? I :
9 D extends { $factory: (...args: any[]) => infer R } ? R :
10 WalkDependencies<D, S>;
11
12 type WalkDependencies<D, S> = D extends primitive ? D :
13 { [K in keyof D]: ExtractDependency<D[K], S> };
14
15 export interface TypeBuilder<T, S extends object> {
16 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
17 target: C, ...params: P): ConstructorBuilder<C, S>;
18 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
19 target: F, ...params: P): FactoryBuilder<F, S>;
20 }
21
22 export interface ServiceBuilder<T, S extends object> {
23 override<K extends keyof S>(name: K, builder: S[K] | ((t: TypeBuilder<S[K], S>) => any)): this;
24 activate(activation: ActivationType): this;
25 inject<M extends keyof T, P extends any[]>(member: T[M] extends (...params: ExtractDependency<P, S>) => any ? M : never, ...params: P): this;
26 }
27
28 export interface ConstructorBuilder<C extends new (...args: any[]) => any, S extends object> extends ServiceBuilder<InstanceType<C>, S> {
29 $type: C;
30 }
31
32 export interface FactoryBuilder<F extends (...args: any[]) => any, S extends object> extends ServiceBuilder<ReturnType<F>, S> {
33 $factory: F;
34 }
35
36 export interface ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
37 register<K extends Y>(name: K, builder: S[K] | ((t: TypeBuilder<S[K], S>) => any)): ConfigBuilder<S, Exclude<Y, K>>;
38 }
@@ -1,79 +1,79
1 import { primitive } from "../safe";
1 import { primitive } from "../safe";
2 import { TypeRegistration, DependencyRegistration, LazyDependencyRegistration, Registration, StrictTypeRegistration } from "./Configuration";
2 import { TypeRegistration, DependencyRegistration, LazyDependencyRegistration, Registration, StrictTypeRegistration } from "./Configuration";
3
3
4 export interface InjectOptions {
4 export interface InjectOptions {
5 lazy?: boolean;
5 lazy?: boolean;
6 }
6 }
7
7
8 type Compatible<T1, T2> = T2 extends T1 ? any : never;
8 type Compatible<T1, T2> = T2 extends T1 ? any : never;
9
9
10 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
10 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
11
11
12 type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
12 type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
13 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
13 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
14 D extends { $type: new (...args: any[]) => infer I } ? I :
14 D extends { $type: new (...args: any[]) => infer I } ? I :
15 WalkDependencies<D, S>;
15 WalkDependencies<D, S>;
16
16
17 type WalkDependencies<D, S> = D extends primitive ? D :
17 type WalkDependencies<D, S> = D extends primitive ? D :
18 { [K in keyof D]: ExtractDependency<D[K], S> };
18 { [K in keyof D]: ExtractDependency<D[K], S> };
19
19
20 export class Builder<T, S extends object> {
20 export class Builder<T, S extends object> {
21 declare<P extends any[]>(...args: P) {
21 declare<P extends any[]>(...args: P) {
22 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
22 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
23
23
24 };
24 };
25 }
25 }
26
26
27 inject<P extends any[]>(...args: P) {
27 inject<P extends any[]>(...args: P) {
28 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
28 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
29 target: X,
29 target: X,
30 memberName: M,
30 memberName: M,
31 descriptor: TypedPropertyDescriptor<Compatible<(...args: ExtractDependency<P, S>) => any, T[M]>>
31 descriptor: TypedPropertyDescriptor<Compatible<(...args: ExtractDependency<P, S>) => any, T[M]>>
32 ) => {
32 ) => {
33
33
34 };
34 };
35 }
35 }
36
36
37 getDescriptor(): TypeRegistration<new () => T, S> {
37 getDescriptor(): TypeRegistration<new () => T, S> {
38 throw new Error();
38 throw new Error();
39 }
39 }
40
40
41 }
41 }
42
42
43 export interface DependencyOptions<T> {
43 export interface DependencyOptions<T> {
44 optional?: boolean;
44 optional?: boolean;
45 default?: T;
45 default?: T;
46 }
46 }
47
47
48 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
48 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
49 lazy: true;
49 lazy: true;
50 }
50 }
51
51
52 interface Declaration<S extends object> {
52 interface Declaration<S extends object> {
53 define<T>(): Builder<T, S>;
53 define<T>(): Builder<T, S>;
54
54
55 dependency<K extends keyof S>(name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
55 dependency<K extends keyof S>(name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
56 dependency<K extends keyof S>(name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
56 dependency<K extends keyof S>(name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
57
57
58 $type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => any>(target: C, ...params: P): StrictTypeRegistration<C, S>;
58 $type<T, P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(target: C, ...params: P): StrictTypeRegistration<C, S>;
59
59
60 configure(): Config<S>;
60 configure(): Config<S>;
61 }
61 }
62
62
63 type ServiceModule<T, S extends object, M extends string = "service"> = {
63 type ServiceModule<T, S extends object, M extends string = "service"> = {
64 [m in M]: Builder<T, S>;
64 [m in M]: Builder<T, S>;
65 };
65 };
66
66
67 type PromiseOrValue<T> = PromiseLike<T> | T;
67 type PromiseOrValue<T> = PromiseLike<T> | T;
68
68
69
69
70 export interface Config<S extends object, Y extends keyof S = keyof S> {
70 export interface Config<S extends object, Y extends keyof S = keyof S> {
71 register<K extends Y>(name: K, m: { $from: Promise<ServiceModule<S[K], S>> }): Config<S, Exclude<Y, K>>;
71 register<K extends Y>(name: K, m: { $from: Promise<ServiceModule<S[K], S>> }): Config<S, Exclude<Y, K>>;
72 register<K extends Y, M extends string>(name: K, m: { $from: Promise<ServiceModule<S[K], S, M>>, service: M }): Config<S, Exclude<Y, K>>;
72 register<K extends Y, M extends string>(name: K, m: { $from: Promise<ServiceModule<S[K], S, M>>, service: M }): Config<S, Exclude<Y, K>>;
73
73
74 register<K extends Y>(name: K, m: Registration<S[K], S>): Config<S, Exclude<Y, K>>;
74 register<K extends Y>(name: K, m: Registration<S[K], S>): Config<S, Exclude<Y, K>>;
75 registerType<K extends Y, P extends any[]>(
75 registerType<K extends Y, P extends any[]>(
76 name: K, $type: new (...args: ExtractDependency<P, S>) => S[K], ...params: P): Config<S, Exclude<Y, K>>;
76 name: K, $type: new (...args: ExtractDependency<P, S>) => S[K], ...params: P): Config<S, Exclude<Y, K>>;
77 }
77 }
78
78
79 export declare function declare<S extends object>(): Declaration<S>;
79 export declare function declare<S extends object>(): Declaration<S>;
@@ -1,35 +1,39
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { define, dependency } from "./services";
2 import { define, dependency } from "./services";
3
3
4 export const service = define<Bar>();
4 export const service = define<Bar>();
5
5
6 @service.declare({
6 @service.declare({
7 foo: dependency("foo"),
7 foo: dependency("foo"),
8 nested: {
8 nested: {
9 lazy: dependency("foo", { lazy: true })
9 lazy: dependency("foo", { lazy: true })
10 },
10 },
11 host: dependency("host")
11 host: dependency("host")
12 }, "")
12 }, "")
13 export class Bar {
13 export class Bar {
14 barName = "bar";
14 barName = "bar";
15
15
16 _v: Foo | undefined;
16 _v: Foo | undefined;
17
17
18 constructor(_opts: {
18 constructor(_opts: {
19 foo?: Foo;
19 foo?: Foo;
20 nested?: {
20 nested?: {
21 lazy: () => Foo
21 lazy: () => Foo
22 },
22 },
23 host: string
23 host: string
24 }, s: string) {
24 }, s: string) {
25
25
26 if (_opts && _opts.foo)
26 if (_opts && _opts.foo)
27 this._v = _opts.foo;
27 this._v = _opts.foo;
28 }
28 }
29
29
30 setName(name: string) {
31
32 }
33
30 getFoo() {
34 getFoo() {
31 if (this._v === undefined)
35 if (this._v === undefined)
32 throw new Error("The foo isn't set");
36 throw new Error("The foo isn't set");
33 return this._v;
37 return this._v;
34 }
38 }
35 }
39 }
@@ -1,23 +1,30
1 import { configure, dependency, Services, $type } from "./services";
1 import { configure, dependency, Services, $type } from "./services";
2 import { Foo } from "./Foo";
2 import { Foo } from "./Foo";
3 import { Bar } from "./Bar";
3 import { Bar } from "./Bar";
4 import { Box } from "./Box";
4 import { Box } from "./Box";
5
5 import { declare } from "../di/Annotations";
6 import { ConfigBuilder, TypeBuilder } from "../di/fluent/interfaces";
6
7
7 export const config = configure()
8 export declare function build<T>(): TypeBuilder<T, Services>;
8 .register("bar", { $from: import("./Bar"), service: "service" })
9
10 export declare const config: ConfigBuilder<Services>;
11 config
12 //.register("bar", { $from: import("./Bar"), service: "service" })
9 // .register("box", { $from: import("./Box") })
13 // .register("box", { $from: import("./Box") })
10 .register("host", "example.com")
14 .register("host", "example.com")
11 // .registerType("bar2", Bar, [{ foo: dependency("foo"), host: "" }]);
15 // .registerType("bar2", Bar, [{ foo: dependency("foo"), host: "" }]);
12 .register("bar2", $type(Bar,
16 .register("bar2", s => s.type(Bar,
13 {
17 {
14 foo: $type(Foo)
18 foo: build().type(Foo)
15 .override("host", "foo.example.com")
16 .inject("setName", dependency("host"))
17 .activate("context"),
19 .activate("context"),
18 host: ""
20 nested: { lazy: dependency("foo", {lazy: true}) },
21 host: dependency("host")
19 },
22 },
20 "")
23 "")
24 .inject("setName", dependency("host"))
21 )
25 )
22 .registerType("box", Box, dependency("bar"));
26 .register("box", s => s
27 .type(Box, dependency("bar"))
28 .activate("context")
29 );
23
30
@@ -1,106 +1,107
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 import { Box } from "ts/mock/Box";
9 import { Box } from "ts/mock/Box";
10
10
11 test("Container register/resolve tests", async t => {
11 test("Container register/resolve tests", async t => {
12 const container = new Container<{
12 const container = new Container<{
13 "bla-bla": string;
13 "bla-bla": string;
14 "connection": string;
14 "connection": string;
15 "dbParams": {
15 "dbParams": {
16 timeout: number;
16 timeout: number;
17 connection: string;
17 connection: string;
18 }
18 }
19 }>();
19 }>();
20
20
21 const connection1 = "db://localhost";
21 const connection1 = "db://localhost";
22
22
23 t.throws(
23 t.throws(
24 () => container.register("bla-bla", "bla-bla" as any),
24 () => container.register("bla-bla", "bla-bla" as any),
25 "Do not allow to register anything other than descriptors"
25 "Do not allow to register anything other than descriptors"
26 );
26 );
27
27
28 t.doesNotThrow(
28 t.doesNotThrow(
29 () => container.register("connection", new ValueDescriptor(connection1)),
29 () => container.register("connection", new ValueDescriptor(connection1)),
30 "register ValueDescriptor"
30 "register ValueDescriptor"
31 );
31 );
32
32
33 t.equals(container.resolve("connection"), connection1, "resolve string value");
33 t.equals(container.resolve("connection"), connection1, "resolve string value");
34
34
35 t.doesNotThrow(
35 t.doesNotThrow(
36 () => container.register(
36 () => container.register(
37 "dbParams",
37 "dbParams",
38 new AggregateDescriptor({
38 new AggregateDescriptor({
39 timeout: 10,
39 timeout: 10,
40 connection: new ReferenceDescriptor({ name: "connection" })
40 connection: new ReferenceDescriptor({ name: "connection" })
41 })
41 })
42 ),
42 ),
43 "register AggregateDescriptor"
43 "register AggregateDescriptor"
44 );
44 );
45
45
46 const dbParams = container.resolve("dbParams");
46 const dbParams = container.resolve("dbParams");
47 t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
47 t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
48 });
48 });
49
49
50 test("Container configure/resolve tests", async t => {
50 test("Container configure/resolve tests", async t => {
51
51
52 const container = new Container<{
52 const container = new Container<{
53 foo: Foo;
53 foo: Foo;
54 box: Bar;
54 box: Bar;
55 bar: Bar;
55 bar: Bar;
56 db: any;
56 }>();
57 }>();
57
58
58 await container.configure({
59 await container.configure({
59 foo: {
60 foo: {
60 $type: Foo
61 $type: Foo
61 },
62 },
62
63
63 box: {
64 box: {
64 $type: Bar,
65 $type: Bar,
65 params: {
66 params: {
66 $dependency: "foo"
67 $dependency: "foo"
67 }
68 }
68 },
69 },
69
70
70 bar: {
71 bar: {
71 $type: Bar,
72 $type: Bar,
72 params: {
73 params: [{
73 db: {
74 db: {
74 provider: {
75 provider: {
75 $dependency: "db"
76 $dependency: "db"
76 }
77 }
77 }
78 }
78 }
79 }]
79 }
80 }
80 });
81 });
81 t.pass("should configure from js object");
82 t.pass("should configure from js object");
82
83
83 const f1 = container.resolve("foo");
84 const f1 = container.resolve("foo");
84
85
85 t.assert(!isNull(f1), "foo should be not null");
86 t.assert(!isNull(f1), "foo should be not null");
86
87
87 t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
88 t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
88
89
89 });
90 });
90
91
91 test("Load configuration from module", async t => {
92 test("Load configuration from module", async t => {
92 const container = new Container();
93 const container = new Container();
93
94
94 await container.configure("../mock/config1", { contextRequire: require });
95 await container.configure("../mock/config1", { contextRequire: require });
95 t.pass("The configuration should load");
96 t.pass("The configuration should load");
96
97
97 const f1 = container.resolve("foo");
98 const f1 = container.resolve("foo");
98
99
99 t.assert(!isNull(f1), "foo should be not null");
100 t.assert(!isNull(f1), "foo should be not null");
100
101
101 const b1 = container.resolve("bar") as Bar;
102 const b1 = container.resolve("bar") as Bar;
102
103
103 t.assert(!isNull(b1), "bar should not be null");
104 t.assert(!isNull(b1), "bar should not be null");
104 t.assert(!isNull(b1._v), "bar.foo should not be null");
105 t.assert(!isNull(b1._v), "bar.foo should not be null");
105
106
106 });
107 });
General Comments 0
You need to be logged in to leave comments. Login now