##// 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,132 +1,132
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");
7
7
8 export interface ActivationContextInfo {
8 export interface ActivationContextInfo {
9 name: string;
9 name: string;
10
10
11 service: string;
11 service: string;
12
12
13 scope: ServiceMap;
13 scope: ServiceMap;
14 }
14 }
15
15
16 export class ActivationContext {
16 export class ActivationContext {
17 _cache: object;
17 _cache: object;
18
18
19 _services: ServiceMap;
19 _services: ServiceMap;
20
20
21 _stack: ActivationContextInfo[];
21 _stack: ActivationContextInfo[];
22
22
23 _visited: object;
23 _visited: object;
24
24
25 _name: string;
25 _name: string;
26
26
27 _localized: boolean;
27 _localized: boolean;
28
28
29 container: Container;
29 container: Container;
30
30
31 constructor(container: Container, services: ServiceMap, name?: string, cache?: object, visited?) {
31 constructor(container: Container, services: ServiceMap, name?: string, cache?: object, visited?) {
32 argumentNotNull(container, "container");
32 argumentNotNull(container, "container");
33 argumentNotNull(services, "services");
33 argumentNotNull(services, "services");
34
34
35 this._name = name;
35 this._name = name;
36 this._visited = visited || {};
36 this._visited = visited || {};
37 this._stack = [];
37 this._stack = [];
38 this._cache = cache || {};
38 this._cache = cache || {};
39 this._services = services;
39 this._services = services;
40 this.container = container;
40 this.container = container;
41 }
41 }
42
42
43 getName() {
43 getName() {
44 return this._name;
44 return this._name;
45 }
45 }
46
46
47 resolve(name, def?): any {
47 resolve(name, def?): any {
48 const d = this._services[name];
48 const d = this._services[name];
49
49
50 if (!d)
50 if (!d)
51 if (arguments.length > 1)
51 if (arguments.length > 1)
52 return def;
52 return def;
53 else
53 else
54 throw new Error(`Service ${name} not found`);
54 throw new Error(`Service ${name} not found`);
55
55
56 return this.activate(d, name);
56 return this.activate(d, name);
57 }
57 }
58
58
59 /**
59 /**
60 * registers services local to the the activation context
60 * registers services local to the the activation context
61 *
61 *
62 * @name{string} the name of the service
62 * @name{string} the name of the service
63 * @service{string} the service descriptor to register
63 * @service{string} the service descriptor to register
64 */
64 */
65 register(name: string, service: Descriptor) {
65 register(name: string, service: Descriptor) {
66 argumentNotEmptyString(name, "name");
66 argumentNotEmptyString(name, "name");
67
67
68 this._services[name] = service;
68 this._services[name] = service;
69 }
69 }
70
70
71 clone() {
71 clone() {
72 return new ActivationContext(
72 return new ActivationContext(
73 this.container,
73 this.container,
74 this._services,
74 this._services,
75 this._name,
75 this._name,
76 this._cache,
76 this._cache,
77 this._visited
77 this._visited
78 );
78 );
79 }
79 }
80
80
81 has(id: string) {
81 has(id: string) {
82 return id in this._cache;
82 return id in this._cache;
83 }
83 }
84
84
85 get(id: string) {
85 get(id: string) {
86 return this._cache[id];
86 return this._cache[id];
87 }
87 }
88
88
89 store(id: string, value) {
89 store(id: string, value) {
90 return (this._cache[id] = value);
90 return (this._cache[id] = value);
91 }
91 }
92
92
93 activate(d: Descriptor, name: string) {
93 activate(d: Descriptor, name: string) {
94 if (trace.isLogEnabled())
94 if (trace.isLogEnabled())
95 trace.log(`enter ${name} ${d}`);
95 trace.log(`enter ${name} ${d}`);
96
96
97 this.enter(name, d.toString());
97 this.enter(name, d.toString());
98 const v = d.activate(this);
98 const v = d.activate(this);
99 this.leave();
99 this.leave();
100
100
101 if (trace.isLogEnabled())
101 if (trace.isLogEnabled())
102 trace.log(`leave ${name}`);
102 trace.log(`leave ${name}`);
103
103
104 return v;
104 return v;
105 }
105 }
106
106
107 visit(id: string) {
107 visit(id: string) {
108 const count = this._visited[id] || 0;
108 const count = this._visited[id] || 0;
109 this._visited[id] = count + 1;
109 this._visited[id] = count + 1;
110 return count;
110 return count;
111 }
111 }
112
112
113 getStack() {
113 getStack() {
114 return this._stack.slice().reverse();
114 return this._stack.slice().reverse();
115 }
115 }
116
116
117 private enter(name: string, service: string) {
117 private enter(name: string, service: string) {
118 this._stack.push({
118 this._stack.push({
119 name,
119 name,
120 service,
120 service,
121 scope: this._services
121 scope: this._services
122 });
122 });
123 this._name = name;
123 this._name = name;
124 this._services = Object.create(this._services);
124 this._services = Object.create(this._services);
125 }
125 }
126
126
127 private leave() {
127 private leave() {
128 const ctx = this._stack.pop();
128 const ctx = this._stack.pop();
129 this._services = ctx.scope;
129 this._services = ctx.scope;
130 this._name = ctx.name;
130 this._name = ctx.name;
131 }
131 }
132 }
132 }
@@ -1,279 +1,291
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;
15
18
16 _cache: object;
19 _cache: object;
17
20
18 _cleanup: (() => void)[];
21 _cleanup: (() => void)[];
19
22
20 _root: Container;
23 _root: Container;
21
24
22 _parent: Container;
25 _parent: Container;
23
26
24 _resolver: ModuleResolverBase;
27 _resolver: ModuleResolverBase;
25
28
26 constructor(parent?: Container) {
29 constructor(parent?: Container) {
27 this._parent = parent;
30 this._parent = parent;
28 this._services = parent ? Object.create(parent._services) : {};
31 this._services = parent ? Object.create(parent._services) : {};
29 this._cache = {};
32 this._cache = {};
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() {
36 return this._root;
40 return this._root;
37 }
41 }
38
42
39 getParent() {
43 getParent() {
40 return this._parent;
44 return this._parent;
41 }
45 }
42
46
43 resolve(name: string, def?) {
47 resolve(name: string, def?) {
44 const d = this._services[name];
48 const d = this._services[name];
45 if (d === undefined) {
49 if (d === undefined) {
46 if (arguments.length > 1)
50 if (arguments.length > 1)
47 return def;
51 return def;
48 else
52 else
49 throw new Error("Service '" + name + "' isn't found");
53 throw new Error("Service '" + name + "' isn't found");
50 }
54 }
51
55
52 const context = new ActivationContext(this, this._services);
56 const context = new ActivationContext(this, this._services);
53 try {
57 try {
54 return context.activate(d, name);
58 return context.activate(d, name);
55 } catch (error) {
59 } catch (error) {
56 throw new ActivationError(name, context.getStack(), error);
60 throw new ActivationError(name, context.getStack(), error);
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 }
63
70
64 register(nameOrCollection, service?) {
71 register(nameOrCollection, service?) {
65 if (arguments.length === 1) {
72 if (arguments.length === 1) {
66 const data = nameOrCollection;
73 const data = nameOrCollection;
67 for (const name in data)
74 for (const name in data)
68 this.register(name, data[name]);
75 this.register(name, data[name]);
69 } else {
76 } else {
70 if (!isDescriptor(service))
77 if (!isDescriptor(service))
71 throw new Error("The service parameter must be a descriptor");
78 throw new Error("The service parameter must be a descriptor");
72
79
73 this._services[nameOrCollection] = service;
80 this._services[nameOrCollection] = service;
74 }
81 }
75 return this;
82 return this;
76 }
83 }
77
84
78 onDispose(callback) {
85 onDispose(callback) {
79 if (!(callback instanceof Function))
86 if (!(callback instanceof Function))
80 throw new Error("The callback must be a function");
87 throw new Error("The callback must be a function");
81 this._cleanup.push(callback);
88 this._cleanup.push(callback);
82 }
89 }
83
90
84 dispose() {
91 dispose() {
85 if (this._cleanup) {
92 if (this._cleanup) {
86 for (const f of this._cleanup)
93 for (const f of this._cleanup)
87 f();
94 f();
88 this._cleanup = null;
95 this._cleanup = null;
89 }
96 }
90 }
97 }
91
98
92 /**
99 /**
93 * @param{String|Object} config
100 * @param{String|Object} config
94 * The configuration of the contaier. Can be either a string or an object,
101 * The configuration of the contaier. Can be either a string or an object,
95 * if the configuration is an object it's treated as a collection of
102 * if the configuration is an object it's treated as a collection of
96 * services which will be registed in the contaier.
103 * services which will be registed in the contaier.
97 *
104 *
98 * @param{Function} opts.contextRequire
105 * @param{Function} opts.contextRequire
99 * The function which will be used to load a configuration or types for services.
106 * The function which will be used to load a configuration or types for services.
100 *
107 *
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 }
111
120
112 createChildContainer() {
121 createChildContainer() {
113 return new Container(this);
122 return new Container(this);
114 }
123 }
115
124
116 has(id) {
125 has(id) {
117 return id in this._cache;
126 return id in this._cache;
118 }
127 }
119
128
120 get(id) {
129 get(id) {
121 return this._cache[id];
130 return this._cache[id];
122 }
131 }
123
132
124 store(id, value) {
133 store(id, value) {
125 return (this._cache[id] = value);
134 return (this._cache[id] = value);
126 }
135 }
127
136
128 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
137 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
129 const resolver = (opts && opts.resolver) || this._resolver;
138 const resolver = (opts && opts.resolver) || this._resolver;
130
139
131 const services = await this._parseRegistrations(data, resolver);
140 const services = await this._parseRegistrations(data, resolver);
132
141
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,
160 optional: registration.optional,
172 optional: registration.optional,
161 default: registration.default,
173 default: registration.default,
162 services: registration.services && await this._parseRegistrations(registration.services, resolver)
174 services: registration.services && await this._parseRegistrations(registration.services, resolver)
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 };
170
182
171 if (data.$type) {
183 if (data.$type) {
172 if (data.$type instanceof Function)
184 if (data.$type instanceof Function)
173 opts.type = data.$type;
185 opts.type = data.$type;
174 else if (typeof data.$type === "string")
186 else if (typeof data.$type === "string")
175 opts.type = await resolver.resolve(data.$type);
187 opts.type = await resolver.resolve(data.$type);
176 else
188 else
177 throw new Error(format("Unsupported type specification: {0:json}", data.$type));
189 throw new Error(format("Unsupported type specification: {0:json}", data.$type));
178 } else {
190 } else {
179 if (data.$factory instanceof Function)
191 if (data.$factory instanceof Function)
180 opts.factory = data.$factory;
192 opts.factory = data.$factory;
181 else if (typeof data.$factory === "string")
193 else if (typeof data.$factory === "string")
182 opts.factory = await resolver.resolve(data.$factory);
194 opts.factory = await resolver.resolve(data.$factory);
183 else
195 else
184 throw new Error(format("Unsupported factory specification: {0:json}", data.$factory));
196 throw new Error(format("Unsupported factory specification: {0:json}", data.$factory));
185 }
197 }
186
198
187 if (data.services)
199 if (data.services)
188 opts.services = await this._parseRegistrations(data.services, resolver);
200 opts.services = await this._parseRegistrations(data.services, resolver);
189
201
190 if (data.inject) {
202 if (data.inject) {
191 if (data.inject instanceof Array)
203 if (data.inject instanceof Array)
192 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
204 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
193 else
205 else
194 opts.inject = [await this._parseObject(data.inject, resolver)];
206 opts.inject = [await this._parseObject(data.inject, resolver)];
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") {
202 switch (data.activation.toLowerCase()) {
214 switch (data.activation.toLowerCase()) {
203 case "singleton":
215 case "singleton":
204 opts.activation = ActivationType.Singleton;
216 opts.activation = ActivationType.Singleton;
205 break;
217 break;
206 case "container":
218 case "container":
207 opts.activation = ActivationType.Container;
219 opts.activation = ActivationType.Container;
208 break;
220 break;
209 case "hierarchy":
221 case "hierarchy":
210 opts.activation = ActivationType.Hierarchy;
222 opts.activation = ActivationType.Hierarchy;
211 break;
223 break;
212 case "context":
224 case "context":
213 opts.activation = ActivationType.Context;
225 opts.activation = ActivationType.Context;
214 break;
226 break;
215 case "call":
227 case "call":
216 opts.activation = ActivationType.Call;
228 opts.activation = ActivationType.Call;
217 break;
229 break;
218 default:
230 default:
219 throw new Error("Unknown activation type: " +
231 throw new Error("Unknown activation type: " +
220 data.activation);
232 data.activation);
221 }
233 }
222 } else {
234 } else {
223 opts.activation = Number(data.activation);
235 opts.activation = Number(data.activation);
224 }
236 }
225 }
237 }
226
238
227 if (data.cleanup)
239 if (data.cleanup)
228 opts.cleanup = data.cleanup;
240 opts.cleanup = data.cleanup;
229
241
230 return new ServiceDescriptor(opts);
242 return new ServiceDescriptor(opts);
231 }
243 }
232
244
233 async _parseObject(data: object, resolver: ModuleResolverBase) {
245 async _parseObject(data: object, resolver: ModuleResolverBase) {
234 if (data.constructor &&
246 if (data.constructor &&
235 data.constructor.prototype !== Object.prototype)
247 data.constructor.prototype !== Object.prototype)
236 return new ValueDescriptor(data);
248 return new ValueDescriptor(data);
237
249
238 const o = {};
250 const o = {};
239
251
240 for (const p in data)
252 for (const p in data)
241 o[p] = await this._parse(data[p], resolver);
253 o[p] = await this._parse(data[p], resolver);
242
254
243 // TODO: handle inline descriptors properly
255 // TODO: handle inline descriptors properly
244 // const ex = {
256 // const ex = {
245 // activate(ctx) {
257 // activate(ctx) {
246 // const value = ctx.activate(this.prop, "prop");
258 // const value = ctx.activate(this.prop, "prop");
247 // // some code
259 // // some code
248 // },
260 // },
249
261
250 // // will be turned to ReferenceDescriptor
262 // // will be turned to ReferenceDescriptor
251 // prop: { $dependency: "depName" }
263 // prop: { $dependency: "depName" }
252 // };
264 // };
253
265
254 return o;
266 return o;
255 }
267 }
256
268
257 async _parseArray(data: Array<any>, resolver: ModuleResolverBase) {
269 async _parseArray(data: Array<any>, resolver: ModuleResolverBase) {
258 if (data.constructor &&
270 if (data.constructor &&
259 data.constructor.prototype !== Array.prototype)
271 data.constructor.prototype !== Array.prototype)
260 return new ValueDescriptor(data);
272 return new ValueDescriptor(data);
261
273
262 return pmap(data, x => this._parse(x, resolver));
274 return pmap(data, x => this._parse(x, resolver));
263 }
275 }
264
276
265 async _parseRegistrations(data: object, resolver: ModuleResolverBase) {
277 async _parseRegistrations(data: object, resolver: ModuleResolverBase) {
266 if (data.constructor &&
278 if (data.constructor &&
267 data.constructor.prototype !== Object.prototype)
279 data.constructor.prototype !== Object.prototype)
268 throw new Error("Registrations must be a simple object");
280 throw new Error("Registrations must be a simple object");
269
281
270 const o: ServiceMap = {};
282 const o: ServiceMap = {};
271
283
272 for (const p of Object.keys(data)) {
284 for (const p of Object.keys(data)) {
273 const v = await this._parse(data[p], resolver);
285 const v = await this._parse(data[p], resolver);
274 o[p] = isDescriptor(v) ? v : new AggregateDescriptor(v);
286 o[p] = isDescriptor(v) ? v : new AggregateDescriptor(v);
275 }
287 }
276
288
277 return o;
289 return o;
278 }
290 }
279 }
291 }
@@ -1,74 +1,100
1 import { ModuleResolverBase } from "./ModuleResolverBase";
1 import { ModuleResolverBase } from "./ModuleResolverBase";
2 import { Uuid } from "../Uuid";
2 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;
12
15
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) {
27
30
28 if (opts.contextRequire)
31 if (opts.contextRequire)
29 this._contextRequire = opts.contextRequire;
32 this._contextRequire = opts.contextRequire;
30
33
31 if (opts.base) {
34 if (opts.base) {
32 if (opts.base.indexOf("./") === 0)
35 if (opts.base.indexOf("./") === 0)
33 throw new Error(`A module id should be an absolute: '${opts.base}'`);
36 throw new Error(`A module id should be an absolute: '${opts.base}'`);
34 this._base = opts.base;
37 this._base = opts.base;
35 }
38 }
36 }
39 }
37
40
38 }
41 }
39
42
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)
46 parts[0] = this._base;
51 parts[0] = this._base;
47 else
52 else
48 throw new Error(`Can't resolve a relative module '${moduleName}'`);
53 throw new Error(`Can't resolve a relative module '${moduleName}'`);
49 }
54 }
50
55
51 if (parts.length > 1)
56 if (parts.length > 1)
52 parts.splice(-1, 1, Uuid());
57 parts.splice(-1, 1, Uuid());
53 else
58 else
54 parts.push(Uuid());
59 parts.push(Uuid());
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)
64
65 try {
66 const contextRequire = await new Promise<RequireFn>(
67 (resolve, reject) => {
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 }
60 );
78 );
61
79
80 trace.debug("creating new moduleResolver");
81
62 return new RequireJsResolver({
82 return new RequireJsResolver({
63 base: parts.slice(0, -1).join("/"),
83 base: parts.slice(0, -1).join("/"),
64 contextRequire
84 contextRequire
65 });
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 );
72 }
98 }
73
99
74 }
100 }
@@ -1,286 +1,288
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap, isDescriptor } from "./interfaces";
2 import { Descriptor, ActivationType, ServiceMap, isDescriptor } from "./interfaces";
3 import { Container } from "./Container";
3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, oid, isPromise } from "../safe";
4 import { argumentNotNull, isPrimitive, oid, isPromise } from "../safe";
5 import { Constructor, Factory } from "../interfaces";
5 import { Constructor, Factory } from "../interfaces";
6 import { TraceSource } from "../log/TraceSource";
6 import { TraceSource } from "../log/TraceSource";
7
7
8 let cacheId = 0;
8 let cacheId = 0;
9
9
10 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10 const trace = TraceSource.get("@implab/core/di/ActivationContext");
11
11
12 function injectMethod(target, method, context, args) {
12 function injectMethod(target, method, context, args) {
13 const m = target[method];
13 const m = target[method];
14 if (!m)
14 if (!m)
15 throw new Error("Method '" + method + "' not found");
15 throw new Error("Method '" + method + "' not found");
16
16
17 if (args instanceof Array)
17 if (args instanceof Array)
18 return m.apply(target, context.parse(args, "." + method));
18 return m.apply(target, context.parse(args, "." + method));
19 else
19 else
20 return m.call(target, context.parse(args, "." + method));
20 return m.call(target, context.parse(args, "." + method));
21 }
21 }
22
22
23 function makeClenupCallback(target, method: ((instance) => void) | string) {
23 function makeClenupCallback(target, method: ((instance) => void) | string) {
24 if (typeof (method) === "string") {
24 if (typeof (method) === "string") {
25 return () => {
25 return () => {
26 target[method]();
26 target[method]();
27 };
27 };
28 } else {
28 } else {
29 return () => {
29 return () => {
30 method(target);
30 method(target);
31 };
31 };
32 }
32 }
33 }
33 }
34
34
35 // TODO: make async
35 // TODO: make async
36 function _parse(value, context: ActivationContext, path: string) {
36 function _parse(value, context: ActivationContext, path: string) {
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 {
54 activation?: ActivationType;
56 activation?: ActivationType;
55
57
56 owner: Container;
58 owner: Container;
57
59
58 type?: Constructor;
60 type?: Constructor;
59
61
60 factory?: Factory;
62 factory?: Factory;
61
63
62 params?;
64 params?;
63
65
64 inject?: object[];
66 inject?: object[];
65
67
66 services?: ServiceMap;
68 services?: ServiceMap;
67
69
68 cleanup?: ((x) => void) | string;
70 cleanup?: ((x) => void) | string;
69 }
71 }
70
72
71 export class ServiceDescriptor implements Descriptor {
73 export class ServiceDescriptor implements Descriptor {
72 _instance;
74 _instance;
73
75
74 _hasInstance = false;
76 _hasInstance = false;
75
77
76 _activationType = ActivationType.Call;
78 _activationType = ActivationType.Call;
77
79
78 _services: ServiceMap;
80 _services: ServiceMap;
79
81
80 _type: Constructor = null;
82 _type: Constructor = null;
81
83
82 _factory: Factory = null;
84 _factory: Factory = null;
83
85
84 _params;
86 _params;
85
87
86 _inject: object[];
88 _inject: object[];
87
89
88 _cleanup: ((x) => void) | string;
90 _cleanup: ((x) => void) | string;
89
91
90 _cacheId: any;
92 _cacheId: any;
91
93
92 _owner: Container;
94 _owner: Container;
93
95
94 constructor(opts: ServiceDescriptorParams) {
96 constructor(opts: ServiceDescriptorParams) {
95 argumentNotNull(opts, "opts");
97 argumentNotNull(opts, "opts");
96 argumentNotNull(opts.owner, "owner");
98 argumentNotNull(opts.owner, "owner");
97
99
98 this._owner = opts.owner;
100 this._owner = opts.owner;
99
101
100 if (!(opts.type || opts.factory))
102 if (!(opts.type || opts.factory))
101 throw new Error(
103 throw new Error(
102 "Either a type or a factory must be specified");
104 "Either a type or a factory must be specified");
103
105
104 if (opts.activation)
106 if (opts.activation)
105 this._activationType = opts.activation;
107 this._activationType = opts.activation;
106
108
107 if (opts.type)
109 if (opts.type)
108 this._type = opts.type;
110 this._type = opts.type;
109
111
110 if (opts.params)
112 if (opts.params)
111 this._params = opts.params;
113 this._params = opts.params;
112
114
113 if (opts.inject)
115 if (opts.inject)
114 this._inject = opts.inject;
116 this._inject = opts.inject;
115
117
116 if (opts.services)
118 if (opts.services)
117 this._services = opts.services;
119 this._services = opts.services;
118
120
119 if (opts.factory)
121 if (opts.factory)
120 this._factory = opts.factory;
122 this._factory = opts.factory;
121
123
122 if (opts.cleanup) {
124 if (opts.cleanup) {
123 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
125 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
124 throw new Error(
126 throw new Error(
125 "The cleanup parameter must be either a function or a function name");
127 "The cleanup parameter must be either a function or a function name");
126
128
127 this._cleanup = opts.cleanup;
129 this._cleanup = opts.cleanup;
128 }
130 }
129
131
130 if (this._activationType === ActivationType.Singleton) {
132 if (this._activationType === ActivationType.Singleton) {
131 const tof = this._type || this._factory;
133 const tof = this._type || this._factory;
132
134
133 // create the persistent cache identifier for the type
135 // create the persistent cache identifier for the type
134 if (isPrimitive(tof))
136 if (isPrimitive(tof))
135 this._cacheId = tof;
137 this._cacheId = tof;
136 else
138 else
137 this._cacheId = oid(tof);
139 this._cacheId = oid(tof);
138 } else {
140 } else {
139 this._cacheId = ++cacheId;
141 this._cacheId = ++cacheId;
140 }
142 }
141 }
143 }
142
144
143 activate(context: ActivationContext) {
145 activate(context: ActivationContext) {
144 // if we have a local service records, register them first
146 // if we have a local service records, register them first
145 let instance;
147 let instance;
146
148
147 switch (this._activationType) {
149 switch (this._activationType) {
148 case ActivationType.Singleton: // SINGLETON
150 case ActivationType.Singleton: // SINGLETON
149 // if the value is cached return it
151 // if the value is cached return it
150 if (this._hasInstance)
152 if (this._hasInstance)
151 return this._instance;
153 return this._instance;
152
154
153 // singletons are bound to the root container
155 // singletons are bound to the root container
154 const container = context.container.getRootContainer();
156 const container = context.container.getRootContainer();
155
157
156 if (container.has(this._cacheId)) {
158 if (container.has(this._cacheId)) {
157 instance = container.get(this._cacheId);
159 instance = container.get(this._cacheId);
158 } else {
160 } else {
159 instance = this._create(context);
161 instance = this._create(context);
160 container.store(this._cacheId, instance);
162 container.store(this._cacheId, instance);
161 if (this._cleanup)
163 if (this._cleanup)
162 container.onDispose(
164 container.onDispose(
163 makeClenupCallback(instance, this._cleanup));
165 makeClenupCallback(instance, this._cleanup));
164 }
166 }
165
167
166 this._hasInstance = true;
168 this._hasInstance = true;
167 return (this._instance = instance);
169 return (this._instance = instance);
168
170
169 case ActivationType.Container: // CONTAINER
171 case ActivationType.Container: // CONTAINER
170 // return a cached value
172 // return a cached value
171
173
172 if (this._hasInstance)
174 if (this._hasInstance)
173 return this._instance;
175 return this._instance;
174
176
175 // create an instance
177 // create an instance
176 instance = this._create(context);
178 instance = this._create(context);
177
179
178 // the instance is bound to the container
180 // the instance is bound to the container
179 if (this._cleanup)
181 if (this._cleanup)
180 this._owner.onDispose(
182 this._owner.onDispose(
181 makeClenupCallback(instance, this._cleanup));
183 makeClenupCallback(instance, this._cleanup));
182
184
183 // cache and return the instance
185 // cache and return the instance
184 this._hasInstance = true;
186 this._hasInstance = true;
185 return (this._instance = instance);
187 return (this._instance = instance);
186 case ActivationType.Context: // CONTEXT
188 case ActivationType.Context: // CONTEXT
187 // return a cached value if one exists
189 // return a cached value if one exists
188
190
189 if (context.has(this._cacheId))
191 if (context.has(this._cacheId))
190 return context.get(this._cacheId);
192 return context.get(this._cacheId);
191 // context context activated instances are controlled by callers
193 // context context activated instances are controlled by callers
192 return context.store(this._cacheId, this._create(context));
194 return context.store(this._cacheId, this._create(context));
193 case ActivationType.Call: // CALL
195 case ActivationType.Call: // CALL
194 // per-call created instances are controlled by callers
196 // per-call created instances are controlled by callers
195 return this._create(context);
197 return this._create(context);
196 case ActivationType.Hierarchy: // HIERARCHY
198 case ActivationType.Hierarchy: // HIERARCHY
197 // hierarchy activated instances are behave much like container activated
199 // hierarchy activated instances are behave much like container activated
198 // except they are created and bound to the child container
200 // except they are created and bound to the child container
199
201
200 // return a cached value
202 // return a cached value
201 if (context.container.has(this._cacheId))
203 if (context.container.has(this._cacheId))
202 return context.container.get(this._cacheId);
204 return context.container.get(this._cacheId);
203
205
204 instance = this._create(context);
206 instance = this._create(context);
205
207
206 if (this._cleanup)
208 if (this._cleanup)
207 context.container.onDispose(makeClenupCallback(
209 context.container.onDispose(makeClenupCallback(
208 instance,
210 instance,
209 this._cleanup));
211 this._cleanup));
210
212
211 return context.container.store(this._cacheId, instance);
213 return context.container.store(this._cacheId, instance);
212 default:
214 default:
213 throw new Error("Invalid activation type: " + this._activationType);
215 throw new Error("Invalid activation type: " + this._activationType);
214 }
216 }
215 }
217 }
216
218
217 isInstanceCreated() {
219 isInstanceCreated() {
218 return this._hasInstance;
220 return this._hasInstance;
219 }
221 }
220
222
221 getInstance() {
223 getInstance() {
222 return this._instance;
224 return this._instance;
223 }
225 }
224
226
225 _create(context: ActivationContext) {
227 _create(context: ActivationContext) {
226 trace.debug(`constructing ${context._name}`);
228 trace.debug(`constructing ${context._name}`);
227
229
228 if (this._activationType !== ActivationType.Call &&
230 if (this._activationType !== ActivationType.Call &&
229 context.visit(this._cacheId) > 0)
231 context.visit(this._cacheId) > 0)
230 throw new Error("Recursion detected");
232 throw new Error("Recursion detected");
231
233
232 if (this._services) {
234 if (this._services) {
233 for (const p in this._services)
235 for (const p in this._services)
234 context.register(p, this._services[p]);
236 context.register(p, this._services[p]);
235 }
237 }
236
238
237 let instance;
239 let instance;
238
240
239 if (!this._factory) {
241 if (!this._factory) {
240 const ctor = this._type;
242 const ctor = this._type;
241 if (this._params && this._params.length) {
243 if (this._params && this._params.length) {
242 this._factory = (...args) => {
244 this._factory = (...args) => {
243 const t = Object.create(ctor.prototype);
245 const t = Object.create(ctor.prototype);
244 const inst = ctor.apply(t, args);
246 const inst = ctor.apply(t, args);
245 return isPrimitive(inst) ? t : inst;
247 return isPrimitive(inst) ? t : inst;
246 };
248 };
247 } else {
249 } else {
248 this._factory = () => {
250 this._factory = () => {
249 return new ctor();
251 return new ctor();
250 };
252 };
251 }
253 }
252 }
254 }
253
255
254 if (this._params === undefined) {
256 if (this._params === undefined) {
255 instance = this._factory();
257 instance = this._factory();
256 } else if (this._params instanceof Array) {
258 } else if (this._params instanceof Array) {
257 instance = this._factory.apply(this, _parse(this._params, context, "args"));
259 instance = this._factory.apply(this, _parse(this._params, context, "args"));
258 } else {
260 } else {
259 instance = this._factory(_parse(this._params, context, "args"));
261 instance = this._factory(_parse(this._params, context, "args"));
260 }
262 }
261
263
262 if (this._inject) {
264 if (this._inject) {
263 this._inject.forEach(spec => {
265 this._inject.forEach(spec => {
264 for (const m in spec)
266 for (const m in spec)
265 injectMethod(instance, m, context, spec[m]);
267 injectMethod(instance, m, context, spec[m]);
266 });
268 });
267 }
269 }
268
270
269 return instance;
271 return instance;
270 }
272 }
271
273
272 // @constructor {singleton} foo/bar/Baz
274 // @constructor {singleton} foo/bar/Baz
273 // @factory {singleton}
275 // @factory {singleton}
274 toString() {
276 toString() {
275 const parts = [];
277 const parts = [];
276
278
277 parts.push(this._type ? "@constructor" : "@factory");
279 parts.push(this._type ? "@constructor" : "@factory");
278
280
279 parts.push(ActivationType[this._activationType]);
281 parts.push(ActivationType[this._activationType]);
280
282
281 if (typeof (this._type) === "string")
283 if (typeof (this._type) === "string")
282 parts.push(this._type);
284 parts.push(this._type);
283
285
284 return parts.join(" ");
286 return parts.join(" ");
285 }
287 }
286 }
288 }
@@ -1,66 +1,66
1 import { isNull, isPrimitive } from "../safe";
1 import { isNull, isPrimitive } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { Constructor, Factory } from "../interfaces";
3 import { Constructor, Factory } from "../interfaces";
4
4
5 export interface Descriptor {
5 export interface Descriptor {
6 activate(context: ActivationContext, name?: string);
6 activate(context: ActivationContext, name?: string);
7 }
7 }
8
8
9 export function isDescriptor(x): x is Descriptor {
9 export function isDescriptor(x): x is Descriptor {
10 return (!isPrimitive(x)) &&
10 return (!isPrimitive(x)) &&
11 (x.activate instanceof Function);
11 (x.activate instanceof Function);
12 }
12 }
13
13
14 export interface ServiceMap {
14 export interface ServiceMap {
15 [s: string]: Descriptor;
15 [s: string]: Descriptor;
16 }
16 }
17
17
18 export enum ActivationType {
18 export enum ActivationType {
19 Singleton,
19 Singleton,
20 Container,
20 Container,
21 Hierarchy,
21 Hierarchy,
22 Context,
22 Context,
23 Call
23 Call
24 }
24 }
25
25
26 export interface RegistrationWithServices {
26 export interface RegistrationWithServices {
27 services?: object;
27 services?: object;
28 }
28 }
29
29
30 export interface ServiceRegistration extends RegistrationWithServices {
30 export interface ServiceRegistration extends RegistrationWithServices {
31 $type?: string | Constructor;
31 $type?: string | Constructor;
32
32
33 $factory?: string | Factory;
33 $factory?: string | Factory;
34
34
35 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
35 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
36
36
37 params?;
37 params?;
38
38
39 inject?: object | object[];
39 inject?: object | object[];
40
40
41 cleanup: (instance) => void | string;
41 cleanup: (instance) => void | string;
42 }
42 }
43
43
44 export interface ValueRegistration {
44 export interface ValueRegistration {
45 $value;
45 $value;
46 parse?: boolean;
46 parse?: boolean;
47 }
47 }
48
48
49 export interface DependencyRegistration extends RegistrationWithServices {
49 export interface DependencyRegistration extends RegistrationWithServices {
50 $dependency: string;
50 $dependency: string;
51 lazy?: boolean;
51 lazy?: boolean;
52 optional?: boolean;
52 optional?: boolean;
53 default?;
53 default?;
54 }
54 }
55
55
56 export function isServiceRegistration(x): x is ServiceRegistration {
56 export function isServiceRegistration(x): x is ServiceRegistration {
57 return (!isPrimitive(x)) && ("$type" in x || "$factory" in x);
57 return (!isPrimitive(x)) && ("$type" in x || "$factory" in x);
58 }
58 }
59
59
60 export function isValueRegistration(x): x is ValueRegistration {
60 export function isValueRegistration(x): x is ValueRegistration {
61 return (!isPrimitive(x)) && ("$value" in x);
61 return (!isPrimitive(x)) && ("$value" in 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,188 +1,187
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
8 export const LogLevel = 300;
8 export const LogLevel = 300;
9
9
10 export const WarnLevel = 200;
10 export const WarnLevel = 200;
11
11
12 export const ErrorLevel = 100;
12 export const ErrorLevel = 100;
13
13
14 export const SilentLevel = 0;
14 export const SilentLevel = 0;
15
15
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;
27 }
27 }
28 }
28 }
29
29
30 class Registry {
30 class Registry {
31 static readonly instance = new Registry();
31 static readonly instance = new Registry();
32
32
33 private _registry: object = new Object();
33 private _registry: object = new Object();
34 private _listeners: object = new Object();
34 private _listeners: object = new Object();
35 private _nextCookie: number = 1;
35 private _nextCookie: number = 1;
36
36
37 get(id: any): TraceSource {
37 get(id: any): TraceSource {
38 argumentNotNull(id, "id");
38 argumentNotNull(id, "id");
39
39
40 if (this._registry[id])
40 if (this._registry[id])
41 return this._registry[id];
41 return this._registry[id];
42
42
43 var source = new TraceSource(id);
43 var source = new TraceSource(id);
44 this._registry[id] = source;
44 this._registry[id] = source;
45 this._onNewSource(source);
45 this._onNewSource(source);
46
46
47 return source;
47 return source;
48 }
48 }
49
49
50 add(id: any, source: TraceSource) {
50 add(id: any, source: TraceSource) {
51 argumentNotNull(id, "id");
51 argumentNotNull(id, "id");
52 argumentNotNull(source, "source");
52 argumentNotNull(source, "source");
53
53
54 this._registry[id] = source;
54 this._registry[id] = source;
55 this._onNewSource(source);
55 this._onNewSource(source);
56 }
56 }
57
57
58 _onNewSource(source: TraceSource) {
58 _onNewSource(source: TraceSource) {
59 for (let i in this._listeners)
59 for (let i in this._listeners)
60 this._listeners[i].call(null, source);
60 this._listeners[i].call(null, source);
61 }
61 }
62
62
63 on(handler: (source: TraceSource) => void): IDestroyable {
63 on(handler: (source: TraceSource) => void): IDestroyable {
64 argumentNotNull(handler, "handler");
64 argumentNotNull(handler, "handler");
65 var me = this;
65 var me = this;
66
66
67 var cookie = this._nextCookie++;
67 var cookie = this._nextCookie++;
68
68
69 this._listeners[cookie] = handler;
69 this._listeners[cookie] = handler;
70
70
71 for (let i in this._registry)
71 for (let i in this._registry)
72 handler(this._registry[i]);
72 handler(this._registry[i]);
73
73
74 return {
74 return {
75 destroy() {
75 destroy() {
76 delete me._listeners[cookie];
76 delete me._listeners[cookie];
77 }
77 }
78 };
78 };
79 }
79 }
80 }
80 }
81
81
82 export class TraceSource {
82 export class TraceSource {
83 readonly id: any
83 readonly id: any
84
84
85 level: number
85 level: number
86
86
87 readonly events: Observable<TraceEvent>
87 readonly events: Observable<TraceEvent>
88
88
89 _notifyNext: (arg: TraceEvent) => void
89 _notifyNext: (arg: TraceEvent) => void
90
90
91 constructor(id: any) {
91 constructor(id: any) {
92
92
93 this.id = id || new Object();
93 this.id = id || new Object();
94 this.events = new Observable((next) => {
94 this.events = new Observable((next) => {
95 this._notifyNext = next;
95 this._notifyNext = next;
96 })
96 })
97 }
97 }
98
98
99 protected emit(level: number, arg: any) {
99 protected emit(level: number, arg: any) {
100 this._notifyNext(new TraceEvent(this, level, arg));
100 this._notifyNext(new TraceEvent(this, level, arg));
101 }
101 }
102
102
103 isDebugEnabled() {
103 isDebugEnabled() {
104 return this.level >= DebugLevel;
104 return this.level >= DebugLevel;
105 }
105 }
106
106
107 debug(msg: string, ...args: any[]) {
107 debug(msg: string, ...args: any[]) {
108 if (this.isEnabled(DebugLevel))
108 if (this.isEnabled(DebugLevel))
109 this.emit(DebugLevel, format.apply(null, arguments));
109 this.emit(DebugLevel, format.apply(null, arguments));
110 }
110 }
111
111
112 isLogEnabled() {
112 isLogEnabled() {
113 return this.level >= LogLevel;
113 return this.level >= LogLevel;
114 }
114 }
115
115
116 log(msg: string, ...args: any[]) {
116 log(msg: string, ...args: any[]) {
117 if (this.isEnabled(LogLevel))
117 if (this.isEnabled(LogLevel))
118 this.emit(LogLevel, format.apply(null, arguments));
118 this.emit(LogLevel, format.apply(null, arguments));
119 }
119 }
120
120
121 isWarnEnabled() {
121 isWarnEnabled() {
122 return this.level >= WarnLevel;
122 return this.level >= WarnLevel;
123 }
123 }
124
124
125 warn(msg: string, ...args: any[]) {
125 warn(msg: string, ...args: any[]) {
126 if (this.isEnabled(WarnLevel))
126 if (this.isEnabled(WarnLevel))
127 this.emit(WarnLevel, format.apply(null, arguments));
127 this.emit(WarnLevel, format.apply(null, arguments));
128 }
128 }
129
129
130 /**
130 /**
131 * returns true if errors will be recorded.
131 * returns true if errors will be recorded.
132 */
132 */
133 isErrorEnabled() {
133 isErrorEnabled() {
134 return this.level >= ErrorLevel;
134 return this.level >= ErrorLevel;
135 }
135 }
136
136
137 /**
137 /**
138 * Traces a error.
138 * Traces a error.
139 *
139 *
140 * @param msg the message.
140 * @param msg the message.
141 * @param args parameters which will be substituted in the message.
141 * @param args parameters which will be substituted in the message.
142 */
142 */
143 error(msg: string, ...args: any[]) {
143 error(msg: string, ...args: any[]) {
144 if (this.isEnabled(ErrorLevel))
144 if (this.isEnabled(ErrorLevel))
145 this.emit(ErrorLevel, format.apply(null, arguments));
145 this.emit(ErrorLevel, format.apply(null, arguments));
146 }
146 }
147
147
148 /**
148 /**
149 * Checks whether the specified level is enabled for this
149 * Checks whether the specified level is enabled for this
150 * trace source.
150 * trace source.
151 *
151 *
152 * @param level the trace level which should be checked.
152 * @param level the trace level which should be checked.
153 */
153 */
154 isEnabled(level: number) {
154 isEnabled(level: number) {
155 return (this.level >= level);
155 return (this.level >= level);
156 }
156 }
157
157
158 /**
158 /**
159 * Traces a raw event, passing data as it is to the underlying listeners
159 * Traces a raw event, passing data as it is to the underlying listeners
160 *
160 *
161 * @param level the level of the event
161 * @param level the level of the event
162 * @param arg the data of the event, can be a simple string or any object.
162 * @param arg the data of the event, can be a simple string or any object.
163 */
163 */
164 traceEvent(level: number, arg: any) {
164 traceEvent(level: number, arg: any) {
165 if (this.isEnabled(level))
165 if (this.isEnabled(level))
166 this.emit(level, arg);
166 this.emit(level, arg);
167 }
167 }
168
168
169 /**
169 /**
170 * Register the specified handler to be called for every new and already
170 * Register the specified handler to be called for every new and already
171 * created trace source.
171 * created trace source.
172 *
172 *
173 * @param handler the handler which will be called for each trace source
173 * @param handler the handler which will be called for each trace source
174 */
174 */
175 static on(handler: (source: TraceSource) => void) {
175 static on(handler: (source: TraceSource) => void) {
176 return Registry.instance.on(handler);
176 return Registry.instance.on(handler);
177 }
177 }
178
178
179 /**
179 /**
180 * Creates or returns already created trace source for the specified id.
180 * Creates or returns already created trace source for the specified id.
181 *
181 *
182 * @param id the id for the trace source
182 * @param id the id for the trace source
183 */
183 */
184 static get(id: any) {
184 static get(id: any) {
185 return Registry.instance.get(id);
185 return Registry.instance.get(id);
186 }
186 }
187 }
187 }
188
@@ -1,74 +1,118
1 import { test, TapeWriter } from "./TestTraits";
1 import { test, TapeWriter } from "./TestTraits";
2 import { Container } from "@implab/core/di/Container";
2 import { Container } from "@implab/core/di/Container";
3 import { ReferenceDescriptor } from "@implab/core/di/ReferenceDescriptor";
3 import { ReferenceDescriptor } from "@implab/core/di/ReferenceDescriptor";
4 import { AggregateDescriptor } from "@implab/core/di/AggregateDescriptor";
4 import { AggregateDescriptor } from "@implab/core/di/AggregateDescriptor";
5 import { ValueDescriptor } from "@implab/core/di/ValueDescriptor";
5 import { ValueDescriptor } from "@implab/core/di/ValueDescriptor";
6 import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
6 import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
7 import { Foo } from "./mock/Foo";
7 import { Foo } from "./mock/Foo";
8 import { Bar } from "./mock/Bar";
8 import { Bar } from "./mock/Bar";
9 import { isNull } from "@implab/core/safe";
9 import { isNull } from "@implab/core/safe";
10
10
11 test("Container register/resolve tests", async t => {
11 test("Container register/resolve tests", async t => {
12 const writer = new TapeWriter(t);
12 const writer = new TapeWriter(t);
13
13
14 TraceSource.on(ts => {
14 TraceSource.on(ts => {
15 ts.level = DebugLevel;
15 ts.level = DebugLevel;
16 writer.writeEvents(ts.events);
16 writer.writeEvents(ts.events);
17 });
17 });
18
18
19 const container = new Container();
19 const container = new Container();
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");
34
35 t.doesNotThrow(
36 () => container.register(
28 "dbParams",
37 "dbParams",
29 new AggregateDescriptor({
38 new AggregateDescriptor({
30 timeout: 10,
39 timeout: 10,
31 connection: new ReferenceDescriptor({ name: "connection" })
40 connection: new ReferenceDescriptor({ name: "connection" })
32 })
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 });
40
51
41 test("Container configure/resolve tests", async t => {
52 test("Container configure/resolve tests", async t => {
42 const writer = new TapeWriter(t);
53 const writer = new TapeWriter(t);
43
54
44 TraceSource.on(ts => {
55 TraceSource.on(ts => {
45 ts.level = DebugLevel;
56 ts.level = DebugLevel;
46 writer.writeEvents(ts.events);
57 writer.writeEvents(ts.events);
47 });
58 });
48
59
49 const container = new Container();
60 const container = new Container();
50
61
51 await container.configure({
62 await container.configure({
52 foo: {
63 foo: {
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: {
59 db: {
77 db: {
60 provider: {
78 provider: {
61 $dependency: "db"
79 $dependency: "db"
62 }
80 }
63 }
81 }
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