##// END OF EJS Templates
tests
cin -
r41:eae7e609c38a di-typescript
parent child
Show More
@@ -0,0 +1,3
1 export class Bar {
2 name = "bar";
3 }
@@ -0,0 +1,3
1 export class Foo {
2 name = "foo";
3 }
@@ -1,4 +1,4
1 Copyright 2017-2018 Implab team
1 Copyright 2017-2019 Implab team
2
2
3 Redistribution and use in source and binary forms, with or without
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are met:
4 modification, are permitted provided that the following conditions are met:
@@ -83,7 +83,7
83 },
83 },
84 "duplexer": {
84 "duplexer": {
85 "version": "0.1.1",
85 "version": "0.1.1",
86 "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
86 "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
87 "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
87 "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
88 "dev": true
88 "dev": true
89 },
89 },
@@ -128,7 +128,7
128 "dependencies": {
128 "dependencies": {
129 "tape": {
129 "tape": {
130 "version": "2.3.3",
130 "version": "2.3.3",
131 "resolved": "http://registry.npmjs.org/tape/-/tape-2.3.3.tgz",
131 "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz",
132 "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=",
132 "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=",
133 "dev": true,
133 "dev": true,
134 "requires": {
134 "requires": {
@@ -288,7 +288,7
288 },
288 },
289 "path-is-absolute": {
289 "path-is-absolute": {
290 "version": "1.0.1",
290 "version": "1.0.1",
291 "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
291 "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
292 "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
292 "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
293 "dev": true
293 "dev": true
294 },
294 },
@@ -300,7 +300,7
300 },
300 },
301 "readable-stream": {
301 "readable-stream": {
302 "version": "1.1.14",
302 "version": "1.1.14",
303 "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
303 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
304 "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
304 "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
305 "dev": true,
305 "dev": true,
306 "requires": {
306 "requires": {
@@ -353,7 +353,7
353 },
353 },
354 "string_decoder": {
354 "string_decoder": {
355 "version": "0.10.31",
355 "version": "0.10.31",
356 "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
356 "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
357 "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
357 "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
358 "dev": true
358 "dev": true
359 },
359 },
@@ -410,13 +410,13
410 },
410 },
411 "through": {
411 "through": {
412 "version": "2.3.8",
412 "version": "2.3.8",
413 "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
413 "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
414 "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
414 "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
415 "dev": true
415 "dev": true
416 },
416 },
417 "through2": {
417 "through2": {
418 "version": "0.2.3",
418 "version": "0.2.3",
419 "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
419 "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
420 "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
420 "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
421 "dev": true,
421 "dev": true,
422 "requires": {
422 "requires": {
@@ -8,7 +8,7 const trace = TraceSource.get("@implab/c
8 export interface ActivationContextInfo {
8 export interface ActivationContextInfo {
9 name: string;
9 name: string;
10
10
11 service: Descriptor;
11 service: string;
12
12
13 scope: ServiceMap;
13 scope: ServiceMap;
14 }
14 }
@@ -22,12 +22,17 export class ActivationContext {
22
22
23 _visited: object;
23 _visited: object;
24
24
25 _name: string;
26
27 _localized: boolean;
28
25 container: Container;
29 container: Container;
26
30
27 constructor(container: Container, services: ServiceMap, cache?: object, visited?) {
31 constructor(container: Container, services: ServiceMap, name?: string, cache?: object, visited?) {
28 argumentNotNull(container, "container");
32 argumentNotNull(container, "container");
29 argumentNotNull(services, "services");
33 argumentNotNull(services, "services");
30
34
35 this._name = name;
31 this._visited = visited || {};
36 this._visited = visited || {};
32 this._stack = [];
37 this._stack = [];
33 this._cache = cache || {};
38 this._cache = cache || {};
@@ -35,7 +40,11 export class ActivationContext {
35 this.container = container;
40 this.container = container;
36 }
41 }
37
42
38 getService(name, def?): any {
43 getName() {
44 return this._name;
45 }
46
47 resolve(name, def?): any {
39 const d = this._services[name];
48 const d = this._services[name];
40
49
41 if (!d)
50 if (!d)
@@ -44,7 +53,7 export class ActivationContext {
44 else
53 else
45 throw new Error(`Service ${name} not found`);
54 throw new Error(`Service ${name} not found`);
46
55
47 return isDescriptor(d) ? d.activate(this, name) : d;
56 return this.activate(d, name);
48 }
57 }
49
58
50 /**
59 /**
@@ -62,7 +71,8 export class ActivationContext {
62 clone() {
71 clone() {
63 return new ActivationContext(
72 return new ActivationContext(
64 this.container,
73 this.container,
65 Object.create(this._services),
74 this._services,
75 this._name,
66 this._cache,
76 this._cache,
67 this._visited
77 this._visited
68 );
78 );
@@ -80,25 +90,18 export class ActivationContext {
80 return (this._cache[id] = value);
90 return (this._cache[id] = value);
81 }
91 }
82
92
83 parse(data, name: string) {
93 activate(d: Descriptor, name: string) {
84 if (isPrimitive(data))
94 if (trace.isLogEnabled())
85 return data;
95 trace.log(`enter ${name} ${d}`);
86
96
87 if (isDescriptor(data)) {
97 this.enter(name, d.toString());
88 return data.activate(this, name);
98 const v = d.activate(this);
89 } else if (data instanceof Array) {
90 this.enter(name);
91 const v = data.map( (x, i) => this.parse(x, `[${i}]`));
92 this.leave();
99 this.leave();
100
101 if (trace.isLogEnabled())
102 trace.log(`leave ${name}`);
103
93 return v;
104 return v;
94 } else {
95 this.enter(name);
96 const result = {};
97 for (const p in data)
98 result[p] = this.parse(data[p], "." + p);
99 this.leave();
100 return result;
101 }
102 }
105 }
103
106
104 visit(id: string) {
107 visit(id: string) {
@@ -111,24 +114,19 export class ActivationContext {
111 return this._stack.slice().reverse();
114 return this._stack.slice().reverse();
112 }
115 }
113
116
114 enter(name: string, d?: Descriptor, localize?: boolean) {
117 private enter(name: string, service: string) {
115 if (trace.isLogEnabled())
116 trace.log("enter " + name + " " + (d || "") +
117 (localize ? " localize" : ""));
118 this._stack.push({
118 this._stack.push({
119 name,
119 name,
120 service: d,
120 service,
121 scope: this._services
121 scope: this._services
122 });
122 });
123 if (localize)
123 this._name = name;
124 this._services = Object.create(this._services);
124 this._services = Object.create(this._services);
125 }
125 }
126
126
127 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
130 this._name = ctx.name;
131 if (trace.isLogEnabled())
132 trace.log("leave " + ctx.name + " " + (ctx.service || ""));
133 }
131 }
134 }
132 }
@@ -27,7 +27,7 export class ActivationError {
27 if (this.activationStack) {
27 if (this.activationStack) {
28 parts.push("at");
28 parts.push("at");
29 this.activationStack
29 this.activationStack
30 .forEach(x => parts.push(` ${x.name} ${x.service ? x.service.toString() : ""}`));
30 .forEach(x => parts.push(` ${x.name} ${x.service}`));
31
31
32 }
32 }
33
33
@@ -1,5 +1,6
1 import { Descriptor } from "./interfaces";
1 import { Descriptor, isDescriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { isPrimitive } from "util";
3
4
4 export class AggregateDescriptor implements Descriptor {
5 export class AggregateDescriptor implements Descriptor {
5 _value: object;
6 _value: object;
@@ -8,17 +9,29 export class AggregateDescriptor impleme
8 this._value = value;
9 this._value = value;
9 }
10 }
10
11
11 activate(context: ActivationContext, name: string) {
12 activate(context: ActivationContext) {
12 context.enter(name);
13 return this._parse(this._value, context, "$value");
13 const v = context.parse(this._value, ".params");
14 context.leave();
15 return v;
16 }
14 }
17
15
18 isInstanceCreated(): boolean {
16 // TODO: make async
19 return false;
17 _parse(value, context: ActivationContext, path: string) {
18 if (isPrimitive(value))
19 return value;
20
21 if (isDescriptor(value))
22 return context.activate(value, path);
23
24 if (value instanceof Array)
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
26
27 const t = {};
28 for (const p of Object.keys(value))
29 t[p] = this._parse(value[p], context, `${path}.${p}`);
30 return t;
31
20 }
32 }
21 getInstance(): any {
33
22 throw new Error("Not supported");
34 toString() {
35 return "@walk";
23 }
36 }
24 }
37 }
@@ -1,13 +1,14
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 } from "./interfaces";
4 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration, DependencyRegistration } from "./interfaces";
5 import { AggregateDescriptor } from "./AggregateDescriptor";
5 import { AggregateDescriptor } from "./AggregateDescriptor";
6 import { isPrimitive } 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
12
12 export class Container {
13 export class Container {
13 _services: ServiceMap;
14 _services: ServiceMap;
@@ -39,35 +40,36 export class Container {
39 return this._parent;
40 return this._parent;
40 }
41 }
41
42
42 getService(name: string, def?) {
43 resolve(name: string, def?) {
43 const d = this._services[name];
44 const d = this._services[name];
44 if (!d)
45 if (d === undefined) {
45 if (arguments.length > 1)
46 if (arguments.length > 1)
46 return def;
47 return def;
47 else
48 else
48 throw new Error("Service '" + name + "' isn't found");
49 throw new Error("Service '" + name + "' isn't found");
49
50 }
50 if (!isDescriptor(d))
51 return d;
52
53 if (d.isInstanceCreated())
54 return d.getInstance();
55
51
56 const context = new ActivationContext(this, this._services);
52 const context = new ActivationContext(this, this._services);
57
58 try {
53 try {
59 return d.activate(context, name);
54 return context.activate(d, name);
60 } catch (error) {
55 } catch (error) {
61 throw new ActivationError(name, context.getStack(), error);
56 throw new ActivationError(name, context.getStack(), error);
62 }
57 }
63 }
58 }
64
59
60 getService(name: string, def?) {
61 return this.resolve.apply(this, arguments);
62 }
63
65 register(nameOrCollection, service?) {
64 register(nameOrCollection, service?) {
66 if (arguments.length === 1) {
65 if (arguments.length === 1) {
67 const data = nameOrCollection;
66 const data = nameOrCollection;
68 for (const name in data)
67 for (const name in data)
69 this.register(name, data[name]);
68 this.register(name, data[name]);
70 } else {
69 } else {
70 if (!isDescriptor(service))
71 throw new Error("The service parameter must be a descriptor");
72
71 this._services[nameOrCollection] = service;
73 this._services[nameOrCollection] = service;
72 }
74 }
73 return this;
75 return this;
@@ -126,19 +128,7 export class Container {
126 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
128 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
127 const resolver = (opts && opts.resolver) || this._resolver;
129 const resolver = (opts && opts.resolver) || this._resolver;
128
130
129 const services: ServiceMap = {};
131 const services = await this._parseRegistrations(data, resolver);
130
131 resolver.beginBatch();
132
133 async function parse(k) {
134 services[k] = await this._parse(data[k], resolver);
135 }
136
137 const batch = Object.keys(data).map(parse);
138
139 resolver.completeBatch();
140
141 await Promise.all(batch);
142
132
143 this.register(services);
133 this.register(services);
144 }
134 }
@@ -148,17 +138,8 export class Container {
148 return registration;
138 return registration;
149
139
150 if (isDependencyRegistration(registration)) {
140 if (isDependencyRegistration(registration)) {
151
141 return this._paseReference(registration, resolver);
152 return new ReferenceDescriptor({
153 name: registration.$dependency,
154 lazy: registration.lazy,
155 optional: registration.optional,
156 default: registration.default,
157 services: registration.services && this._parseObject(registration.services, resolver)
158 });
159
160 } else if (isValueRegistration(registration)) {
142 } else if (isValueRegistration(registration)) {
161
162 return !registration.parse ?
143 return !registration.parse ?
163 new ValueDescriptor(registration.$value) :
144 new ValueDescriptor(registration.$value) :
164 new AggregateDescriptor(this._parse(registration.$value, resolver));
145 new AggregateDescriptor(this._parse(registration.$value, resolver));
@@ -172,6 +153,16 export class Container {
172 return this._parseObject(registration, resolver);
153 return this._parseObject(registration, resolver);
173 }
154 }
174
155
156 async _paseReference(registration: DependencyRegistration, resolver: ModuleResolverBase) {
157 return new ReferenceDescriptor({
158 name: registration.$dependency,
159 lazy: registration.lazy,
160 optional: registration.optional,
161 default: registration.default,
162 services: registration.services && await this._parseRegistrations(registration.services, resolver)
163 });
164 }
165
175 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
166 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
176 const opts: ServiceDescriptorParams = {
167 const opts: ServiceDescriptorParams = {
177 owner: this
168 owner: this
@@ -194,12 +185,14 export class Container {
194 }
185 }
195
186
196 if (data.services)
187 if (data.services)
197 opts.services = await this._parseObject(data.services, resolver);
188 opts.services = await this._parseRegistrations(data.services, resolver);
198
189
190 if (data.inject) {
199 if (data.inject instanceof Array)
191 if (data.inject instanceof Array)
200 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
192 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
201 else
193 else
202 opts.inject = [await this._parseObject(data.inject, resolver)];
194 opts.inject = [await this._parseObject(data.inject, resolver)];
195 }
203
196
204 if (data.params)
197 if (data.params)
205 opts.params = this._parse(data.params, resolver);
198 opts.params = this._parse(data.params, resolver);
@@ -237,7 +230,7 export class Container {
237 return new ServiceDescriptor(opts);
230 return new ServiceDescriptor(opts);
238 }
231 }
239
232
240 _parseObject(data: object, resolver: ModuleResolverBase) {
233 async _parseObject(data: object, resolver: ModuleResolverBase) {
241 if (data.constructor &&
234 if (data.constructor &&
242 data.constructor.prototype !== Object.prototype)
235 data.constructor.prototype !== Object.prototype)
243 return new ValueDescriptor(data);
236 return new ValueDescriptor(data);
@@ -245,16 +238,42 export class Container {
245 const o = {};
238 const o = {};
246
239
247 for (const p in data)
240 for (const p in data)
248 o[p] = this._parse(data[p], resolver);
241 o[p] = await this._parse(data[p], resolver);
242
243 // TODO: handle inline descriptors properly
244 // const ex = {
245 // activate(ctx) {
246 // const value = ctx.activate(this.prop, "prop");
247 // // some code
248 // },
249
250 // // will be turned to ReferenceDescriptor
251 // prop: { $dependency: "depName" }
252 // };
249
253
250 return o;
254 return o;
251 }
255 }
252
256
253 _parseArray(data: Array<any>, resolver: ModuleResolverBase) {
257 async _parseArray(data: Array<any>, resolver: ModuleResolverBase) {
254 if (data.constructor &&
258 if (data.constructor &&
255 data.constructor.prototype !== Array.prototype)
259 data.constructor.prototype !== Array.prototype)
256 return new ValueDescriptor(data);
260 return new ValueDescriptor(data);
257
261
258 return data.map(x => this._parse(x, resolver));
262 return pmap(data, x => this._parse(x, resolver));
263 }
264
265 async _parseRegistrations(data: object, resolver: ModuleResolverBase) {
266 if (data.constructor &&
267 data.constructor.prototype !== Object.prototype)
268 throw new Error("Registrations must be a simple object");
269
270 const o: ServiceMap = {};
271
272 for (const p of Object.keys(data)) {
273 const v = await this._parse(data[p], resolver);
274 o[p] = isDescriptor(v) ? v : new AggregateDescriptor(v);
275 }
276
277 return o;
259 }
278 }
260 }
279 }
@@ -27,41 +27,37 export class ReferenceDescriptor impleme
27 this._name = opts.name;
27 this._name = opts.name;
28 this._lazy = !!opts.lazy;
28 this._lazy = !!opts.lazy;
29 this._optional = !!opts.optional;
29 this._optional = !!opts.optional;
30 this._default = !!opts.default;
30 this._default = opts.default;
31 this._services = opts.services;
31 this._services = opts.services;
32 }
32 }
33
33
34 activate(context: ActivationContext, name: string) {
34 activate(context: ActivationContext, name: string) {
35
36 if (this._lazy) {
37 // сохраняем контекст активации
38 context = context.clone();
39
40 // добавляем сервисы
35 // добавляем сервисы
41 if (this._services) {
36 if (this._services) {
42 for (const p of Object.keys(this._services))
37 for (const p of Object.keys(this._services))
43 context.register(p, this._services[p]);
38 context.register(p, this._services[p]);
44 }
39 }
45
40
41 if (this._lazy) {
42 const saved = context.clone();
43
46 return (cfg: ServiceMap) => {
44 return (cfg: ServiceMap) => {
47 // защищаем контекст на случай исключения в процессе
45 // защищаем контекст на случай исключения в процессе
48 // активации
46 // активации
49 const ct = context.clone();
47 const ct = saved.clone();
50 try {
48 try {
51 if (cfg) {
49 if (cfg) {
52 for (const k in cfg)
50 for (const k in cfg)
53 ct.register(k, cfg[k]);
51 ct.register(k, cfg[k]);
54 }
52 }
55
53
56 return this._optional ? ct.getService(this._name, this._default) : ct
54 return this._optional ? ct.resolve(this._name, this._default) : ct
57 .getService(this._name);
55 .resolve(this._name);
58 } catch (error) {
56 } catch (error) {
59 throw new ActivationError(this._name, ct.getStack(), error);
57 throw new ActivationError(this._name, ct.getStack(), error);
60 }
58 }
61 };
59 };
62 } else {
60 } else {
63 context.enter(name, this, !!this._services);
64
65 // добавляем сервисы
61 // добавляем сервисы
66 if (this._services) {
62 if (this._services) {
67 for (const p of Object.keys(this._services))
63 for (const p of Object.keys(this._services))
@@ -69,23 +65,13 export class ReferenceDescriptor impleme
69 }
65 }
70
66
71 const v = this._optional ?
67 const v = this._optional ?
72 context.getService(this._name, this._default) :
68 context.resolve(this._name, this._default) :
73 context.getService(this._name);
69 context.resolve(this._name);
74
75 context.leave();
76
70
77 return v;
71 return v;
78 }
72 }
79 }
73 }
80
74
81 isInstanceCreated() {
82 return false;
83 }
84
85 getInstance() {
86 throw new Error("The reference descriptor doesn't allowed to hold an instance");
87 }
88
89 toString() {
75 toString() {
90 const opts = [];
76 const opts = [];
91 if (this._optional)
77 if (this._optional)
@@ -1,11 +1,14
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap } 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
7
7 let cacheId = 0;
8 let cacheId = 0;
8
9
10 const trace = TraceSource.get("@implab/core/di/ActivationContext");
11
9 function injectMethod(target, method, context, args) {
12 function injectMethod(target, method, context, args) {
10 const m = target[method];
13 const m = target[method];
11 if (!m)
14 if (!m)
@@ -29,6 +32,24 function makeClenupCallback(target, meth
29 }
32 }
30 }
33 }
31
34
35 // TODO: make async
36 function _parse(value, context: ActivationContext, path: string) {
37 if (isPrimitive(value))
38 return value;
39
40 if (isDescriptor(value))
41 return context.activate(value, path);
42
43 if (value instanceof Array)
44 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
45
46 const t = {};
47 for (const p of Object.keys(value))
48 t[p] = this._parse(value[p], context, `${path}.${p}`);
49 return t;
50
51 }
52
32 export interface ServiceDescriptorParams {
53 export interface ServiceDescriptorParams {
33 activation?: ActivationType;
54 activation?: ActivationType;
34
55
@@ -119,7 +140,7 export class ServiceDescriptor implement
119 }
140 }
120 }
141 }
121
142
122 activate(context: ActivationContext, name: string) {
143 activate(context: ActivationContext) {
123 // if we have a local service records, register them first
144 // if we have a local service records, register them first
124 let instance;
145 let instance;
125
146
@@ -135,7 +156,7 export class ServiceDescriptor implement
135 if (container.has(this._cacheId)) {
156 if (container.has(this._cacheId)) {
136 instance = container.get(this._cacheId);
157 instance = container.get(this._cacheId);
137 } else {
158 } else {
138 instance = this._create(context, name);
159 instance = this._create(context);
139 container.store(this._cacheId, instance);
160 container.store(this._cacheId, instance);
140 if (this._cleanup)
161 if (this._cleanup)
141 container.onDispose(
162 container.onDispose(
@@ -152,7 +173,7 export class ServiceDescriptor implement
152 return this._instance;
173 return this._instance;
153
174
154 // create an instance
175 // create an instance
155 instance = this._create(context, name);
176 instance = this._create(context);
156
177
157 // the instance is bound to the container
178 // the instance is bound to the container
158 if (this._cleanup)
179 if (this._cleanup)
@@ -168,12 +189,10 export class ServiceDescriptor implement
168 if (context.has(this._cacheId))
189 if (context.has(this._cacheId))
169 return context.get(this._cacheId);
190 return context.get(this._cacheId);
170 // context context activated instances are controlled by callers
191 // context context activated instances are controlled by callers
171 return context.store(this._cacheId, this._create(
192 return context.store(this._cacheId, this._create(context));
172 context,
173 name));
174 case ActivationType.Call: // CALL
193 case ActivationType.Call: // CALL
175 // per-call created instances are controlled by callers
194 // per-call created instances are controlled by callers
176 return this._create(context, name);
195 return this._create(context);
177 case ActivationType.Hierarchy: // HIERARCHY
196 case ActivationType.Hierarchy: // HIERARCHY
178 // hierarchy activated instances are behave much like container activated
197 // hierarchy activated instances are behave much like container activated
179 // except they are created and bound to the child container
198 // except they are created and bound to the child container
@@ -182,7 +201,7 export class ServiceDescriptor implement
182 if (context.container.has(this._cacheId))
201 if (context.container.has(this._cacheId))
183 return context.container.get(this._cacheId);
202 return context.container.get(this._cacheId);
184
203
185 instance = this._create(context, name);
204 instance = this._create(context);
186
205
187 if (this._cleanup)
206 if (this._cleanup)
188 context.container.onDispose(makeClenupCallback(
207 context.container.onDispose(makeClenupCallback(
@@ -203,8 +222,8 export class ServiceDescriptor implement
203 return this._instance;
222 return this._instance;
204 }
223 }
205
224
206 _create(context, name) {
225 _create(context: ActivationContext) {
207 context.enter(name, this, Boolean(this._services));
226 trace.debug(`constructing ${context._name}`);
208
227
209 if (this._activationType !== ActivationType.Call &&
228 if (this._activationType !== ActivationType.Call &&
210 context.visit(this._cacheId) > 0)
229 context.visit(this._cacheId) > 0)
@@ -235,13 +254,9 export class ServiceDescriptor implement
235 if (this._params === undefined) {
254 if (this._params === undefined) {
236 instance = this._factory();
255 instance = this._factory();
237 } else if (this._params instanceof Array) {
256 } else if (this._params instanceof Array) {
238 instance = this._factory.apply(this, context.parse(
257 instance = this._factory.apply(this, _parse(this._params, context, "args"));
239 this._params,
240 ".params"));
241 } else {
258 } else {
242 instance = this._factory(context.parse(
259 instance = this._factory(_parse(this._params, context, "args"));
243 this._params,
244 ".params"));
245 }
260 }
246
261
247 if (this._inject) {
262 if (this._inject) {
@@ -251,8 +266,6 export class ServiceDescriptor implement
251 });
266 });
252 }
267 }
253
268
254 context.leave();
255
256 return instance;
269 return instance;
257 }
270 }
258
271
@@ -1,5 +1,4
1 import { Descriptor } from "./interfaces";
1 import { Descriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
3
2
4 export class ValueDescriptor implements Descriptor {
3 export class ValueDescriptor implements Descriptor {
5 _value;
4 _value;
@@ -8,16 +7,11 export class ValueDescriptor implements
8 this._value = value;
7 this._value = value;
9 }
8 }
10
9
11 activate(context: ActivationContext, name: string) {
10 activate() {
12 context.enter(name);
13 const v = this._value;
14 context.leave();
15 return v;
16 }
17 isInstanceCreated(): boolean {
18 return true;
19 }
20 getInstance() {
21 return this._value;
11 return this._value;
22 }
12 }
13
14 toString() {
15 return `@type=${typeof this._value}`;
23 }
16 }
17 }
@@ -4,8 +4,6 import { Constructor, Factory } from "..
4
4
5 export interface Descriptor {
5 export interface Descriptor {
6 activate(context: ActivationContext, name?: string);
6 activate(context: ActivationContext, name?: string);
7 isInstanceCreated(): boolean;
8 getInstance();
9 }
7 }
10
8
11 export function isDescriptor(x): x is Descriptor {
9 export function isDescriptor(x): x is Descriptor {
@@ -14,7 +12,7 export function isDescriptor(x): x is De
14 }
12 }
15
13
16 export interface ServiceMap {
14 export interface ServiceMap {
17 [s: string]: any;
15 [s: string]: Descriptor;
18 }
16 }
19
17
20 export enum ActivationType {
18 export enum ActivationType {
@@ -1,5 +1,5
1 let _nextOid = 0;
1 let _nextOid = 0;
2 const _oid = Symbol("__oid");
2 const _oid = typeof Symbol === "function" ? Symbol("__oid") : "__oid";
3
3
4 export function oid(instance: object): string {
4 export function oid(instance: object): string {
5 if (isNull(instance))
5 if (isNull(instance))
@@ -232,7 +232,7 export function delegate<T, K extends ke
232 export function pmap(items, cb) {
232 export function pmap(items, cb) {
233 argumentNotNull(cb, "cb");
233 argumentNotNull(cb, "cb");
234
234
235 if (items && items.then instanceof Function)
235 if (isPromise(items))
236 return items.then(data => pmap(data, cb));
236 return items.then(data => pmap(data, cb));
237
237
238 if (isNull(items) || !items.length)
238 if (isNull(items) || !items.length)
@@ -1,14 +1,26
1 import { test } 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";
6 import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
7 import { Foo } from "./mock/Foo";
8 import { Bar } from "./mock/Bar";
9 import { isNull } from "@implab/core/safe";
5
10
6 test("Container register/getService tests", async t => {
11 test("Container register/resolve tests", async t => {
12 const writer = new TapeWriter(t);
13
14 TraceSource.on(ts => {
15 ts.level = DebugLevel;
16 writer.writeEvents(ts.events);
17 });
18
7 const container = new Container();
19 const container = new Container();
8
20
9 const connection1 = "db://localhost";
21 const connection1 = "db://localhost";
10
22
11 container.register("connection", connection1);
23 container.register("connection", new ValueDescriptor(connection1));
12
24
13 t.equals(container.getService("connection"), connection1);
25 t.equals(container.getService("connection"), connection1);
14
26
@@ -21,6 +33,42 test("Container register/getService test
21 );
33 );
22
34
23 const dbParams = container.getService("dbParams");
35 const dbParams = container.getService("dbParams");
24 t.equals(dbParams.connection, connection1);
36 t.equals(dbParams.connection, connection1, "should get connection");
37
38 writer.destroy();
39 });
40
41 test("Container configure/resolve tests", async t => {
42 const writer = new TapeWriter(t);
43
44 TraceSource.on(ts => {
45 ts.level = DebugLevel;
46 writer.writeEvents(ts.events);
47 });
48
49 const container = new Container();
50
51 await container.configure({
52 foo: {
53 $type: Foo
54 },
25
55
56 bar: {
57 $type: Bar,
58 params: {
59 db: {
60 provider: {
61 $dependency: "db"
62 }
63 }
64 }
65 }
26 });
66 });
67
68 const f1 = container.resolve("foo");
69 t.assert(!isNull(f1), "foo should be not null");
70
71 const b1 = container.resolve("bar");
72
73 writer.destroy();
74 });
@@ -66,8 +66,12 export function test(name: string, cb: (
66 try {
66 try {
67 await cb(t);
67 await cb(t);
68 } catch (e) {
68 } catch (e) {
69
70 // verbose error information
71 // tslint:disable-next-line
69 console.error(e);
72 console.error(e);
70 t.fail(e);
73 t.fail(e);
74
71 } finally {
75 } finally {
72 t.end();
76 t.end();
73 }
77 }
@@ -6,7 +6,9
6 "outDir" : "build/dist",
6 "outDir" : "build/dist",
7 "declaration": true,
7 "declaration": true,
8 "lib": [
8 "lib": [
9 "es2015"
9 "es5",
10 "es2015.promise",
11 "es2015.symbol"
10 ]
12 ]
11 },
13 },
12 "include" : [
14 "include" : [
General Comments 0
You need to be logged in to leave comments. Login now