##// END OF EJS Templates
working on fluent configuration, di annotations removed
cin -
r134:511bcc634d65 ioc ts support
parent child
Show More
@@ -71,7 +71,6 function(declare, safe, when, QueryResul
71 71 });
72 72 mapped.total = total;
73 73 var results = new QueryResults(mapped);
74 console.log(results);
75 74 return results;
76 75 });
77 76 },
@@ -2,8 +2,10 import { Cancellation } from "../Cancell
2 2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
3 3 import { destroy } from "../safe";
4 4
5 const noop = () => void (0);
6
5 7 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 _cancel: ((e: any) => void) | undefined;
8 _cancel: ((e: any) => void) = noop;
7 9
8 10 _completion: Promise<void> = Promise.resolve();
9 11
@@ -26,7 +28,7 export class AsyncComponent implements I
26 28 // after the operation is complete we need to cleanup the
27 29 // resources
28 30 destroy(h);
29 this._cancel = undefined;
31 this._cancel = noop;
30 32 }
31 33 };
32 34
@@ -34,7 +36,6 export class AsyncComponent implements I
34 36 }
35 37
36 38 cancel(reason: any) {
37 if (this._cancel)
38 this._cancel(reason);
39 this._cancel(reason);
39 40 }
40 41 }
@@ -1,6 +1,6
1 1 import { TraceSource } from "../log/TraceSource";
2 2 import { argumentNotEmptyString } from "../safe";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService } from "./interfaces";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetime } from "./interfaces";
4 4 import { Container } from "./Container";
5 5 import { MapOf } from "../interfaces";
6 6
@@ -13,6 +13,8 export interface ActivationContextInfo {
13 13
14 14 }
15 15
16 let nextId = 1;
17
16 18 export class ActivationContext<S extends object> {
17 19 _cache: MapOf<any>;
18 20
@@ -73,18 +75,23 export class ActivationContext<S extends
73 75 this._services[name] = service as any;
74 76 }
75 77
76 has(id: string) {
77 return id in this._cache;
78 createLifetime(): ILifetime {
79 const id = nextId++;
80 const me = this;
81 return {
82 initialize() {
83 },
84 has() {
85 return id in me._cache;
86 },
87 get() {
88 return me._cache[id];
89 },
90 store(item: any) {
91 me._cache[id] = item;
92 }
93 };
78 94 }
79
80 get<T>(id: string) {
81 return this._cache[id];
82 }
83
84 store(id: string, value: any) {
85 return (this._cache[id] = value);
86 }
87
88 95 activate<T>(d: Descriptor<S, T>, name: string) {
89 96 if (trace.isLogEnabled())
90 97 trace.log(`enter ${name} ${d}`);
@@ -3,7 +3,7 import {
3 3 ActivationType,
4 4 ContainerKeys,
5 5 TypeOfService,
6 ILifetimeManager
6 ILifetime
7 7 } from "./interfaces";
8 8
9 9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
@@ -281,9 +281,11 export class Configuration<S extends obj
281 281 trace.debug("<{0}", name);
282 282 }
283 283
284 async _visit(data: any, name: string): Promise<any> {
285 if (isPrimitive(data) || isDescriptor(data))
286 return data;
284 _visit(data: any, name: string): Promise<any> {
285 if (isPrimitive(data))
286 return Promise.resolve(new ValueDescriptor(data));
287 if (isDescriptor(data))
288 return Promise.resolve(data);
287 289
288 290 if (isDependencyRegistration<S>(data)) {
289 291 return this._visitDependencyRegistration(data, name);
@@ -398,9 +400,11 export class Configuration<S extends obj
398 400 opts.type = data.$type;
399 401 } else {
400 402 const [moduleName, typeName] = data.$type.split(":", 2);
401 const t = opts.type = this._resolveType(moduleName, typeName);
402 if (!(t instanceof Function))
403 throw Error("$type (" + data.$type + ") is not a constructable");
403 opts.type = this._resolveType(moduleName, typeName).then(t => {
404 if (!(t instanceof Function))
405 throw Error("$type (" + data.$type + ") is not a constructable");
406 return t;
407 });
404 408 }
405 409
406 410 const d = new TypeServiceDescriptor<S, any, any[]>(
@@ -427,20 +431,20 export class Configuration<S extends obj
427 431 return d;
428 432 }
429 433
430 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetimeManager {
434 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
431 435 switch (activation) {
432 436 case "container":
433 return this._container.getLifetimeManager();
437 return LifetimeManager.containerLifetime(this._container);
434 438 case "hierarchy":
435 return LifetimeManager.hierarchyLifetime;
439 return LifetimeManager.hierarchyLifetime();
436 440 case "context":
437 return LifetimeManager.contextLifetime;
441 return LifetimeManager.contextLifetime();
438 442 case "singleton":
439 443 if (typeId === undefined)
440 444 throw Error("The singleton activation requires a typeId");
441 445 return LifetimeManager.singletonLifetime(typeId);
442 446 default:
443 return LifetimeManager.empty;
447 return LifetimeManager.empty();
444 448 }
445 449 }
446 450 }
@@ -38,7 +38,7 export class LazyReferenceDescriptor<S e
38 38
39 39 const saved = context.clone();
40 40
41 return (cfg?: PartialServiceMap<S>) => {
41 return (cfg?: PartialServiceMap<S>): any => {
42 42 // защищаем контекст на случай исключения в процессе
43 43 // активации
44 44 const ct = cfg ? saved.clone() : saved;
@@ -2,6 +2,7 import { IDestroyable, MapOf } from "../
2 2 import { argumentNotNull, isDestroyable } from "../safe";
3 3 import { ILifetimeManager, ILifetime } from "./interfaces";
4 4 import { ActivationContext } from "./ActivationContext";
5 import { Container } from "./Container";
5 6
6 7 function safeCall(item: () => void) {
7 8 try {
@@ -16,7 +17,7 const emptyLifetime: ILifetime = {
16 17 return false;
17 18 },
18 19
19 enter() {
20 initialize() {
20 21
21 22 },
22 23
@@ -30,6 +31,21 const emptyLifetime: ILifetime = {
30 31
31 32 };
32 33
34 const unknownLifetime: ILifetime = {
35 has() {
36 throw new Error("The lifetime is unknown");
37 },
38 initialize() {
39 throw new Error("Can't call initialize on the unknown lifetime object");
40 },
41 get() {
42 throw new Error("The lifetime object isn't initialized");
43 },
44 store() {
45 throw new Error("Can't store a value in the unknown lifetime object");
46 }
47 }
48
33 49 let nextId = 0;
34 50
35 51 export class LifetimeManager implements IDestroyable, ILifetimeManager {
@@ -39,7 +55,7 export class LifetimeManager implements
39 55
40 56 private _pending: MapOf<boolean> = {};
41 57
42 initialize(): ILifetime {
58 create(): ILifetime {
43 59 const self = this;
44 60 const id = ++nextId;
45 61 return {
@@ -54,7 +70,7 export class LifetimeManager implements
54 70 return t;
55 71 },
56 72
57 enter() {
73 initialize() {
58 74 if (self._pending[id])
59 75 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
60 76 self._pending[id] = true;
@@ -89,43 +105,71 export class LifetimeManager implements
89 105 }
90 106 }
91 107
92 static readonly empty: ILifetimeManager = {
93 initialize(): ILifetime {
94 return emptyLifetime;
95 }
96 };
108 static empty(): ILifetime {
109 return emptyLifetime;
110 }
111
112 static hierarchyLifetime(): ILifetime {
113 let _lifetime = unknownLifetime;
114 return {
115 initialize(context: ActivationContext<any>) {
116 if (_lifetime !== unknownLifetime)
117 throw new Error("Cyclic reference activation detected");
97 118
98 static readonly hierarchyLifetime: ILifetimeManager = {
99 initialize(context: ActivationContext<any>): ILifetime {
100 return context.getContainer().getLifetimeManager().initialize(context);
101 }
102 };
119 _lifetime = context.getContainer().getLifetimeManager().create(context);
120 },
121 get() {
122 return _lifetime.get();
123 },
124 has() {
125 return _lifetime.has();
126 },
127 store(item: any, cleanup?: (item: any) => void) {
128 return _lifetime.store(item, cleanup);
129 }
130 };
131 }
103 132
104 static readonly contextLifetime: ILifetimeManager = {
105 initialize(context: ActivationContext<any>): ILifetime {
106 const id = String(++nextId);
107 return {
108 enter() {
109 if (context.visit(id))
110 throw new Error("Cyclic reference detected");
111 },
112 get() {
113 return context.get(id);
114 },
115 has() {
116 return context.has(id);
117 },
118 store(item: any) {
119 context.store(id, item);
120 }
121 };
122 }
123 };
133 static contextLifetime(): ILifetime {
134 let _lifetime = unknownLifetime;
135 return {
136 initialize(context: ActivationContext<any>) {
137 if (_lifetime !== unknownLifetime)
138 throw new Error("Cyclic reference detected");
139 _lifetime = context.createLifetime();
140 },
141 get() {
142 return _lifetime.get();
143 },
144 has() {
145 return _lifetime.has();
146 },
147 store(item: any) {
148 _lifetime.store(item);
149 }
150 };
151 }
124 152
125 static singletonLifetime(typeId: string): ILifetimeManager {
153 static singletonLifetime(typeId: string): ILifetime {
154 return emptyLifetime;
155 }
156
157 static containerLifetime(container: Container<any>) {
158 let _lifetime = unknownLifetime;
126 159 return {
127 initialize() {
128 return emptyLifetime;
160 initialize(context: ActivationContext<any>) {
161 if (_lifetime !== unknownLifetime)
162 throw new Error("Cyclic reference detected");
163 _lifetime = container.getLifetimeManager().create(context);
164 },
165 get() {
166 return _lifetime.get();
167 },
168 has() {
169 return _lifetime.has();
170 },
171 store(item: any) {
172 _lifetime.store(item);
129 173 }
130 174 };
131 175 }
@@ -3,9 +3,26 import { ActivationContext } from "./Act
3 3 import { Descriptor, PartialServiceMap, TypeOfService, ContainerKeys } from "./interfaces";
4 4
5 5 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
6 /**
7 * The name of the descriptor
8 */
6 9 name: K;
10
11 /**
12 * The flag that indicates that the referenced service isn't required to exist.
13 * If the reference is optional and the referenced service doesn't exist,
14 * the undefined or a default value will be returned.
15 */
7 16 optional?: boolean;
17
18 /**
19 * a default value for the reference when the referenced service doesn't exist.
20 */
8 21 default?: TypeOfService<S, K>;
22
23 /**
24 * The service overrides
25 */
9 26 services?: PartialServiceMap<S>;
10 27 }
11 28
@@ -29,7 +46,10 export class ReferenceDescriptor<S exten
29 46 this._services = (opts.services || {}) as PartialServiceMap<S>;
30 47 }
31 48
32 activate(context: ActivationContext<S>) {
49 /** This method activates the referenced service if one exists
50 * @param context activation context which is used during current activation
51 */
52 activate(context: ActivationContext<S>): any {
33 53 // добавляем сервисы
34 54 if (this._services) {
35 55 each(this._services, (v, k) => context.register(k, v));
@@ -59,7 +59,7 export type InjectionSpec<T> = {
59 59 };
60 60
61 61 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
62 lifetime?: ILifetimeManager;
62 lifetime?: ILifetime;
63 63
64 64 params?: P;
65 65
@@ -79,14 +79,14 export class ServiceDescriptor<S extends
79 79
80 80 _cleanup: ((item: T) => void) | undefined;
81 81
82 _lifetimeManager = LifetimeManager.empty;
82 _lifetime = LifetimeManager.empty();
83 83
84 84 _objectLifetime: ILifetime | undefined;
85 85
86 86 constructor(opts: ServiceDescriptorParams<S, T, P>) {
87 87
88 88 if (opts.lifetime)
89 this._lifetimeManager = opts.lifetime;
89 this._lifetime = opts.lifetime;
90 90
91 91 if (!isNull(opts.params))
92 92 this._params = opts.params;
@@ -105,15 +105,12 export class ServiceDescriptor<S extends
105 105 }
106 106
107 107 activate(context: ActivationContext<S>) {
108 if (!this._objectLifetime)
109 this._objectLifetime = this._lifetimeManager.initialize(context);
110
111 const lifetime = this._objectLifetime;
108 const lifetime = this._lifetime;
112 109
113 110 if (lifetime.has()) {
114 111 return lifetime.get();
115 112 } else {
116 lifetime.enter();
113 lifetime.initialize(context);
117 114 const instance = this._create(context);
118 115 lifetime.store(instance, this._cleanup);
119 116 return instance;
@@ -1,5 +1,4
1 import { Resolver, ServiceModule, LazyDependencyOptions, DependencyOptions } from "./interfaces";
2 import { AnnotationBuilder } from "../Annotations";
1 import { Resolver, LazyDependencyOptions, DependencyOptions } from "./interfaces";
3 2 import { Container } from "../Container";
4 3 import { Descriptor, ILifetime, ContainerKeys } from "../interfaces";
5 4 import { ActivationContext } from "../ActivationContext";
@@ -12,9 +11,6 export class DescriptorBuilder<T, S exte
12 11 this._container = container;
13 12 this._cb = cb;
14 13 }
15 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>) {
16
17 }
18 14
19 15 factory(f: (resolve: Resolver<S>, activate: (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => any) => T): void {
20 16 this._cb({
@@ -34,7 +30,7 export class DescriptorBuilder<T, S exte
34 30 if (lifetime.has()) {
35 31 return lifetime.get();
36 32 } else {
37 lifetime.enter();
33 lifetime.initialize(context);
38 34 const instance = factory();
39 35 lifetime.store(instance, cleanup);
40 36 return instance;
@@ -26,13 +26,13 export type ServiceModule<T, S extends o
26 26 [m in M]: AnnotationBuilder<T, S>;
27 27 };
28 28
29 export type InferReferenceType<S extends object, K extends keyof ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
29 export type InferReferenceType<S extends object, K extends ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
30 30 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
31 31 TypeOfService<S, K>;
32 32
33 33 export interface Resolver<S extends object> {
34 <K extends keyof ContainerKeys<S>, O extends LazyDependencyOptions>(this: void, name: K, opts: O): () => InferReferenceType<S, K, O>;
35 <K extends keyof ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
34 <K extends ContainerKeys<S>, O extends LazyDependencyOptions>(this: void, name: K, opts: O): () => InferReferenceType<S, K, O>;
35 <K extends ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
36 36 }
37 37
38 38 export interface DescriptorBuilder<T, S extends object> {
@@ -38,7 +38,7 export type ContainerRegistered<S extend
38 38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
39 39
40 40 export interface ILifetimeManager {
41 initialize(context: ActivationContext<any>): ILifetime;
41 create(context: ActivationContext<any>): ILifetime;
42 42 }
43 43
44 44 /**
@@ -51,7 +51,7 export interface ILifetime {
51 51
52 52 get(): any;
53 53
54 enter(): void;
54 initialize(context: ActivationContext<any>): void;
55 55
56 56 store(item: any, cleanup?: (item: any) => void): void;
57 57 }
@@ -1,6 +1,5
1 1 import { isPrimitive } from "../safe";
2 2 import { Descriptor } from "./interfaces";
3 import { AnnotationBuilder } from "./Annotations";
4 3 import { Configuration } from "./fluent/Configuration";
5 4
6 5 export function isDescriptor(x: any): x is Descriptor {
@@ -8,16 +7,6 export function isDescriptor(x: any): x
8 7 (x.activate instanceof Function);
9 8 }
10 9
11 export function declare<S extends object>() {
12 return {
13 annotate<T>() {
14 return new AnnotationBuilder<T, S>();
15 },
16 configure(): Configuration<S> {
17 throw new Error();
18 },
19 dependency() {
20 throw new Error();
21 }
22 };
10 export function configure<S extends object>() {
11 return new Configuration<S>();
23 12 }
@@ -129,8 +129,7 export function get(member: string, cont
129 129 * @param {Function} cb функция, вызываемая для каждого элемента
130 130 * @param {Object} thisArg значение, которое будет передано в качестве
131 131 * <c>this</c> в <c>cb</c>.
132 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
133 * если достигнут конец массива.
132 * @returns {void}
134 133 */
135 134 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
136 135 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
@@ -138,18 +137,14 export function each(obj: any, cb: any,
138 137 export function each(obj: any, cb: any, thisArg?: any) {
139 138 argumentNotNull(cb, "cb");
140 139 if (obj instanceof Array) {
140 let v: any;
141 141 for (let i = 0; i < obj.length; i++) {
142 const x = cb.call(thisArg, obj[i], i);
143 if (x !== undefined)
144 return x;
142 v = obj[i];
143 if (v !== undefined)
144 cb.call(thisArg, v, i);
145 145 }
146 146 } else {
147 const _keys = Object.keys(obj);
148 for (const k of _keys) {
149 const x = cb.call(thisArg, obj[k], k);
150 if (x !== undefined)
151 return x;
152 }
147 Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k));
153 148 }
154 149 }
155 150
@@ -478,7 +473,7 export function firstWhere<T>(
478 473 }
479 474
480 475 export function isDestroyable(d: any): d is IDestroyable {
481 if (d && "destroy" in d && typeof(destroy) === "function")
476 if (d && "destroy" in d && typeof (destroy) === "function")
482 477 return true;
483 478 return false;
484 479 }
@@ -1,4 +1,4
1 import { isPrimitive, isNull, each, isKeyof, get } from "../safe";
1 import { isPrimitive, isNull, isKeyof, get } from "../safe";
2 2 import { MapOf } from "../interfaces";
3 3
4 4 type SubstFn = (name: string, format?: string) => string;
@@ -1,7 +1,6
1 1 import { Foo } from "./Foo";
2 import { annotate, dependency } from "./services";
3 2
4 export const service = annotate<Bar>();
3 /* export const service = annotate<Bar>();
5 4
6 5 @service.wire({
7 6 foo: dependency("foo"),
@@ -9,22 +8,27 export const service = annotate<Bar>();
9 8 lazy: dependency("foo", { lazy: true })
10 9 },
11 10 host: dependency("host")
12 }, "")
11 }, "") */
13 12 export class Bar {
14 13 barName = "Twister";
15 14
16 15 _v: Foo | undefined;
17 16
18 constructor(_opts: {
19 foo?: Foo;
20 nested?: {
21 lazy: () => Foo
17 constructor(
18 _opts: {
19 foo?: Foo;
20 nested?: {
21 lazy: () => Foo
22 },
23 host: string
22 24 },
23 host: string
24 }, s: string) {
25 s: string
26 ) {
25 27
26 28 if (_opts && _opts.foo)
27 29 this._v = _opts.foo;
30 if (s)
31 this.barName = s;
28 32 }
29 33
30 34 setName(name: string) {
@@ -1,12 +1,11
1 1 import { Bar } from "./Bar";
2 import { annotate, dependency } from "./services";
3 2
4 3 // export service descriptor
5 4 // через service передается информация о типе зависимости
6 5 // даже если это шаблон.
7 export const service = annotate<Box<Bar>>();
6 // export const service = annotate<Box<Bar>>();
8 7
9 @service.wire()
8 // @service.wire()
10 9 export class Box<T> {
11 10 private _value: T | undefined;
12 11
@@ -14,7 +13,7 export class Box<T> {
14 13 this._value = value;
15 14 }
16 15
17 @service.inject(dependency("bar"))
16 // @service.inject(dependency("bar"))
18 17 setValue(value: T) {
19 18 this._value = value;
20 19 return value;
@@ -1,10 +1,13
1 import { configure } from "./services";
1 import { Services } from "./services";
2 import { configure } from "../di/traits";
3 import { LifetimeManager } from "../di/LifetimeManager";
2 4
3 export const config = configure()
5 export const config = configure<Services>()
4 6 .register("host", s => s.value("example.com"))
5 7 .register("bar2", bar2 => Promise.all([import("./Foo"), import("./Bar")])
6 8 .then(([{ Foo }, { Bar }]) => {
7 const lifetime: any = undefined; // new HierarchyLifetime()
9 const lifetime = LifetimeManager.hierarchyLifetime();
10
8 11 bar2.factory((resolve, activate) => {
9 12 const bar = new Bar({
10 13 foo: activate(lifetime, () => new Foo()),
@@ -1,7 +1,6
1 1 import { Foo } from "./Foo";
2 2 import { Bar } from "./Bar";
3 3 import { Box } from "./Box";
4 import { declare } from "../di/traits";
5 4
6 5 /**
7 6 * Сервисы доступные внутри контейнера
@@ -18,8 +17,3 export interface Services {
18 17 host: string;
19 18
20 19 }
21
22 /**
23 * Экспортируем вспомогательные функции для описания сервисов и кинфогурации
24 */
25 export const { dependency, annotate, configure } = declare<Services>();
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now