##// END OF EJS Templates
Code cleanup,...
cin -
r22:93dca6f27f52 propose observables
parent child
Show More
@@ -0,0 +1,55
1 # Observable
2
3 Универсальный способ организации потока сообщений. Данный механизм может
4 использоваться для оповещения об изменениях состояний объектов или для доставки
5 самостоятельных событий, например, связанных с действиями пользователя.
6
7 Является реализацией классического наблюдателя с возможность сообщить о коце
8 потока событий. Данная реализация не содержит никаких дополнительных функций,
9 таких как фильтрация, канал с состоянием, преобразования сообщений и т.п. Это
10 сделано специально, чтобы реализация оставалась максимально простой.
11
12 Пример того, как можно создать последовательность из 10 событий:
13
14 ```ts
15
16 var events = new Observable(async (notify, error, complete) => {
17 // цикл в котором возникает событие
18 for(let i = 0; i < 10; i++) {
19 await delay(1000);
20 // в качестве данных передается номер события
21 notify(i);
22 }
23 // по окончании последовательности информируем, что событий больше не будет
24 compelte();
25 });
26
27 // создаем окно с отображением хода событий
28 var progress = showProgress({ min: 0, max: 9, current: 0});
29
30 // подписываемся на события
31 events.on(
32 // обработчик очередного события
33 msg => {
34 progress.setValue(msg);
35 }.
36 // обработчик ошибки
37 e => {
38 progress.showError(e);
39 },
40 // обработчик конца потока
41 () => {
42 progress.close();
43 }
44 );
45
46 ```
47
48 ```ts
49
50 // превращаем события dom в Observable
51 var events = new Observable((notify) => {
52 on(domNode,'mousemove', notify);
53 });
54
55 ``` No newline at end of file
@@ -1,50 +1,52
1 define(["./TraceSource"], function (TraceSource) {
1 define(["./TraceSource"], function (TraceSource_1) {
2 'use strict';
2 'use strict';
3
3
4 var TraceSource = TraceSource_1.TraceSource;
5
4 return {
6 return {
5
7
6 on: function (filter, cb) {
8 on: function (filter, cb) {
7 if (arguments.length == 1) {
9 if (arguments.length == 1) {
8 cb = filter;
10 cb = filter;
9 filter = undefined;
11 filter = undefined;
10 }
12 }
11 var test;
13 var test;
12 if (filter instanceof RegExp) {
14 if (filter instanceof RegExp) {
13 test = function (chId) {
15 test = function (chId) {
14 return filter.test(chId);
16 return filter.test(chId);
15 };
17 };
16 } else if (filter instanceof Function) {
18 } else if (filter instanceof Function) {
17 test = filter;
19 test = filter;
18 } else if (filter) {
20 } else if (filter) {
19 test = function (chId) {
21 test = function (chId) {
20 return chId == filter;
22 return chId == filter;
21 };
23 };
22 }
24 }
23
25
24 if (test) {
26 if (test) {
25 TraceSource.on(function (source) {
27 TraceSource.on(function (source) {
26 if (test(source.id))
28 if (test(source.id))
27 source.on(cb);
29 source.on(cb);
28 });
30 });
29 } else {
31 } else {
30 TraceSource.on(function (source) {
32 TraceSource.on(function (source) {
31 source.on(cb);
33 source.on(cb);
32 });
34 });
33 }
35 }
34 },
36 },
35
37
36 load: function (id, require, cb) {
38 load: function (id, require, cb) {
37 if (id) {
39 if (id) {
38 cb(TraceSource.get(id));
40 cb(TraceSource.get(id));
39 } else if (require.module && require.module.mid) {
41 } else if (require.module && require.module.mid) {
40 cb(TraceSource.get(require.module.mid));
42 cb(TraceSource.get(require.module.mid));
41 } else {
43 } else {
42 require(['module'], function (module) {
44 require(['module'], function (module) {
43 cb(TraceSource.get(module && module.id));
45 cb(TraceSource.get(module && module.id));
44 });
46 });
45 }
47 }
46 },
48 },
47
49
48 dynamic: true,
50 dynamic: true,
49 };
51 };
50 }); No newline at end of file
52 });
@@ -1,87 +1,83
1 import { IActivationController, IActivatable, ICancellation } from '../interfaces';
1 import { IActivationController, IActivatable, ICancellation } from '../interfaces';
2 import { AsyncComponent } from './AsyncComponent';
2 import { AsyncComponent } from './AsyncComponent';
3 import { Cancellation } from '../Cancellation';
3 import { Cancellation } from '../Cancellation';
4 import * as TraceSource from '../log/TraceSource';
4 import { TraceSource } from '../log/TraceSource';
5
5
6 type Constructor<T = {}> = new (...args: any[]) => T;
6 type Constructor<T = {}> = new (...args: any[]) => T;
7
7
8 const log = TraceSource.get('@implab/core/components/ActivatableMixin');
8 const log = TraceSource.get('@implab/core/components/ActivatableMixin');
9
9
10 function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
11 return class extends Base implements IActivatable {
11 return class extends Base implements IActivatable {
12 _controller: IActivationController;
12 _controller: IActivationController;
13
13
14 _active: boolean;
14 _active: boolean;
15
15
16 isActive() {
16 isActive() {
17 return this._active;
17 return this._active;
18 }
18 }
19
19
20 getActivationController() {
20 getActivationController() {
21 return this._controller;
21 return this._controller;
22 }
22 }
23
23
24 setActivationController(controller: IActivationController) {
24 setActivationController(controller: IActivationController) {
25 this._controller = controller;
25 this._controller = controller;
26 }
26 }
27
27
28 async onActivating(ct: ICancellation) {
28 async onActivating(ct: ICancellation) {
29 if (this._controller)
29 if (this._controller)
30 await this._controller.activating(this, ct);
30 await this._controller.activating(this, ct);
31 }
31 }
32
32
33 async onActivated(ct: ICancellation) {
33 async onActivated(ct: ICancellation) {
34 if (this._controller)
34 if (this._controller)
35 await this._controller.activated(this, ct);
35 await this._controller.activated(this, ct);
36 }
36 }
37
37
38 activate(ct: ICancellation = Cancellation.none) {
38 activate(ct: ICancellation = Cancellation.none) {
39 return this.runOperation(this._activateAsync.bind(this), ct);
39 return this.runOperation(this._activateAsync.bind(this), ct);
40 }
40 }
41
41
42 async _activateAsync(ct: ICancellation) {
42 async _activateAsync(ct: ICancellation) {
43 if (this.isActive())
43 if (this.isActive())
44 return;
44 return;
45
45
46 await this.onActivating(ct);
46 await this.onActivating(ct);
47 this._active = true;
47 this._active = true;
48 try {
48 try {
49 await this.onActivated(ct);
49 await this.onActivated(ct);
50 } catch (e) {
50 } catch (e) {
51 log.error("Suppressed onActivated error: {0}", e);
51 log.error("Suppressed onActivated error: {0}", e);
52 }
52 }
53 }
53 }
54
54
55 async onDeactivating(ct: ICancellation) {
55 async onDeactivating(ct: ICancellation) {
56 if (this._controller)
56 if (this._controller)
57 await this._controller.deactivating(this, ct);
57 await this._controller.deactivating(this, ct);
58 }
58 }
59
59
60 async onDeactivated(ct: ICancellation) {
60 async onDeactivated(ct: ICancellation) {
61 if (this._controller)
61 if (this._controller)
62 await this._controller.deactivated(this, ct);
62 await this._controller.deactivated(this, ct);
63 }
63 }
64
64
65 deactivate(ct: ICancellation = Cancellation.none) {
65 deactivate(ct: ICancellation = Cancellation.none) {
66 return this.runOperation(this._deactivateAsync.bind(this), ct);
66 return this.runOperation(this._deactivateAsync.bind(this), ct);
67 }
67 }
68
68
69 async _deactivateAsync(ct: ICancellation) {
69 async _deactivateAsync(ct: ICancellation) {
70 if (!this.isActive())
70 if (!this.isActive())
71 return;
71 return;
72 await this.onDeactivating(ct);
72 await this.onDeactivating(ct);
73 this._active = false;
73 this._active = false;
74 try {
74 try {
75 await this.onDeactivated(ct);
75 await this.onDeactivated(ct);
76 } catch (e) {
76 } catch (e) {
77 log.error("Suppressed onDeactivated error: {0}", e);
77 log.error("Suppressed onDeactivated error: {0}", e);
78 }
78 }
79 }
79 }
80 }
80 }
81 }
81 }
82
82
83 namespace ActivatableMixin {
83 export const traceSource = log; No newline at end of file
84 export const traceSource = log;
85 }
86
87 export = ActivatableMixin; No newline at end of file
@@ -1,17 +1,40
1 import { Cancellation } from "../Cancellation";
1 import { Cancellation } from "../Cancellation";
2 import { IAsyncComponent, ICancellation } from "../interfaces";
2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
3 import { destroy } from "../safe";
3
4
4 export class AsyncComponent implements IAsyncComponent {
5 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 _cancel: (e) => void;
7
5 _completion: Promise<void> = Promise.resolve();
8 _completion: Promise<void> = Promise.resolve();
6
9
7 getCompletion() { return this._completion };
10 getCompletion() { return this._completion };
8
11
9 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
12 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
13 // create inner cancellation bound to the passed cancellation token
14 let h: IDestroyable;
15 let inner = new Cancellation(cancel => {
16
17 this._cancel = cancel;
18 h = ct.register(cancel);
19 });
20
10 // TODO create cancellation source here
21 // TODO create cancellation source here
11 async function guard() {
22 let guard = async () => {
12 await op(ct);
23 try {
24 await op(inner);
25 } finally {
26 // after the operation is complete we need to cleanup the
27 // resources
28 destroy(h);
29 this._cancel = null;
30 }
13 }
31 }
14
32
15 return this._completion = guard();
33 return this._completion = guard();
16 }
34 }
35
36 cancel(reason) {
37 if (this._cancel)
38 this._cancel(reason);
39 }
17 } No newline at end of file
40 }
@@ -1,198 +1,193
1 import { IObservable, IDestroyable, ICancellation } from '../interfaces';
1 import { IObservable, IDestroyable, ICancellation } from '../interfaces';
2 import { Cancellation } from '../Cancellation'
2 import { Cancellation } from '../Cancellation'
3 import { argumentNotNull } from '../safe';
3 import { argumentNotNull } from '../safe';
4
4
5
5
6 interface Handler<T> {
6 interface Handler<T> {
7 (x: T): void
7 (x: T): void
8 }
8 }
9
9
10 interface Initializer<T> {
10 interface Initializer<T> {
11 (notify: Handler<T>, error?: (e: any) => void, complete?: () => void): void;
11 (notify: Handler<T>, error?: (e: any) => void, complete?: () => void): void;
12 }
12 }
13
13
14 // TODO: think about to move this interfaces.ts and make it public
14 // TODO: think about to move this interfaces.ts and make it public
15 interface IObserver<T> {
15 interface IObserver<T> {
16 next(event: T): void
16 next(event: T): void
17
17
18 error(e: any): void
18 error(e: any): void
19
19
20 complete(): void
20 complete(): void
21 }
21 }
22
22
23 const noop = () => {};
23 const noop = () => {};
24
24
25 class Observable<T> implements IObservable<T> {
25 export class Observable<T> implements IObservable<T> {
26 private _once = new Array<IObserver<T>>();
26 private _once = new Array<IObserver<T>>();
27
27
28 private _observers = new Array<IObserver<T>>();
28 private _observers = new Array<IObserver<T>>();
29
29
30
30
31 private _complete: boolean
31 private _complete: boolean
32
32
33 private _error: any
33 private _error: any
34
34
35 constructor(func?: Initializer<T>) {
35 constructor(func?: Initializer<T>) {
36 if (func)
36 if (func)
37 func(
37 func(
38 this._notifyNext.bind(this),
38 this._notifyNext.bind(this),
39 this._notifyError.bind(this),
39 this._notifyError.bind(this),
40 this._notifyCompleted.bind(this)
40 this._notifyCompleted.bind(this)
41 );
41 );
42 }
42 }
43
43
44 /**
44 /**
45 * Registers handlers for the current observable object.
45 * Registers handlers for the current observable object.
46 *
46 *
47 * @param next the handler for events
47 * @param next the handler for events
48 * @param error the handler for a error
48 * @param error the handler for a error
49 * @param complete the handler for a completion
49 * @param complete the handler for a completion
50 * @returns {IDestroyable} the handler for the current subscription, this
50 * @returns {IDestroyable} the handler for the current subscription, this
51 * handler can be used to unsubscribe from events.
51 * handler can be used to unsubscribe from events.
52 *
52 *
53 */
53 */
54 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
54 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
55 argumentNotNull(next, "next");
55 argumentNotNull(next, "next");
56
56
57 let me = this;
57 let me = this;
58
58
59 let observer: IObserver<T> & IDestroyable = {
59 let observer: IObserver<T> & IDestroyable = {
60 next: next,
60 next: next,
61 error: error ? error.bind(null) : noop,
61 error: error ? error.bind(null) : noop,
62 complete: complete ? complete.bind(null) : noop,
62 complete: complete ? complete.bind(null) : noop,
63
63
64 destroy() {
64 destroy() {
65 me._removeObserver(this);
65 me._removeObserver(this);
66 }
66 }
67 }
67 }
68
68
69 this._addObserver(observer);
69 this._addObserver(observer);
70
70
71
71
72 return observer;
72 return observer;
73 }
73 }
74
74
75 private _addObserver(observer: IObserver<T>) {
75 private _addObserver(observer: IObserver<T>) {
76 if (this._complete) {
76 if (this._complete) {
77 try {
77 try {
78 if (this._error)
78 if (this._error)
79 observer.error(this._error);
79 observer.error(this._error);
80 else
80 else
81 observer.complete();
81 observer.complete();
82 } catch (e) {
82 } catch (e) {
83 this.onObserverException(e);
83 this.onObserverException(e);
84 }
84 }
85 } else {
85 } else {
86 this._observers.push(observer);
86 this._observers.push(observer);
87 }
87 }
88 }
88 }
89
89
90 /**
90 /**
91 * Waits for the next event. This method can't be used to read messages
91 * Waits for the next event. This method can't be used to read messages
92 * as a sequence since it can skip some messages between calls.
92 * as a sequence since it can skip some messages between calls.
93 *
93 *
94 * @param ct a cancellation token
94 * @param ct a cancellation token
95 */
95 */
96 next(ct: ICancellation = Cancellation.none): Promise<T> {
96 next(ct: ICancellation = Cancellation.none): Promise<T> {
97 return new Promise<T>((resolve, reject) => {
97 return new Promise<T>((resolve, reject) => {
98 let observer: IObserver<T> = {
98 let observer: IObserver<T> = {
99 next: resolve,
99 next: resolve,
100 error: reject,
100 error: reject,
101 complete: () => reject("No more events are available")
101 complete: () => reject("No more events are available")
102 };
102 };
103
103
104 if (this._addOnce(observer) && ct.isSupported()) {
104 if (this._addOnce(observer) && ct.isSupported()) {
105 ct.register((e) => {
105 ct.register((e) => {
106 this._removeOnce(observer);
106 this._removeOnce(observer);
107 reject(e);
107 reject(e);
108 });
108 });
109 }
109 }
110 });
110 });
111 }
111 }
112
112
113 private _addOnce(observer: IObserver<T>) {
113 private _addOnce(observer: IObserver<T>) {
114 if (this._complete) {
114 if (this._complete) {
115 try {
115 try {
116 if (this._error)
116 if (this._error)
117 observer.error(this._error);
117 observer.error(this._error);
118 else
118 else
119 observer.complete();
119 observer.complete();
120 } catch (e) {
120 } catch (e) {
121 this.onObserverException(e);
121 this.onObserverException(e);
122 }
122 }
123 return false;
123 return false;
124 }
124 }
125
125
126 this._once.push(observer);
126 this._once.push(observer);
127 return true;
127 return true;
128 }
128 }
129
129
130 protected onObserverException(e: any) {
130 protected onObserverException(e: any) {
131 }
131 }
132
132
133 private _removeOnce(d: IObserver<T>) {
133 private _removeOnce(d: IObserver<T>) {
134 let i = this._once.indexOf(d);
134 let i = this._once.indexOf(d);
135 if (i >= 0)
135 if (i >= 0)
136 this._once.splice(i, 1);
136 this._once.splice(i, 1);
137 }
137 }
138
138
139 private _removeObserver(d: IObserver<T>) {
139 private _removeObserver(d: IObserver<T>) {
140 let i = this._observers.indexOf(d);
140 let i = this._observers.indexOf(d);
141 if (i >= 0)
141 if (i >= 0)
142 this._observers.splice(i, 1);
142 this._observers.splice(i, 1);
143 }
143 }
144
144
145 private _notify(guard: (observer: IObserver<T>) => void) {
145 private _notify(guard: (observer: IObserver<T>) => void) {
146 if (this._once.length) {
146 if (this._once.length) {
147 for (let i = 0; i < this._once.length; i++)
147 for (let i = 0; i < this._once.length; i++)
148 guard(this._once[i]);
148 guard(this._once[i]);
149 this._once = [];
149 this._once = [];
150 }
150 }
151
151
152 for (let i = 0; i < this._observers.length; i++)
152 for (let i = 0; i < this._observers.length; i++)
153 guard(this._observers[i]);
153 guard(this._observers[i]);
154 }
154 }
155
155
156 protected _notifyNext(evt: T) {
156 protected _notifyNext(evt: T) {
157 let guard = (observer: IObserver<T>) => {
157 let guard = (observer: IObserver<T>) => {
158 try {
158 try {
159 observer.next(evt);
159 observer.next(evt);
160 } catch (e) {
160 } catch (e) {
161 this.onObserverException(e);
161 this.onObserverException(e);
162 }
162 }
163 }
163 }
164
164
165 this._notify(guard);
165 this._notify(guard);
166 }
166 }
167
167
168 protected _notifyError(e: any) {
168 protected _notifyError(e: any) {
169 let guard = (observer: IObserver<T>) => {
169 let guard = (observer: IObserver<T>) => {
170 try {
170 try {
171 observer.error(e);
171 observer.error(e);
172 } catch (e) {
172 } catch (e) {
173 this.onObserverException(e);
173 this.onObserverException(e);
174 }
174 }
175 }
175 }
176
176
177 this._notify(guard);
177 this._notify(guard);
178 this._observers = [];
178 this._observers = [];
179 }
179 }
180
180
181 protected _notifyCompleted() {
181 protected _notifyCompleted() {
182 let guard = (observer: IObserver<T>) => {
182 let guard = (observer: IObserver<T>) => {
183 try {
183 try {
184 observer.complete();
184 observer.complete();
185 } catch (e) {
185 } catch (e) {
186 this.onObserverException(e);
186 this.onObserverException(e);
187 }
187 }
188 }
188 }
189
189
190 this._notify(guard);
190 this._notify(guard);
191 this._observers = [];
191 this._observers = [];
192 }
192 }
193 }
193 } No newline at end of file
194
195 namespace Observable {
196 }
197
198 export = Observable; No newline at end of file
@@ -1,171 +1,188
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 * as Observable from '../components/Observable'
3 import { Observable } from '../components/Observable'
4 import { IDestroyable } from '../interfaces';
4 import { IDestroyable } from '../interfaces';
5 import * as TraceEvent from './TraceEvent'
5
6 export const DebugLevel = 400;
7
8 export const LogLevel = 300;
9
10 export const WarnLevel = 200;
11
12 export const ErrorLevel = 100;
13
14 export const SilentLevel = 0;
15
16 export class TraceEvent {
17 readonly source: TraceSource;
18
19 readonly level: Number;
20
21 readonly arg: any;
22
23 constructor(source: TraceSource, level: Number, arg: any) {
24 this.source = source;
25 this.level = level;
26 this.arg = arg;
27 }
28 }
6
29
7 class Registry {
30 class Registry {
8 static readonly instance = new Registry();
31 static readonly instance = new Registry();
9
32
10 private _registry: object = new Object();
33 private _registry: object = new Object();
11 private _listeners: object = new Object();
34 private _listeners: object = new Object();
12 private _nextCookie: number = 1;
35 private _nextCookie: number = 1;
13
36
14 get(id: any): TraceSource {
37 get(id: any): TraceSource {
15 argumentNotNull(id, "id");
38 argumentNotNull(id, "id");
16
39
17 if (this._registry[id])
40 if (this._registry[id])
18 return this._registry[id];
41 return this._registry[id];
19
42
20 var source = new TraceSource(id);
43 var source = new TraceSource(id);
21 this._registry[id] = source;
44 this._registry[id] = source;
22 this._onNewSource(source);
45 this._onNewSource(source);
23
46
24 return source;
47 return source;
25 }
48 }
26
49
27 add(id: any, source: TraceSource) {
50 add(id: any, source: TraceSource) {
28 argumentNotNull(id, "id");
51 argumentNotNull(id, "id");
29 argumentNotNull(source, "source");
52 argumentNotNull(source, "source");
30
53
31 this._registry[id] = source;
54 this._registry[id] = source;
32 this._onNewSource(source);
55 this._onNewSource(source);
33 }
56 }
34
57
35 _onNewSource(source: TraceSource) {
58 _onNewSource(source: TraceSource) {
36 for (let i in this._listeners)
59 for (let i in this._listeners)
37 this._listeners[i].call(null, source);
60 this._listeners[i].call(null, source);
38 }
61 }
39
62
40 on(handler: (source: TraceSource) => void): IDestroyable {
63 on(handler: (source: TraceSource) => void): IDestroyable {
41 argumentNotNull(handler, "handler");
64 argumentNotNull(handler, "handler");
42 var me = this;
65 var me = this;
43
66
44 var cookie = this._nextCookie++;
67 var cookie = this._nextCookie++;
45
68
46 this._listeners[cookie] = handler;
69 this._listeners[cookie] = handler;
47
70
48 for (let i in this._registry)
71 for (let i in this._registry)
49 handler(this._registry[i]);
72 handler(this._registry[i]);
50
73
51 return {
74 return {
52 destroy() {
75 destroy() {
53 delete me._listeners[cookie];
76 delete me._listeners[cookie];
54 }
77 }
55 };
78 };
56 }
79 }
57 }
80 }
58
81
59 class TraceSource extends Observable<TraceEvent> {
82 export class TraceSource {
60 readonly id: any
83 readonly id: any
61
84
62 level: number
85 level: number
63
86
87 readonly events: Observable<TraceEvent>
88
89 _notifyNext: (arg: TraceEvent) => void
90
64 constructor(id: any) {
91 constructor(id: any) {
65 super();
92
66 this.id = id || new Object();
93 this.id = id || new Object();
94 this.events = new Observable((next) => {
95 this._notifyNext = next;
96 })
67 }
97 }
68
98
69 protected emit(level: number, arg: any) {
99 protected emit(level: number, arg: any) {
70 this._notifyNext(new TraceEvent(this, level, arg));
100 this._notifyNext(new TraceEvent(this, level, arg));
71 }
101 }
72
102
73 isDebugEnabled() {
103 isDebugEnabled() {
74 return this.level >= TraceSource.DebugLevel;
104 return this.level >= DebugLevel;
75 }
105 }
76
106
77 debug(msg: string, ...args: any[]) {
107 debug(msg: string, ...args: any[]) {
78 if (this.isEnabled(TraceSource.DebugLevel))
108 if (this.isEnabled(DebugLevel))
79 this.emit(TraceSource.DebugLevel, format(msg, args));
109 this.emit(DebugLevel, format(msg, args));
80 }
110 }
81
111
82 isLogEnabled() {
112 isLogEnabled() {
83 return this.level >= TraceSource.LogLevel;
113 return this.level >= LogLevel;
84 }
114 }
85
115
86 log(msg: string, ...args: any[]) {
116 log(msg: string, ...args: any[]) {
87 if (this.isEnabled(TraceSource.LogLevel))
117 if (this.isEnabled(LogLevel))
88 this.emit(TraceSource.LogLevel, format(msg, args));
118 this.emit(LogLevel, format(msg, args));
89 }
119 }
90
120
91 isWarnEnabled() {
121 isWarnEnabled() {
92 return this.level >= TraceSource.WarnLevel;
122 return this.level >= WarnLevel;
93 }
123 }
94
124
95 warn(msg: string, ...args: any[]) {
125 warn(msg: string, ...args: any[]) {
96 if (this.isEnabled(TraceSource.WarnLevel))
126 if (this.isEnabled(WarnLevel))
97 this.emit(TraceSource.WarnLevel, format(msg, args));
127 this.emit(WarnLevel, format(msg, args));
98 }
128 }
99
129
100 /**
130 /**
101 * returns true if errors will be recorded.
131 * returns true if errors will be recorded.
102 */
132 */
103 isErrorEnabled() {
133 isErrorEnabled() {
104 return this.level >= TraceSource.ErrorLevel;
134 return this.level >= ErrorLevel;
105 }
135 }
106
136
107 /**
137 /**
108 * Traces a error.
138 * Traces a error.
109 *
139 *
110 * @param msg the message.
140 * @param msg the message.
111 * @param args parameters which will be substituted in the message.
141 * @param args parameters which will be substituted in the message.
112 */
142 */
113 error(msg: string, ...args: any[]) {
143 error(msg: string, ...args: any[]) {
114 if (this.isEnabled(TraceSource.ErrorLevel))
144 if (this.isEnabled(ErrorLevel))
115 this.emit(TraceSource.ErrorLevel, format(msg, args));
145 this.emit(ErrorLevel, format(msg, args));
116 }
146 }
117
147
118 /**
148 /**
119 * Checks whether the specified level is enabled for this
149 * Checks whether the specified level is enabled for this
120 * trace source.
150 * trace source.
121 *
151 *
122 * @param level the trace level which should be checked.
152 * @param level the trace level which should be checked.
123 */
153 */
124 isEnabled(level: number) {
154 isEnabled(level: number) {
125 return (this.level >= level);
155 return (this.level >= level);
126 }
156 }
127
157
128 /**
158 /**
129 * 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
130 *
160 *
131 * @param level the level of the event
161 * @param level the level of the event
132 * @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.
133 */
163 */
134 traceEvent(level: number, arg: any) {
164 traceEvent(level: number, arg: any) {
135 if (this.isEnabled(level))
165 if (this.isEnabled(level))
136 this.emit(level, arg);
166 this.emit(level, arg);
137 }
167 }
138
168
139 /**
169 /**
140 * 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
141 * created trace source.
171 * created trace source.
142 *
172 *
143 * @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
144 */
174 */
145 static on(handler: (source: TraceSource) => void) {
175 static on(handler: (source: TraceSource) => void) {
146 return Registry.instance.on(handler);
176 return Registry.instance.on(handler);
147 }
177 }
148
178
149 /**
179 /**
150 * Creates or returns already created trace source for the specified id.
180 * Creates or returns already created trace source for the specified id.
151 *
181 *
152 * @param id the id for the trace source
182 * @param id the id for the trace source
153 */
183 */
154 static get(id: any) {
184 static get(id: any) {
155 return Registry.instance.get(id);
185 return Registry.instance.get(id);
156 }
186 }
157 }
187 }
158
188
159 namespace TraceSource {
160 export const DebugLevel = 400;
161
162 export const LogLevel = 300;
163
164 export const WarnLevel = 200;
165
166 export const ErrorLevel = 100;
167
168 export const SilentLevel = 0;
169 }
170
171 export = TraceSource; No newline at end of file
@@ -1,35 +1,29
1 import { IObservable, IDestroyable, ICancellation } from "../../interfaces";
1 import { IObservable, IDestroyable, ICancellation } from "../../interfaces";
2 import * as TraceEvent from '../TraceEvent';
3 import { Cancellation } from "../../Cancellation";
2 import { Cancellation } from "../../Cancellation";
4 import * as TraceSource from "../TraceSource";
3 import { TraceEvent, LogLevel, WarnLevel } from "../TraceSource";
5
4
6 class ConsoleWriter implements IDestroyable {
5 export class ConsoleWriter implements IDestroyable {
7 readonly _subscriptions = new Array<IDestroyable>();
6 readonly _subscriptions = new Array<IDestroyable>();
8
7
9 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
8 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
10 var subscription = source.on(this.writeEvent.bind(this));
9 var subscription = source.on(this.writeEvent.bind(this));
11 if (ct.isSupported()) {
10 if (ct.isSupported()) {
12 ct.register(subscription.destroy.bind(subscription));
11 ct.register(subscription.destroy.bind(subscription));
13 }
12 }
14 this._subscriptions.push(subscription);
13 this._subscriptions.push(subscription);
15 }
14 }
16
15
17 writeEvent(next: TraceEvent) {
16 writeEvent(next: TraceEvent) {
18 if (next.level >= TraceSource.LogLevel) {
17 if (next.level >= LogLevel) {
19 console.log(next.source.id.toString(), next.arg);
18 console.log(next.source.id.toString(), next.arg);
20 } else if(next.level >= TraceSource.WarnLevel) {
19 } else if(next.level >= WarnLevel) {
21 console.warn(next.source.id.toString(), next.arg);
20 console.warn(next.source.id.toString(), next.arg);
22 } else {
21 } else {
23 console.error(next.source.id.toString(), next.arg);
22 console.error(next.source.id.toString(), next.arg);
24 }
23 }
25 }
24 }
26
25
27 destroy() {
26 destroy() {
28 this._subscriptions.forEach(x => x.destroy());
27 this._subscriptions.forEach(x => x.destroy());
29 }
28 }
30 }
29 } No newline at end of file
31
32 namespace ConsoleWriter {
33 }
34
35 export = ConsoleWriter; No newline at end of file
@@ -1,231 +1,236
1 export function argumentNotNull(arg, name) {
1 export function argumentNotNull(arg, name) {
2 if (arg === null || arg === undefined)
2 if (arg === null || arg === undefined)
3 throw new Error("The argument " + name + " can't be null or undefined");
3 throw new Error("The argument " + name + " can't be null or undefined");
4 }
4 }
5
5
6 export function argumentNotEmptyString(arg, name) {
6 export function argumentNotEmptyString(arg, name) {
7 if (typeof (arg) !== "string" || !arg.length)
7 if (typeof (arg) !== "string" || !arg.length)
8 throw new Error("The argument '" + name + "' must be a not empty string");
8 throw new Error("The argument '" + name + "' must be a not empty string");
9 }
9 }
10
10
11 export function argumentNotEmptyArray(arg, name) {
11 export function argumentNotEmptyArray(arg, name) {
12 if (!(arg instanceof Array) || !arg.length)
12 if (!(arg instanceof Array) || !arg.length)
13 throw new Error("The argument '" + name + "' must be a not empty array");
13 throw new Error("The argument '" + name + "' must be a not empty array");
14 }
14 }
15
15
16 export function argumentOfType(arg, type, name) {
16 export function argumentOfType(arg, type, name) {
17 if (!(arg instanceof type))
17 if (!(arg instanceof type))
18 throw new Error("The argument '" + name + "' type doesn't match");
18 throw new Error("The argument '" + name + "' type doesn't match");
19 }
19 }
20
20
21 export function isNull(arg) {
21 export function isNull(arg) {
22 return (arg === null || arg === undefined);
22 return (arg === null || arg === undefined);
23 }
23 }
24
24
25 export function isPrimitive(arg) {
25 export function isPrimitive(arg) {
26 return (arg === null || arg === undefined || typeof (arg) === "string" ||
26 return (arg === null || arg === undefined || typeof (arg) === "string" ||
27 typeof (arg) === "number" || typeof (arg) === "boolean");
27 typeof (arg) === "number" || typeof (arg) === "boolean");
28 }
28 }
29
29
30 export function isInteger(arg) {
30 export function isInteger(arg) {
31 return parseInt(arg) == arg;
31 return parseInt(arg) == arg;
32 }
32 }
33
33
34 export function isNumber(arg) {
34 export function isNumber(arg) {
35 return parseFloat(arg) == arg;
35 return parseFloat(arg) == arg;
36 }
36 }
37
37
38 export function isString(val) {
38 export function isString(val) {
39 return typeof (val) == "string" || val instanceof String;
39 return typeof (val) == "string" || val instanceof String;
40 }
40 }
41
41
42 export function isNullOrEmptyString(str) {
42 export function isNullOrEmptyString(str) {
43 if (str === null || str === undefined ||
43 if (str === null || str === undefined ||
44 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
44 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
45 return true;
45 return true;
46 }
46 }
47
47
48 export function isNotEmptyArray(arg) {
48 export function isNotEmptyArray(arg) {
49 return (arg instanceof Array && arg.length > 0);
49 return (arg instanceof Array && arg.length > 0);
50 }
50 }
51
51
52 /**
52 /**
53 * Выполняет метод для каждого элемента массива, останавливается, когда
53 * Выполняет метод для каждого элемента массива, останавливается, когда
54 * либо достигнут конец массива, либо функция <c>cb</c> вернула
54 * либо достигнут конец массива, либо функция <c>cb</c> вернула
55 * значение.
55 * значение.
56 *
56 *
57 * @param {Array | Object} obj массив элементов для просмотра
57 * @param {Array | Object} obj массив элементов для просмотра
58 * @param {Function} cb функция, вызываемая для каждого элемента
58 * @param {Function} cb функция, вызываемая для каждого элемента
59 * @param {Object} thisArg значение, которое будет передано в качестве
59 * @param {Object} thisArg значение, которое будет передано в качестве
60 * <c>this</c> в <c>cb</c>.
60 * <c>this</c> в <c>cb</c>.
61 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
61 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
62 * если достигнут конец массива.
62 * если достигнут конец массива.
63 */
63 */
64 export function each(obj, cb, thisArg) {
64 export function each(obj, cb, thisArg) {
65 argumentNotNull(cb, "cb");
65 argumentNotNull(cb, "cb");
66 var i, x;
66 var i, x;
67 if (obj instanceof Array) {
67 if (obj instanceof Array) {
68 for (i = 0; i < obj.length; i++) {
68 for (i = 0; i < obj.length; i++) {
69 x = cb.call(thisArg, obj[i], i);
69 x = cb.call(thisArg, obj[i], i);
70 if (x !== undefined)
70 if (x !== undefined)
71 return x;
71 return x;
72 }
72 }
73 } else {
73 } else {
74 var keys = Object.keys(obj);
74 var keys = Object.keys(obj);
75 for (i = 0; i < keys.length; i++) {
75 for (i = 0; i < keys.length; i++) {
76 var k = keys[i];
76 var k = keys[i];
77 x = cb.call(thisArg, obj[k], k);
77 x = cb.call(thisArg, obj[k], k);
78 if (x !== undefined)
78 if (x !== undefined)
79 return x;
79 return x;
80 }
80 }
81 }
81 }
82 }
82 }
83
83
84 /** Wraps the specified function to emulate an asynchronous execution.
84 /** Wraps the specified function to emulate an asynchronous execution.
85 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
85 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
86 * @param{Function|String} fn [Required] Function wich will be wrapped.
86 * @param{Function|String} fn [Required] Function wich will be wrapped.
87 */
87 */
88 export function async(_fn: (...args: any[]) => any, thisArg) : (...args: any[]) => PromiseLike<any> {
88 export function async(_fn: (...args: any[]) => any, thisArg) : (...args: any[]) => PromiseLike<any> {
89 let fn = _fn;
89 let fn = _fn;
90
90
91 if (arguments.length == 2 && !(fn instanceof Function))
91 if (arguments.length == 2 && !(fn instanceof Function))
92 fn = thisArg[fn];
92 fn = thisArg[fn];
93
93
94 if (fn == null)
94 if (fn == null)
95 throw new Error("The function must be specified");
95 throw new Error("The function must be specified");
96
96
97 function wrapresult(x, e?) : PromiseLike<any> {
97 function wrapresult(x, e?) : PromiseLike<any> {
98 if (e) {
98 if (e) {
99 return {
99 return {
100 then: function (cb, eb) {
100 then: function (cb, eb) {
101 try {
101 try {
102 return eb ? wrapresult(eb(e)) : this;
102 return eb ? wrapresult(eb(e)) : this;
103 } catch (e2) {
103 } catch (e2) {
104 return wrapresult(null, e2);
104 return wrapresult(null, e2);
105 }
105 }
106 }
106 }
107 };
107 };
108 } else {
108 } else {
109 if (x && x.then)
109 if (x && x.then)
110 return x;
110 return x;
111 return {
111 return {
112 then: function (cb) {
112 then: function (cb) {
113 try {
113 try {
114 return cb ? wrapresult(cb(x)) : this;
114 return cb ? wrapresult(cb(x)) : this;
115 } catch (e2) {
115 } catch (e2) {
116 return wrapresult(e2);
116 return wrapresult(e2);
117 }
117 }
118 }
118 }
119 };
119 };
120 }
120 }
121 }
121 }
122
122
123 return function () {
123 return function () {
124 try {
124 try {
125 return wrapresult(fn.apply(thisArg, arguments));
125 return wrapresult(fn.apply(thisArg, arguments));
126 } catch (e) {
126 } catch (e) {
127 return wrapresult(null, e);
127 return wrapresult(null, e);
128 }
128 }
129 };
129 };
130 }
130 }
131
131
132 export function delegate(target, _method: (string | Function)) {
132 export function delegate(target, _method: (string | Function)) {
133 let method : Function;
133 let method : Function;
134
134
135 if (!(_method instanceof Function)) {
135 if (!(_method instanceof Function)) {
136 argumentNotNull(target, "target");
136 argumentNotNull(target, "target");
137 method = target[_method];
137 method = target[_method];
138 } else {
138 } else {
139 method = _method;
139 method = _method;
140 }
140 }
141
141
142 if (!(method instanceof Function))
142 if (!(method instanceof Function))
143 throw new Error("'method' argument must be a Function or a method name");
143 throw new Error("'method' argument must be a Function or a method name");
144
144
145 return function () {
145 return function () {
146 return method.apply(target, arguments);
146 return method.apply(target, arguments);
147 };
147 };
148 }
148 }
149
149
150 /**
150 /**
151 * Для каждого элемента массива вызывает указанную функцию и сохраняет
151 * Для каждого элемента массива вызывает указанную функцию и сохраняет
152 * возвращенное значение в массиве результатов.
152 * возвращенное значение в массиве результатов.
153 *
153 *
154 * @remarks cb может выполняться асинхронно, при этом одновременно будет
154 * @remarks cb может выполняться асинхронно, при этом одновременно будет
155 * только одна операция.
155 * только одна операция.
156 *
156 *
157 * @async
157 * @async
158 */
158 */
159 export function pmap(items, cb) {
159 export function pmap(items, cb) {
160 argumentNotNull(cb, "cb");
160 argumentNotNull(cb, "cb");
161
161
162 if (items && items.then instanceof Function)
162 if (items && items.then instanceof Function)
163 return items.then(function (data) {
163 return items.then(function (data) {
164 return pmap(data, cb);
164 return pmap(data, cb);
165 });
165 });
166
166
167 if (isNull(items) || !items.length)
167 if (isNull(items) || !items.length)
168 return items;
168 return items;
169
169
170 var i = 0,
170 var i = 0,
171 result = [];
171 result = [];
172
172
173 function next() {
173 function next() {
174 var r, ri;
174 var r, ri;
175
175
176 function chain(x) {
176 function chain(x) {
177 result[ri] = x;
177 result[ri] = x;
178 return next();
178 return next();
179 }
179 }
180
180
181 while (i < items.length) {
181 while (i < items.length) {
182 r = cb(items[i], i);
182 r = cb(items[i], i);
183 ri = i;
183 ri = i;
184 i++;
184 i++;
185 if (r && r.then) {
185 if (r && r.then) {
186 return r.then(chain);
186 return r.then(chain);
187 } else {
187 } else {
188 result[ri] = r;
188 result[ri] = r;
189 }
189 }
190 }
190 }
191 return result;
191 return result;
192 }
192 }
193
193
194 return next();
194 return next();
195 }
195 }
196
196
197 /**
197 /**
198 * Выбирает первый элемент из последовательности, или обещания, если в
198 * Выбирает первый элемент из последовательности, или обещания, если в
199 * качестве параметра используется обещание, оно должно вернуть массив.
199 * качестве параметра используется обещание, оно должно вернуть массив.
200 *
200 *
201 * @param {Function} cb обработчик результата, ему будет передан первый
201 * @param {Function} cb обработчик результата, ему будет передан первый
202 * элемент последовательности в случае успеха
202 * элемент последовательности в случае успеха
203 * @param {Function} err обработчик исключения, если массив пустой, либо
203 * @param {Function} err обработчик исключения, если массив пустой, либо
204 * не массив
204 * не массив
205 *
205 *
206 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
206 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
207 * обещание, либо первый элемент.
207 * обещание, либо первый элемент.
208 * @async
208 * @async
209 */
209 */
210 export function first(sequence: any, cb: Function, err: Function) {
210 export function first(sequence: any, cb: Function, err: Function) {
211 if (sequence) {
211 if (sequence) {
212 if (sequence.then instanceof Function) {
212 if (sequence.then instanceof Function) {
213 return sequence.then(function (res) {
213 return sequence.then(function (res) {
214 return first(res, cb, err);
214 return first(res, cb, err);
215 }, err);
215 }, err);
216 } else if (sequence && "length" in sequence) {
216 } else if (sequence && "length" in sequence) {
217 if (sequence.length === 0) {
217 if (sequence.length === 0) {
218 if (err)
218 if (err)
219 return err(new Error("The sequence is empty"));
219 return err(new Error("The sequence is empty"));
220 else
220 else
221 throw new Error("The sequence is empty");
221 throw new Error("The sequence is empty");
222 }
222 }
223 return cb ? cb(sequence[0]) : sequence[0];
223 return cb ? cb(sequence[0]) : sequence[0];
224 }
224 }
225 }
225 }
226
226
227 if (err)
227 if (err)
228 return err(new Error("The sequence is required"));
228 return err(new Error("The sequence is required"));
229 else
229 else
230 throw new Error("The sequence is required");
230 throw new Error("The sequence is required");
231 }
232
233 export function destroy(d: any) {
234 if (d && 'destroy' in d)
235 d.destroy();
231 } No newline at end of file
236 }
@@ -1,29 +1,30
1 define(["tape"], function(tape) {
1 define(["tape"], function(tape) {
2 "use strict";
2 "use strict";
3 var sourceId = '73a633f3-eab8-49b0-8601-07cae710f234';
3 var sourceId = '73a633f3-eab8-49b0-8601-07cae710f234';
4 var sourceId2 = '3ba9c7cd-ed77-437b-9a2f-1cbeb1226b5b';
4 var sourceId2 = '3ba9c7cd-ed77-437b-9a2f-1cbeb1226b5b';
5 tape('Load TraceSource for the module', function(t) {
5 tape('Load TraceSource for the module', function(t) {
6 require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource) {
6 require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource_1) {
7 var TraceSource = TraceSource_1.TraceSource;
7 t.equal(trace && trace.id, sourceId, "trace should be taken from the loader plugin parameter");
8 t.equal(trace && trace.id, sourceId, "trace should be taken from the loader plugin parameter");
8
9
9 var count = 0;
10 var count = 0;
10
11
11 var h = TraceSource.on(function(x) {
12 var h = TraceSource.on(function(x) {
12 if(x.id == sourceId || x.id == sourceId2)
13 if(x.id == sourceId || x.id == sourceId2)
13 count++;
14 count++;
14 });
15 });
15
16
16 t.equal(count, 1, "should see created channel immediatelly");
17 t.equal(count, 1, "should see created channel immediatelly");
17 t.equal(trace, TraceSource.get(sourceId), "should get same TraceSource from registry");
18 t.equal(trace, TraceSource.get(sourceId), "should get same TraceSource from registry");
18 t.equal(count, 1);
19 t.equal(count, 1);
19
20
20 TraceSource.get(sourceId2);
21 TraceSource.get(sourceId2);
21
22
22 t.equal(count, 2);
23 t.equal(count, 2);
23
24
24 h.destroy();
25 h.destroy();
25
26
26 t.end();
27 t.end();
27 });
28 });
28 });
29 });
29 }); No newline at end of file
30 });
@@ -1,108 +1,108
1 import * as tape from 'tape';
1 import * as tape from 'tape';
2 import * as ActivatableMixin from '@implab/core/components/ActivatableMixin';
2 import { ActivatableMixin} from '@implab/core/components/ActivatableMixin';
3 import { AsyncComponent } from '@implab/core/components/AsyncComponent';
3 import { AsyncComponent } from '@implab/core/components/AsyncComponent';
4 import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces';
4 import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces';
5 import { Cancellation } from '@implab/core/Cancellation';
5 import { Cancellation } from '@implab/core/Cancellation';
6
6
7 class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
7 class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
8
8
9 }
9 }
10
10
11 class MockActivationController implements IActivationController {
11 class MockActivationController implements IActivationController {
12
12
13 _active: IActivatable = null;
13 _active: IActivatable = null;
14
14
15
15
16 getActive() : IActivatable {
16 getActive() : IActivatable {
17 return this._active;
17 return this._active;
18 }
18 }
19
19
20 async deactivate() {
20 async deactivate() {
21 if (this._active)
21 if (this._active)
22 await this._active.deactivate();
22 await this._active.deactivate();
23 this._active = null;
23 this._active = null;
24 }
24 }
25
25
26 async activate(component: IActivatable) {
26 async activate(component: IActivatable) {
27 if (!component || component.isActive())
27 if (!component || component.isActive())
28 return;
28 return;
29 component.setActivationController(this);
29 component.setActivationController(this);
30
30
31 await component.activate();
31 await component.activate();
32 }
32 }
33
33
34 async activating(component: IActivatable, ct: ICancellation = Cancellation.none) {
34 async activating(component: IActivatable, ct: ICancellation = Cancellation.none) {
35 if (component != this._active)
35 if (component != this._active)
36 await this.deactivate();
36 await this.deactivate();
37 }
37 }
38
38
39 async activated(component: IActivatable, ct: ICancellation = Cancellation.none) {
39 async activated(component: IActivatable, ct: ICancellation = Cancellation.none) {
40 this._active = component;
40 this._active = component;
41 }
41 }
42
42
43 async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) {
43 async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) {
44
44
45 }
45 }
46
46
47 async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) {
47 async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) {
48 if (this._active == component)
48 if (this._active == component)
49 this._active = null;
49 this._active = null;
50 }
50 }
51 }
51 }
52
52
53 tape('simple activation',async function(t){
53 tape('simple activation',async function(t){
54
54
55 let a = new SimpleActivatable();
55 let a = new SimpleActivatable();
56 t.false(a.isActive());
56 t.false(a.isActive());
57
57
58 await a.activate();
58 await a.activate();
59 t.true(a.isActive());
59 t.true(a.isActive());
60
60
61 await a.deactivate();
61 await a.deactivate();
62 t.false(a.isActive());
62 t.false(a.isActive());
63
63
64 t.end();
64 t.end();
65 });
65 });
66
66
67 tape('controller activation', async function(t) {
67 tape('controller activation', async function(t) {
68
68
69 let a = new SimpleActivatable();
69 let a = new SimpleActivatable();
70 let c = new MockActivationController();
70 let c = new MockActivationController();
71
71
72 t.false(a.isActive(), "the component is not active by default");
72 t.false(a.isActive(), "the component is not active by default");
73 t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default");
73 t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default");
74 t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default");
74 t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default");
75
75
76 t.comment("Active the component through the controller");
76 t.comment("Active the component through the controller");
77 await c.activate(a);
77 await c.activate(a);
78 t.true(a.isActive(), "The component should successfully activate");
78 t.true(a.isActive(), "The component should successfully activate");
79 t.equal(c.getActive(), a, "The controller should point to the activated component");
79 t.equal(c.getActive(), a, "The controller should point to the activated component");
80 t.equal(a.getActivationController(), c, "The component should point to the controller");
80 t.equal(a.getActivationController(), c, "The component should point to the controller");
81
81
82 t.comment("Deactive the component throug the controller");
82 t.comment("Deactive the component throug the controller");
83 await c.deactivate();
83 await c.deactivate();
84
84
85 t.false(a.isActive(), "The component should successfully deactivate");
85 t.false(a.isActive(), "The component should successfully deactivate");
86 t.equal(c.getActive(), null, "The controller shouldn't point to any component");
86 t.equal(c.getActive(), null, "The controller shouldn't point to any component");
87 t.equal(a.getActivationController(), c, "The componet should point to it's controller");
87 t.equal(a.getActivationController(), c, "The componet should point to it's controller");
88
88
89 t.end();
89 t.end();
90 });
90 });
91
91
92 tape('handle error in onActivating', async function(t) {
92 tape('handle error in onActivating', async function(t) {
93 let a = new SimpleActivatable();
93 let a = new SimpleActivatable();
94
94
95 a.onActivating = async function() {
95 a.onActivating = async function() {
96 throw "Should fail";
96 throw "Should fail";
97 };
97 };
98
98
99 try {
99 try {
100 await a.activate();
100 await a.activate();
101 t.fail("activation should fail");
101 t.fail("activation should fail");
102 } catch {
102 } catch {
103 }
103 }
104
104
105 t.false(a.isActive(), "the component should remain inactive");
105 t.false(a.isActive(), "the component should remain inactive");
106
106
107 t.end();
107 t.end();
108 }); No newline at end of file
108 });
@@ -1,63 +1,62
1 import { IObservable, ICancellation, IDestroyable } from "../../build/dist/interfaces";
1 import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
2 import * as TraceEvent from '../../build/dist/log/TraceEvent';
2 import { Cancellation } from "@implab/core/Cancellation";
3 import { Cancellation } from "../../build/dist/Cancellation";
3 import { TraceEvent, LogLevel, WarnLevel } from "@implab/core/log/TraceSource";
4 import * as TraceSource from "../../build/dist/log/TraceSource";
5 import * as tape from 'tape';
4 import * as tape from 'tape';
6 import { argumentNotNull } from "../../build/dist/safe";
5 import { argumentNotNull } from "@implab/core/safe";
7
6
8 export class TapeWriter implements IDestroyable {
7 export class TapeWriter implements IDestroyable {
9 readonly _tape: tape.Test
8 readonly _tape: tape.Test
10
9
11 _subscriptions = new Array<IDestroyable>();
10 _subscriptions = new Array<IDestroyable>();
12
11
13 constructor(tape: tape.Test) {
12 constructor(tape: tape.Test) {
14 argumentNotNull(tape, "tape");
13 argumentNotNull(tape, "tape");
15 this._tape = tape;
14 this._tape = tape;
16 }
15 }
17
16
18 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
17 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
19 let subscription = source.on(this.writeEvent.bind(this));
18 let subscription = source.on(this.writeEvent.bind(this));
20 if (ct.isSupported()) {
19 if (ct.isSupported()) {
21 ct.register(subscription.destroy.bind(subscription));
20 ct.register(subscription.destroy.bind(subscription));
22 }
21 }
23 this._subscriptions.push(subscription);
22 this._subscriptions.push(subscription);
24 }
23 }
25
24
26 writeEvent(next: TraceEvent) {
25 writeEvent(next: TraceEvent) {
27 if (next.level >= TraceSource.LogLevel) {
26 if (next.level >= LogLevel) {
28 this._tape.comment("LOG " + next.arg);
27 this._tape.comment("LOG " + next.arg);
29 } else if (next.level >= TraceSource.WarnLevel) {
28 } else if (next.level >= WarnLevel) {
30 this._tape.comment("WARN " + next.arg);
29 this._tape.comment("WARN " + next.arg);
31 } else {
30 } else {
32 this._tape.comment("ERROR " + next.arg);
31 this._tape.comment("ERROR " + next.arg);
33 }
32 }
34 }
33 }
35
34
36 destroy() {
35 destroy() {
37 this._subscriptions.forEach(x => x.destroy());
36 this._subscriptions.forEach(x => x.destroy());
38 }
37 }
39 }
38 }
40
39
41 export async function delay(timeout: number, ct: ICancellation = Cancellation.none) {
40 export async function delay(timeout: number, ct: ICancellation = Cancellation.none) {
42 let un: IDestroyable;
41 let un: IDestroyable;
43
42
44 try {
43 try {
45 await new Promise((resolve, reject) => {
44 await new Promise((resolve, reject) => {
46 if (ct.isRequested()) {
45 if (ct.isRequested()) {
47 un = ct.register(reject);
46 un = ct.register(reject);
48 } else {
47 } else {
49 let ht = setTimeout(() => {
48 let ht = setTimeout(() => {
50 resolve();
49 resolve();
51 }, timeout);
50 }, timeout);
52
51
53 un = ct.register(e => {
52 un = ct.register(e => {
54 clearTimeout(ht);
53 clearTimeout(ht);
55 reject(e);
54 reject(e);
56 });
55 });
57 }
56 }
58 });
57 });
59 } finally {
58 } finally {
60 if(un)
59 if(un)
61 un.destroy();
60 un.destroy();
62 };
61 };
63 } No newline at end of file
62 }
@@ -1,69 +1,69
1 import * as TraceSource from '@implab/core/log/TraceSource'
1 import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource'
2 import * as tape from 'tape';
2 import * as tape from 'tape';
3 import { TapeWriter } from './TestTraits';
3 import { TapeWriter } from './TestTraits';
4
4
5 const sourceId = 'test/TraceSourceTests';
5 const sourceId = 'test/TraceSourceTests';
6
6
7 tape('trace message', t => {
7 tape('trace message', t => {
8 let trace = TraceSource.get(sourceId);
8 let trace = TraceSource.get(sourceId);
9
9
10 trace.level = TraceSource.DebugLevel;
10 trace.level = DebugLevel;
11
11
12 let h = trace.on((ev) => {
12 let h = trace.events.on((ev) => {
13 t.equal(ev.source, trace, "sender should be the current trace source");
13 t.equal(ev.source, trace, "sender should be the current trace source");
14 t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level");
14 t.equal(ev.level, DebugLevel, "level should be debug level");
15 t.equal(ev.arg, "Hello, World!", "The message should be a formatted message");
15 t.equal(ev.arg, "Hello, World!", "The message should be a formatted message");
16
16
17 t.end();
17 t.end();
18 });
18 });
19
19
20 trace.debug("Hello, {0}!", "World");
20 trace.debug("Hello, {0}!", "World");
21
21
22 h.destroy();
22 h.destroy();
23 });
23 });
24
24
25 tape('trace event', t => {
25 tape('trace event', t => {
26 let trace = TraceSource.get(sourceId);
26 let trace = TraceSource.get(sourceId);
27
27
28 trace.level = TraceSource.DebugLevel;
28 trace.level = DebugLevel;
29
29
30 let event = {
30 let event = {
31 name: "custom event"
31 name: "custom event"
32 };
32 };
33
33
34 let h = trace.on((ev) => {
34 let h = trace.events.on((ev) => {
35 t.equal(ev.source, trace, "sender should be the current trace source");
35 t.equal(ev.source, trace, "sender should be the current trace source");
36 t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level");
36 t.equal(ev.level, DebugLevel, "level should be debug level");
37 t.equal(ev.arg, event, "The message should be the specified object");
37 t.equal(ev.arg, event, "The message should be the specified object");
38
38
39 t.end();
39 t.end();
40 });
40 });
41
41
42 trace.traceEvent(TraceSource.DebugLevel, event);
42 trace.traceEvent(DebugLevel, event);
43
43
44 h.destroy();
44 h.destroy();
45 });
45 });
46
46
47 tape('tape comment writer', async t => {
47 tape('tape comment writer', async t => {
48 let writer = new TapeWriter(t);
48 let writer = new TapeWriter(t);
49
49
50 TraceSource.on(ts => {
50 TraceSource.on(ts => {
51 writer.writeEvents(ts);
51 writer.writeEvents(ts.events);
52 });
52 });
53
53
54 let trace = TraceSource.get(sourceId);
54 let trace = TraceSource.get(sourceId);
55 trace.level = TraceSource.DebugLevel;
55 trace.level = DebugLevel;
56
56
57 trace.log("Hello, {0}!", 'World');
57 trace.log("Hello, {0}!", 'World');
58 trace.log("Multi\n line");
58 trace.log("Multi\n line");
59 trace.warn("Look at me!");
59 trace.warn("Look at me!");
60 trace.error("DIE!");
60 trace.error("DIE!");
61
61
62 writer.destroy();
62 writer.destroy();
63
63
64 trace.log("You shouldn't see it!");
64 trace.log("You shouldn't see it!");
65
65
66 t.comment("DONE");
66 t.comment("DONE");
67
67
68 t.end();
68 t.end();
69 }); No newline at end of file
69 });
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now