##// END OF EJS Templates
Merged in propose observables (pull request #2)...
m407 -
r29:e0b5fc764f84 merge default
parent child
Show More
@@ -0,0 +1,135
1 # Observable
2
3 Универсальный способ организации потока сообщений. Данный механизм может
4 использоваться для оповещения об изменениях состояний объектов или для доставки
5 самостоятельных событий, например, связанных с действиями пользователя.
6
7 Является реализацией классического шаблона наблюдателя с возможность сообщить
8 о коце потока событий. Данная реализация не содержит никаких дополнительных
9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
11
12 Пример того, как можно создать последовательность из 10 событий:
13
14 ```ts
15 var events = new Observable(async (notify, error, complete) => {
16 // цикл в котором возникает событие
17 for(let i = 0; i < 10; i++) {
18 await delay(1000);
19 // в качестве данных передается номер события
20 notify(i);
21 }
22 // по окончании последовательности информируем, что событий больше не будет
23 compelte();
24 });
25
26 // создаем окно с отображением хода событий
27 var progress = showProgress({ min: 0, max: 9, current: 0});
28
29 // подписываемся на события
30 events.on(
31 // обработчик очередного события
32 msg => {
33 progress.setValue(msg);
34 }.
35 // обработчик ошибки
36 e => {
37 progress.showError(e);
38 },
39 // обработчик конца потока
40 () => {
41 progress.close();
42 }
43 );
44
45 // ожидание следующего события
46 let firstEvent = await events.next();
47 ```
48
49 `Observable` можно создавать из событий другого объекта, например, виджета:
50
51 ```ts
52 // клсс
53 class Canvas {
54 readonly mouseMove: IObservable<[number,number]>
55
56 postCreate() {
57 // превращаем события виджета в Observable
58 this.mouseMove = new Observable<[number,number]>((notify) => {
59 this.mousePad.on('mousemove',(e) => notify([e.clientX, e.clientY]) );
60 });
61 }
62 }
63
64 ```
65
66 Если объект инкапсулирует в себе `Observable`, он также может сохранить методы
67 для оповещения подписчиков для дальнейшего их использования внутри класса.
68
69 ```ts
70 // класс, который будет генерировать события местоположения
71 class PositionTracker implements IDestroyable {
72 // _nextPosition и _complete будут связаны с position при создании
73 // экземпляра PositionTracker.
74 _nextPosition: (pos: Position) => void
75 _complete: () => void
76
77 readonly position: IObservable<Position>
78
79 // конструктор
80 constructor(...args: any[]) {
81 super(args);
82
83 // создаем Observable
84 this.position = new Observable<Position>((notify, error, complete) => {
85 // сохраняем методы для оповещения о новом местоположении
86 this._nextPosition = notify;
87 // метод об оповещении конца потока событий
88 this._complete = complete
89 });
90 }
91
92 // метод для очистки ресурсов
93 destroy() {
94 this._complete();
95
96 super();
97 }
98 }
99
100 ```
101
102 ## Observable и последовательности
103
104 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
105 парадигме событийного (или реактивного) программировния. Следует также понимать,
106 что при переходе от синхронного процедурного программирования к событийному так
107 же меняется и направление управления (Inverse Of Control), что означает
108 следующее:
109
110 * при работе с итераторами клиенты сами определяют момент чтения следующего
111 элемента последовательности.
112 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
113 их поступления и не могут на это повлиять.
114
115 Последний пункт можно изменить применив, например, буффер или канал с
116 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
117 наблюдателя.
118
119 ```ts
120 while(1) {
121 // ожидаем следующее событие, по сути это подписка только на одно событие
122 let next = await events.next();
123
124 // такой цикл может пропускать сообщения, поскольку асинхронная операция
125 // позволит возобновить создание новых событий, на которые мы не подписаны
126 await processEvent(next);
127
128 // не только асинхронные операции могут привести к пропуску события
129 // например вызов метода, который приводит к созданию события так же
130 // приведет к тому, что созданное событие не будет обработано в текущем
131 // цикле
132 doSmthAndRiseEvent();
133 }
134
135 ``` No newline at end of file
@@ -0,0 +1,186
1 # Observable
2
3 Универсальный способ организации потока сообщений. Данный механизм может
4 использоваться для оповещения об изменениях состояний объектов или для доставки
5 самостоятельных событий, например, связанных с действиями пользователя.
6
7 Является реализацией классического шаблона наблюдателя с возможность сообщить
8 о коце потока событий. Данная реализация не содержит никаких дополнительных
9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
11
12 Пример того, как можно создать последовательность из 10 событий:
13
14 ```ts
15 var events = new Observable(async (notify, error, complete) => {
16 // цикл в котором возникает событие
17 for(let i = 0; i < 10; i++) {
18 await delay(1000);
19 // в качестве данных передается номер события
20 notify(i);
21 }
22 // по окончании последовательности информируем, что событий больше не будет
23 compelte();
24 });
25
26 // создаем окно с отображением хода событий
27 var progress = showProgress({ min: 0, max: 9, current: 0});
28
29 // подписываемся на события
30 events.on(
31 // обработчик очередного события
32 msg => {
33 progress.setValue(msg);
34 }.
35 // обработчик ошибки
36 e => {
37 progress.showError(e);
38 },
39 // обработчик конца потока
40 () => {
41 progress.close();
42 }
43 );
44
45 // ожидание следующего события
46 let firstEvent = await events.next();
47 ```
48
49 `Observable` можно создавать из событий другого объекта, например, виджета:
50
51 ```ts
52 // клсс
53 class Canvas {
54 readonly mouseMove: IObservable<[number,number]>
55
56 postCreate() {
57 // превращаем события виджета в Observable
58 this.mouseMove = new Observable<[number,number]>((notify) => {
59 this.mousePad.on('mousemove',(e) => notify([e.clientX, e.clientY]) );
60 });
61 }
62 }
63
64 ```
65
66 Если объект инкапсулирует в себе `Observable`, он также может сохранить методы
67 для оповещения подписчиков для дальнейшего их использования внутри класса.
68
69 ```ts
70 // класс, который будет генерировать события местоположения
71 class PositionTracker implements IDestroyable {
72 // _nextPosition и _complete будут связаны с position при создании
73 // экземпляра PositionTracker.
74 _nextPosition: (pos: Position) => void
75 _complete: () => void
76
77 readonly position: IObservable<Position>
78
79 // конструктор
80 constructor(...args: any[]) {
81 super(args);
82
83 // создаем Observable
84 this.position = new Observable<Position>((notify, error, complete) => {
85 // сохраняем методы для оповещения о новом местоположении
86 this._nextPosition = notify;
87 // метод об оповещении конца потока событий
88 this._complete = complete
89 });
90 }
91
92 // метод для очистки ресурсов
93 destroy() {
94 this._complete();
95
96 super();
97 }
98 }
99 ```
100
101 Существует также несколько варинатов получения сообщений
102
103 ```ts
104 // регистрация метода для получений событий
105 let subscription = pushEvents.on((msg) => {
106 displayPopup(msg);
107 });
108
109 // подписку можно отменить, после чего обработчики больше не будут вызываться
110 subcription.destroy();
111
112 // если требуется получить только одно сообщение можно использовать
113 // асинхронный метод next(ct?: ICancellation)
114
115 let msg = await pushEvents.next();
116
117 // пример метода для получения координат с карты, который использует
118 // событие нажатия мышью для определения координат.
119
120 class Map {
121 /**
122
123 Получает координаты по щелчку мыши.
124
125 @async
126
127 @returns [lon,lat]
128
129 */
130 async peekCoordinates(ct: ICancellation = Cancellation.none) {
131 // получаем событие клика
132 let evt = this.viewport.click.next(ct);
133
134 // преобразуем позицию на экране в координаты карты
135 return this.clientToCoodinates([evt.clientx,evt.clientY]);
136 }
137 }
138
139
140 let map : Map; // где-то объявлено
141
142 // пример получения координат с карты
143 let coords = await map.peekCoordinates();
144
145 ```
146
147 ## Observable и последовательности
148
149 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
150 парадигме событийного (или реактивного) программировния. Следует также понимать,
151 что при переходе от синхронного процедурного программирования к событийному так
152 же меняется и направление управления (Inverse Of Control), что означает
153 следующее:
154
155 * при работе с итераторами клиенты сами определяют момент чтения следующего
156 элемента последовательности.
157 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
158 их поступления и не могут на это повлиять.
159
160 Последний пункт можно изменить применив, например, буффер или канал с
161 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
162 наблюдателя.
163
164 ```ts
165 // обработка в цикле не гарантирует получения всех сообщений
166 while(1) {
167 // ожидаем следующее событие, по сути это подписка только на одно событие
168 let next = await events.next();
169
170 // такой цикл может пропускать сообщения, поскольку асинхронная операция
171 // позволит возобновить создание новых событий, на которые мы не подписаны
172 await processEvent(next);
173
174 // не только асинхронные операции могут привести к пропуску события
175 // например вызов метода, который приводит к созданию события так же
176 // приведет к тому, что созданное событие не будет обработано в текущем
177 // цикле
178 doSmthAndRiseEvent();
179 }
180
181 // для получения всех сообщений нужно регистрировать подписчика
182 events.on((data) => {
183 // будет вызван для всех сообщений
184 processEvent(data);
185 });
186 ``` No newline at end of file
@@ -0,0 +1,195
1 import { IObservable, IDestroyable, ICancellation } from './interfaces';
2 import { Cancellation } from './Cancellation'
3 import { argumentNotNull } from './safe';
4
5
6 interface Handler<T> {
7 (x: T): void
8 }
9
10 interface Initializer<T> {
11 (notify: Handler<T>, error?: (e: any) => void, complete?: () => void): void;
12 }
13
14 // TODO: think about to move this interfaces.ts and make it public
15 interface IObserver<T> {
16 next(event: T): void
17
18 error(e: any): void
19
20 complete(): void
21 }
22
23 const noop = () => {};
24
25 export class Observable<T> implements IObservable<T> {
26 private _once = new Array<IObserver<T>>();
27
28 private _observers = new Array<IObserver<T>>();
29
30
31 private _complete: boolean
32
33 private _error: any
34
35 constructor(func?: Initializer<T>) {
36 if (func)
37 func(
38 this._notifyNext.bind(this),
39 this._notifyError.bind(this),
40 this._notifyCompleted.bind(this)
41 );
42 }
43
44 /**
45 * Registers handlers for the current observable object.
46 *
47 * @param next the handler for events
48 * @param error the handler for a error
49 * @param complete the handler for a completion
50 * @returns {IDestroyable} the handler for the current subscription, this
51 * handler can be used to unsubscribe from events.
52 *
53 */
54 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
55 argumentNotNull(next, "next");
56
57 let me = this;
58
59 let observer: IObserver<T> & IDestroyable = {
60 next: next,
61 error: error ? error.bind(null) : noop,
62 complete: complete ? complete.bind(null) : noop,
63
64 destroy() {
65 me._removeObserver(this);
66 }
67 };
68
69 this._addObserver(observer);
70
71
72 return observer;
73 }
74
75 private _addObserver(observer: IObserver<T>) {
76 if (this._complete) {
77 try {
78 if (this._error)
79 observer.error(this._error);
80 else
81 observer.complete();
82 } catch (e) {
83 this.onObserverException(e);
84 }
85 } else {
86 this._observers.push(observer);
87 }
88 }
89
90 /**
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.
93 *
94 * @param ct a cancellation token
95 */
96 next(ct: ICancellation = Cancellation.none): Promise<T> {
97 return new Promise<T>((resolve, reject) => {
98 let observer: IObserver<T> = {
99 next: resolve,
100 error: reject,
101 complete: () => reject("No more events are available")
102 };
103
104 if (this._addOnce(observer) && ct.isSupported()) {
105 ct.register((e) => {
106 this._removeOnce(observer);
107 reject(e);
108 });
109 }
110 });
111 }
112
113 private _addOnce(observer: IObserver<T>) {
114 if (this._complete) {
115 try {
116 if (this._error)
117 observer.error(this._error);
118 else
119 observer.complete();
120 } catch (e) {
121 this.onObserverException(e);
122 }
123 return false;
124 }
125
126 this._once.push(observer);
127 return true;
128 }
129
130 protected onObserverException(e: any) {
131 }
132
133 private _removeOnce(d: IObserver<T>) {
134 let i = this._once.indexOf(d);
135 if (i >= 0)
136 this._once.splice(i, 1);
137 }
138
139 private _removeObserver(d: IObserver<T>) {
140 let i = this._observers.indexOf(d);
141 if (i >= 0)
142 this._observers.splice(i, 1);
143 }
144
145 private _notify(guard: (observer: IObserver<T>) => void) {
146 if (this._once.length) {
147 for (let i = 0; i < this._once.length; i++)
148 guard(this._once[i]);
149 this._once = [];
150 }
151
152 for (let i = 0; i < this._observers.length; i++)
153 guard(this._observers[i]);
154 }
155
156 protected _notifyNext(evt: T) {
157 let guard = (observer: IObserver<T>) => {
158 try {
159 observer.next(evt);
160 } catch (e) {
161 this.onObserverException(e);
162 }
163 }
164
165 this._notify(guard);
166 }
167
168 protected _notifyError(e: any) {
169 let guard = (observer: IObserver<T>) => {
170 try {
171 observer.error(e);
172 } catch (e) {
173 this.onObserverException(e);
174 }
175 }
176
177 this._notify(guard);
178 this._observers = [];
179 this._complete = true;
180 }
181
182 protected _notifyCompleted() {
183 let guard = (observer: IObserver<T>) => {
184 try {
185 observer.complete();
186 } catch (e) {
187 this.onObserverException(e);
188 }
189 }
190
191 this._notify(guard);
192 this._observers = [];
193 this._complete = true;
194 }
195 } No newline at end of file
@@ -0,0 +1,74
1 import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource'
2 import * as tape from 'tape';
3 import { TapeWriter, delay } from './TestTraits';
4 import { Observable } from '@implab/core/Observable';
5 import { IObservable } from '@implab/core/interfaces';
6
7 let trace = TraceSource.get("ObservableTests");
8
9 tape('events sequence example', async t => {
10
11
12 let events: IObservable<number>
13
14 let done = new Promise<void>((resolve) => {
15 events = new Observable<number>(async (notify, fail, complete) => {
16 for (let i = 0; i < 10; i++) {
17 await delay(0);
18 notify(i);
19 }
20 complete();
21 resolve();
22 });
23 });
24
25 let count = 0;
26 let complete = false;
27 events.on(x => count = count + x, null, () => complete = true);
28
29 let first = await events.next();
30
31 t.equals(first, 0, "the first event");
32 t.false(complete, "the sequence is not complete");
33
34 await done;
35
36 t.equals(count, 45, "the summ of the evetns");
37 t.true(complete, "the sequence is complete");
38
39 t.end();
40 });
41
42 tape('event sequence termination', async t => {
43 let events: IObservable<number>
44
45 let done = new Promise<void>((resolve) => {
46 events = new Observable<number>(async (notify, fail, complete) => {
47 await delay(0);
48 notify(1);
49 complete();
50 notify(2);
51 complete();
52 fail("Sequence terminated");
53 resolve();
54 });
55 });
56
57 let count = 0;
58 events.on(() => {}, (e) => count++, () => count++);
59
60 let first = await events.next();
61 t.equals(first, 1, "the first message");
62 try {
63 await events.next();
64 t.fail("shoud throw an exception");
65 } catch(e) {
66 t.pass("the sequence is terminated");
67 }
68
69 await done;
70
71 t.equals(count, 1, "the sequence must be terminated once");
72
73 t.end();
74 }); No newline at end of file
1 NO CONTENT: file renamed from docs/cancellations.ru.md to docs/en/cancellations.md
NO CONTENT: file renamed from docs/cancellations.ru.md to docs/en/cancellations.md
1 NO CONTENT: file copied from docs/cancellations.ru.md to docs/ru/cancellations.md
NO CONTENT: file copied from docs/cancellations.ru.md to docs/ru/cancellations.md
@@ -1,6 +1,8
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) {
@@ -1,13 +1,13
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
@@ -80,8 +80,4 function ActivatableMixin<TBase extends
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,8 +1,31
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 '../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();
@@ -56,14 +79,21 class Registry {
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) {
@@ -71,37 +101,37 class TraceSource extends Observable<Tra
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 /**
@@ -111,8 +141,8 class TraceSource extends Observable<Tra
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 /**
@@ -156,16 +186,3 class TraceSource extends Observable<Tra
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,9 +1,8
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) {
@@ -15,9 +14,9 class ConsoleWriter implements IDestroya
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);
@@ -27,9 +26,4 class ConsoleWriter implements IDestroya
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
@@ -228,4 +228,9 export function first(sequence: any, cb:
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,2 +1,3
1 define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]);
1 //define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]);
2 //define(["./CancellationTests"]); No newline at end of file
2 //define(["./CancellationTests"]);
3 define(["./ObservableTests"]); No newline at end of file
@@ -3,7 +3,8 define(["tape"], function(tape) {
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;
@@ -1,5 +1,5
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';
@@ -1,9 +1,8
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
@@ -24,9 +23,9 export class TapeWriter implements IDest
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);
@@ -1,4 +1,4
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
@@ -7,11 +7,11 const sourceId = 'test/TraceSourceTests'
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();
@@ -25,21 +25,21 tape('trace message', t => {
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 });
@@ -48,11 +48,11 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");
1 NO CONTENT: file was removed
NO CONTENT: file was removed
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