##// END OF EJS Templates
Container.configure sync/async tests
cin -
r42:3a5e68edd843 di-typescript
parent child
Show More
@@ -0,0 +1,17
1 define({
2 foo: {
3 $type: "./Foo#Foo"
4 },
5
6 bar: {
7 $type: "./Bar#Bar",
8 params: {
9 db: {
10 provider: {
11 $dependency: "db"
12 }
13 }
14 }
15 },
16 db: "db://localhost"
17 }); No newline at end of file
@@ -1,6 +1,6
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
3 import { Descriptor, ServiceMap, isDescriptor } from "./interfaces";
3 import { Descriptor, ServiceMap } from "./interfaces";
4 import { Container } from "./Container";
4 import { Container } from "./Container";
5
5
6 const trace = TraceSource.get("@implab/core/di/ActivationContext");
6 const trace = TraceSource.get("@implab/core/di/ActivationContext");
@@ -1,14 +1,17
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
3 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
4 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration, DependencyRegistration } from "./interfaces";
4 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration, DependencyRegistration, ValueRegistration } from "./interfaces";
5 import { AggregateDescriptor } from "./AggregateDescriptor";
5 import { AggregateDescriptor } from "./AggregateDescriptor";
6 import { isPrimitive, pmap } from "../safe";
6 import { isPrimitive, pmap } from "../safe";
7 import { ReferenceDescriptor } from "./ReferenceDescriptor";
7 import { ReferenceDescriptor } from "./ReferenceDescriptor";
8 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
8 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
9 import { ModuleResolverBase } from "./ModuleResolverBase";
9 import { ModuleResolverBase } from "./ModuleResolverBase";
10 import format = require("../text/format");
10 import format = require("../text/format");
11 import { throws } from "assert";
11 import { TraceSource } from "../log/TraceSource";
12 import { RequireJsResolver } from "./RequireJsResolver";
13
14 const trace = TraceSource.get("@implab/core/di/ActivationContext");
12
15
13 export class Container {
16 export class Container {
14 _services: ServiceMap;
17 _services: ServiceMap;
@@ -30,6 +33,7 export class Container {
30 this._cleanup = [];
33 this._cleanup = [];
31 this._root = parent ? parent.getRootContainer() : this;
34 this._root = parent ? parent.getRootContainer() : this;
32 this._services.container = new ValueDescriptor(this);
35 this._services.container = new ValueDescriptor(this);
36 this._resolver = new RequireJsResolver();
33 }
37 }
34
38
35 getRootContainer() {
39 getRootContainer() {
@@ -57,6 +61,9 export class Container {
57 }
61 }
58 }
62 }
59
63
64 /**
65 * @deprecated use resolve() method
66 */
60 getService(name: string, def?) {
67 getService(name: string, def?) {
61 return this.resolve.apply(this, arguments);
68 return this.resolve.apply(this, arguments);
62 }
69 }
@@ -101,10 +108,12 export class Container {
101 */
108 */
102 async configure(config: string | object, opts?: object) {
109 async configure(config: string | object, opts?: object) {
103 if (typeof (config) === "string") {
110 if (typeof (config) === "string") {
111 trace.log(`load configuration '${config}'`);
104 const resolver = await this._resolver.createResolver(config, opts);
112 const resolver = await this._resolver.createResolver(config, opts);
105 const data = await this._resolver.loadModule(config);
113 const data = await this._resolver.loadModule(config);
106 return this._configure(data, { resolver });
114 return this._configure(data, { resolver });
107 } else {
115 } else {
116 trace.log(`json configuration`);
108 return this._configure(config);
117 return this._configure(config);
109 }
118 }
110 }
119 }
@@ -133,27 +142,30 export class Container {
133 this.register(services);
142 this.register(services);
134 }
143 }
135
144
136 async _parse(registration: any, resolver: ModuleResolverBase) {
145 async _parse(data: any, resolver: ModuleResolverBase) {
137 if (isPrimitive(registration) || isDescriptor(registration))
146 if (isPrimitive(data) || isDescriptor(data))
138 return registration;
147 return data;
139
148
140 if (isDependencyRegistration(registration)) {
149 if (isDependencyRegistration(data)) {
141 return this._paseReference(registration, resolver);
150 return this._makeReferenceDescriptor(data, resolver);
142 } else if (isValueRegistration(registration)) {
151 } else if (isValueRegistration(data)) {
143 return !registration.parse ?
152 return this._makeValueDescriptor(data, resolver);
144 new ValueDescriptor(registration.$value) :
153 } else if (data.$type || data.$factory) {
145 new AggregateDescriptor(this._parse(registration.$value, resolver));
154 return this._makeServiceDescriptor(data, resolver);
146
155 } else if (data instanceof Array) {
147 } else if (registration.$type || registration.$factory) {
156 return this._parseArray(data, resolver);
148 return this._parseService(registration, resolver);
149 } else if (registration instanceof Array) {
150 return this._parseArray(registration, resolver);
151 }
157 }
152
158
153 return this._parseObject(registration, resolver);
159 return this._parseObject(data, resolver);
154 }
160 }
155
161
156 async _paseReference(registration: DependencyRegistration, resolver: ModuleResolverBase) {
162 async _makeValueDescriptor(data: ValueRegistration, resolver: ModuleResolverBase) {
163 return !data.parse ?
164 new ValueDescriptor(data.$value) :
165 new AggregateDescriptor(this._parse(data.$value, resolver));
166 }
167
168 async _makeReferenceDescriptor(registration: DependencyRegistration, resolver: ModuleResolverBase) {
157 return new ReferenceDescriptor({
169 return new ReferenceDescriptor({
158 name: registration.$dependency,
170 name: registration.$dependency,
159 lazy: registration.lazy,
171 lazy: registration.lazy,
@@ -163,7 +175,7 export class Container {
163 });
175 });
164 }
176 }
165
177
166 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
178 async _makeServiceDescriptor(data: ServiceRegistration, resolver: ModuleResolverBase) {
167 const opts: ServiceDescriptorParams = {
179 const opts: ServiceDescriptorParams = {
168 owner: this
180 owner: this
169 };
181 };
@@ -195,7 +207,7 export class Container {
195 }
207 }
196
208
197 if (data.params)
209 if (data.params)
198 opts.params = this._parse(data.params, resolver);
210 opts.params = await this._parse(data.params, resolver);
199
211
200 if (data.activation) {
212 if (data.activation) {
201 if (typeof (data.activation) === "string") {
213 if (typeof (data.activation) === "string") {
@@ -3,9 +3,12 import { Uuid } from "../Uuid";
3 import { argumentNotEmptyString } from "../safe";
3 import { argumentNotEmptyString } from "../safe";
4 import { TraceSource } from "../log/TraceSource";
4 import { TraceSource } from "../log/TraceSource";
5
5
6 declare function require(modules: string[], cb?: (...args: any[]) => any): void;
6 type RequireFn = (modules: string[], cb?: (...args: any[]) => any) => void;
7
7
8 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any): void;
8 declare const require: RequireFn;
9
10 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any, eb?: (e) => any): void;
11 declare function define(modules: string[], cb?: (...args: any[]) => any, eb?: (e) => any): void;
9
12
10 interface RequireJsResolverParams {
13 interface RequireJsResolverParams {
11 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void;
14 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void;
@@ -13,14 +16,14 interface RequireJsResolverParams {
13 base: string;
16 base: string;
14 }
17 }
15
18
16 TraceSource.get("RequireJsResolver");
19 const trace = TraceSource.get("@implab/core/di/RequireJsResolver");
17
20
18 export class RequireJsResolver extends ModuleResolverBase {
21 export class RequireJsResolver extends ModuleResolverBase {
19 _contextRequire = require;
22 _contextRequire = require;
20
23
21 _base: string;
24 _base: string;
22
25
23 constructor(opts) {
26 constructor(opts?: RequireJsResolverParams) {
24 super();
27 super();
25
28
26 if (opts) {
29 if (opts) {
@@ -40,6 +43,8 export class RequireJsResolver extends M
40 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
43 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
41 argumentNotEmptyString(moduleName, "moduleName");
44 argumentNotEmptyString(moduleName, "moduleName");
42
45
46 trace.log("createResolver({0})", moduleName);
47
43 const parts = moduleName.split("/");
48 const parts = moduleName.split("/");
44 if (parts[0] === ".") {
49 if (parts[0] === ".") {
45 if (this._base)
50 if (this._base)
@@ -55,17 +60,38 export class RequireJsResolver extends M
55
60
56 const shim = parts.join("/");
61 const shim = parts.join("/");
57
62
58 const contextRequire = await new Promise(
63 trace.debug(`define shim ${shim}`);
59 resolve => define(shim, ["require"], resolve)
60 );
61
64
62 return new RequireJsResolver({
65 try {
63 base: parts.slice(0, -1).join("/"),
66 const contextRequire = await new Promise<RequireFn>(
64 contextRequire
67 (resolve, reject) => {
65 });
68 try {
69 define(shim, ["require"], r => {
70 trace.debug("shim resolved");
71 resolve(r);
72 }, reject);
73 require([shim]);
74 } catch (e) {
75 reject(e);
76 }
77 }
78 );
79
80 trace.debug("creating new moduleResolver");
81
82 return new RequireJsResolver({
83 base: parts.slice(0, -1).join("/"),
84 contextRequire
85 });
86 } catch (e) {
87 trace.error(e);
88 throw e;
89 }
90
66 }
91 }
67
92
68 async loadModule(moduleName: string): Promise<object> {
93 async loadModule(moduleName: string): Promise<object> {
94 trace.log(`loadModule(${moduleName})`);
69 return new Promise<object>(
95 return new Promise<object>(
70 resolve => this._contextRequire.call(null, [moduleName], resolve)
96 resolve => this._contextRequire.call(null, [moduleName], resolve)
71 );
97 );
@@ -37,17 +37,19 function _parse(value, context: Activati
37 if (isPrimitive(value))
37 if (isPrimitive(value))
38 return value;
38 return value;
39
39
40 trace.debug("parse {0}", path);
41
40 if (isDescriptor(value))
42 if (isDescriptor(value))
41 return context.activate(value, path);
43 return context.activate(value, path);
42
44
43 if (value instanceof Array)
45 if (value instanceof Array)
44 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
46 return value.map((x, i) => _parse(x, context, `${path}[${i}]`));
45
47
46 const t = {};
48 const t = {};
47 for (const p of Object.keys(value))
49 for (const p of Object.keys(value))
48 t[p] = this._parse(value[p], context, `${path}.${p}`);
50 t[p] = _parse(value[p], context, `${path}.${p}`);
51
49 return t;
52 return t;
50
51 }
53 }
52
54
53 export interface ServiceDescriptorParams {
55 export interface ServiceDescriptorParams {
@@ -62,5 +62,5 export function isValueRegistration(x):
62 }
62 }
63
63
64 export function isDependencyRegistration(x): x is DependencyRegistration {
64 export function isDependencyRegistration(x): x is DependencyRegistration {
65 return (!isPrimitive(x)) && ("$depdendency" in x);
65 return (!isPrimitive(x)) && ("$dependency" in x);
66 }
66 }
@@ -1,7 +1,7
1 import * as format from '../text/format'
1 import * as format from "../text/format";
2 import { argumentNotNull } from '../safe';
2 import { argumentNotNull } from "../safe";
3 import { Observable } from '../Observable'
3 import { Observable } from "../Observable";
4 import { IDestroyable } from '../interfaces';
4 import { IDestroyable } from "../interfaces";
5
5
6 export const DebugLevel = 400;
6 export const DebugLevel = 400;
7
7
@@ -16,11 +16,11 export const SilentLevel = 0;
16 export class TraceEvent {
16 export class TraceEvent {
17 readonly source: TraceSource;
17 readonly source: TraceSource;
18
18
19 readonly level: Number;
19 readonly level: number;
20
20
21 readonly arg: any;
21 readonly arg: any;
22
22
23 constructor(source: TraceSource, level: Number, arg: any) {
23 constructor(source: TraceSource, level: number, arg: any) {
24 this.source = source;
24 this.source = source;
25 this.level = level;
25 this.level = level;
26 this.arg = arg;
26 this.arg = arg;
@@ -185,4 +185,3 export class TraceSource {
185 return Registry.instance.get(id);
185 return Registry.instance.get(id);
186 }
186 }
187 }
187 }
188
@@ -20,20 +20,31 test("Container register/resolve tests",
20
20
21 const connection1 = "db://localhost";
21 const connection1 = "db://localhost";
22
22
23 container.register("connection", new ValueDescriptor(connection1));
23 t.throws(
24 () => container.register("bla-bla", "bla-bla"),
25 "Do not allow to register anything other than descriptors"
26 );
24
27
25 t.equals(container.getService("connection"), connection1);
28 t.doesNotThrow(
29 () => container.register("connection", new ValueDescriptor(connection1)),
30 "register ValueDescriptor"
31 );
26
32
27 container.register(
33 t.equals(container.getService("connection"), connection1, "resolve string value");
28 "dbParams",
34
29 new AggregateDescriptor({
35 t.doesNotThrow(
30 timeout: 10,
36 () => container.register(
31 connection: new ReferenceDescriptor({ name: "connection" })
37 "dbParams",
32 })
38 new AggregateDescriptor({
39 timeout: 10,
40 connection: new ReferenceDescriptor({ name: "connection" })
41 })
42 ),
43 "register AggregateDescriptor"
33 );
44 );
34
45
35 const dbParams = container.getService("dbParams");
46 const dbParams = container.getService("dbParams");
36 t.equals(dbParams.connection, connection1, "should get connection");
47 t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
37
48
38 writer.destroy();
49 writer.destroy();
39 });
50 });
@@ -53,6 +64,13 test("Container configure/resolve tests"
53 $type: Foo
64 $type: Foo
54 },
65 },
55
66
67 box: {
68 $type: Bar,
69 params: {
70 $dependency: "foo"
71 }
72 },
73
56 bar: {
74 bar: {
57 $type: Bar,
75 $type: Bar,
58 params: {
76 params: {
@@ -64,11 +82,37 test("Container configure/resolve tests"
64 }
82 }
65 }
83 }
66 });
84 });
85 t.pass("should configure from js object");
67
86
68 const f1 = container.resolve("foo");
87 const f1 = container.resolve("foo");
88
89 t.assert(!isNull(f1), "foo should be not null");
90
91 t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
92
93 writer.destroy();
94 });
95
96 test("Load configuration from module", async t => {
97 const writer = new TapeWriter(t);
98
99 TraceSource.on(ts => {
100 ts.level = DebugLevel;
101 writer.writeEvents(ts.events);
102 });
103
104 const container = new Container();
105
106 await container.configure("test/mock/config1");
107 t.pass("The configuration should load");
108
109 const f1 = container.resolve("foo");
110
69 t.assert(!isNull(f1), "foo should be not null");
111 t.assert(!isNull(f1), "foo should be not null");
70
112
71 const b1 = container.resolve("bar");
113 const b1 = container.resolve("bar");
72
114
115 t.assert(!isNull(b1), "foo should be not null");
116
73 writer.destroy();
117 writer.destroy();
74 });
118 });
General Comments 0
You need to be logged in to leave comments. Login now