##// END OF EJS Templates
working on Observable
cin -
r14:53e756f117f7 propose cancellat...
parent child
Show More
@@ -0,0 +1,21
1 import * as TraceSource from './TraceSource'
2
3 class TraceEvent {
4 readonly source: TraceSource;
5
6 readonly level: Number;
7
8 readonly arg: any;
9
10 constructor(source: TraceSource, level: Number, arg: any) {
11 this.source = source;
12 this.level = level;
13 this.arg = arg;
14 }
15 }
16
17 namespace TraceEvent {
18
19 }
20
21 export = TraceEvent No newline at end of file
@@ -0,0 +1,29
1 import { IObservable } from "../../interfaces";
2 import * as TraceEvent from '../TraceEvent';
3 import { ICancellation } from "../../interfaces";
4 import { Cancellation } from "../../Cancellation";
5 import * as TraceSource from "../TraceSource";
6
7 class ConsoleWriter {
8 async write(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
9 let next;
10 while(next = await source.next(ct)) {
11 this._writeEvent(next);
12 }
13 }
14
15 private _writeEvent(next: TraceEvent) {
16 if (next.level >= TraceSource.LogLevel) {
17 console.log(next.source.toString(), next.arg);
18 } else if(next.level >= TraceSource.WarnLevel) {
19 console.warn(next.source.toString(), next.arg);
20 } else {
21 console.error(next.source.toString(), next.arg);
22 }
23 }
24 }
25
26 namespace ConsoleWriter {
27 }
28
29 export = ConsoleWriter; No newline at end of file
@@ -1,23 +1,29
1 import { IObservable, IObserver, IDestroyable, ICancellation } from '../interfaces';
1 import { IObservable, IDestroyable, ICancellation } from '../interfaces';
2 import { Cancellation } from '../Cancellation'
2 import { Cancellation } from '../Cancellation'
3 import * as TraceSource from '../log/TraceSource'
4 import { argumentNotNull } from '../safe';
3 import { argumentNotNull } from '../safe';
5
4
6 const trace = TraceSource.get('@implab/core/components/Observable');
7
8
5
9 class Observable<T> implements IObservable<T> {
6 interface Handler<T> {
10 private _once = new Array<IObserver<T>>();
7 (x:T) : void
11
8 }
12 private readonly _observers = new Array<IObserver<T>>();
13
9
14 constructor(func: (notify: IObserver<T>) => void) {
10 interface Initializer<T> {
15 argumentNotNull(func, "func");
11 (notify: Handler<T>) : (() => void) | void;
16
17 func(this._notify.bind(this));
18 }
12 }
19
13
20 on(observer: IObserver<T>): IDestroyable {
14
15 class Observable<T> implements IObservable<T>, IDestroyable {
16 private _once = new Array<Handler<T>>();
17
18 private readonly _observers = new Array<Handler<T>>();
19
20 private readonly _cleanup : (() => void) | void;
21
22 constructor(func?: Initializer<T>) {
23 this._cleanup = func && func(this._notify.bind(this));
24 }
25
26 on(observer: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
21 argumentNotNull(observer, "observer");
27 argumentNotNull(observer, "observer");
22
28
23 this._observers.push(observer);
29 this._observers.push(observer);
@@ -30,7 +36,7 class Observable<T> implements IObservab
30 }
36 }
31 }
37 }
32
38
33 wait(ct: ICancellation = Cancellation.none): Promise<T> {
39 next(ct: ICancellation = Cancellation.none): Promise<T> {
34 return new Promise<T>((resolve, reject) => {
40 return new Promise<T>((resolve, reject) => {
35 this._once.push(resolve);
41 this._once.push(resolve);
36 if (ct.isSupported()) {
42 if (ct.isSupported()) {
@@ -42,24 +48,28 class Observable<T> implements IObservab
42 });
48 });
43 }
49 }
44
50
45 onObserverException(e: any) {
51 destroy() {
46 trace.error("Unhandled exception in the observer: {0}", e);
52 if(this._cleanup)
53 this._cleanup.call(null);
47 }
54 }
48
55
49 private _removeOnce(d: IObserver<T>) {
56 protected onObserverException(e: any) {
57 }
58
59 private _removeOnce(d: Handler<T>) {
50 let i = this._once.indexOf(d);
60 let i = this._once.indexOf(d);
51 if (i >= 0)
61 if (i >= 0)
52 this._once.splice(i);
62 this._once.splice(i);
53 }
63 }
54
64
55 private _removeObserver(d: IObserver<T>) {
65 private _removeObserver(d: Handler<T>) {
56 let i = this._observers.indexOf(d);
66 let i = this._observers.indexOf(d);
57 if (i >= 0)
67 if (i >= 0)
58 this._observers.splice(i);
68 this._observers.splice(i);
59 }
69 }
60
70
61 private _notify(evt: T) {
71 protected _notify(evt: T) {
62 let guard = (observer: IObserver<T>) => {
72 let guard = (observer: Handler<T>) => {
63 try {
73 try {
64 observer(evt);
74 observer(evt);
65 } catch (e) {
75 } catch (e) {
@@ -79,7 +89,6 class Observable<T> implements IObservab
79 }
89 }
80
90
81 namespace Observable {
91 namespace Observable {
82 export const traceSource = trace;
83 }
92 }
84
93
85 export = Observable; No newline at end of file
94 export = Observable;
@@ -1,5 +1,3
1 import { watchFile } from "fs";
2
3 export interface IDestroyable {
1 export interface IDestroyable {
4 destroy();
2 destroy();
5 }
3 }
@@ -72,12 +70,7 export interface ICancellable {
72 cancel(reason?: any): void;
70 cancel(reason?: any): void;
73 }
71 }
74
72
75 export interface IObserver<T> {
76 (x:T): void;
77 }
78
79 export interface IObservable<T> {
73 export interface IObservable<T> {
80 on(observer: IObserver<T>): IDestroyable;
74 on(next: (x:T) => void, error?: (e:any) => void, complete?:() => void): IDestroyable;
81
75 next(ct?: ICancellation) : Promise<T>;
82 wait(ct?: ICancellation) : Promise<T>;
83 } No newline at end of file
76 }
@@ -1,17 +1,8
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
3 import * as Observable from '../components/Observable'
4 interface TraceEventHandler {
4 import { IDestroyable } from '../interfaces';
5 (sender: TraceSource, level: number, arg: any): void;
5 import * as TraceEvent from './TraceEvent'
6 }
7
8 interface TraceSourceHandler {
9 (source: TraceSource): void;
10 }
11
12 interface Destroyable {
13 destroy();
14 }
15
6
16 class Registry {
7 class Registry {
17 static readonly instance = new Registry();
8 static readonly instance = new Registry();
@@ -46,7 +37,7 class Registry {
46 this._listeners[i].call(null, source);
37 this._listeners[i].call(null, source);
47 }
38 }
48
39
49 on(handler: TraceSourceHandler): Destroyable {
40 on(handler: (source: TraceSource) => void): IDestroyable {
50 argumentNotNull(handler, "handler");
41 argumentNotNull(handler, "handler");
51 var me = this;
42 var me = this;
52
43
@@ -65,45 +56,18 class Registry {
65 }
56 }
66 }
57 }
67
58
68 class TraceSource {
59 class TraceSource extends Observable<TraceEvent> {
69
70 readonly id: any
60 readonly id: any
71
61
72 // using array will provide faster iteration the with object
73 private _handlers: Array<TraceEventHandler> = new Array<TraceEventHandler>();
74
75 level: number
62 level: number
76
63
77 constructor(id: any) {
64 constructor(id: any) {
65 super();
78 this.id = id || new Object();
66 this.id = id || new Object();
79 }
67 }
80
68
81 on(handler: TraceEventHandler): Destroyable {
82 argumentNotNull(handler, "handler");
83 var me = this;
84 me._handlers.push(handler);
85
86 return {
87 destroy() {
88 me.remove(handler);
89 }
90 }
91 }
92
93 remove(handler: TraceEventHandler): void {
94 let i = this._handlers.indexOf(handler);
95 if (i >= 0)
96 this._handlers.splice(i, 1);
97 }
98
99 protected emit(level: number, arg: any) {
69 protected emit(level: number, arg: any) {
100 this._handlers.forEach(h => {
70 this._notify(new TraceEvent(this, level, arg));
101 try {
102 h(this, level, arg);
103 } catch (e) {
104 // suppress error in log handlers
105 }
106 });
107 }
71 }
108
72
109 isDebugEnabled() {
73 isDebugEnabled() {
@@ -178,7 +142,7 class TraceSource {
178 *
142 *
179 * @param handler the handler which will be called for each trace source
143 * @param handler the handler which will be called for each trace source
180 */
144 */
181 static on(handler: TraceSourceHandler) {
145 static on(handler: (source: TraceSource) => void) {
182 return Registry.instance.on(handler);
146 return Registry.instance.on(handler);
183 }
147 }
184
148
@@ -1,10 +1,8
1 import * as tape from 'tape';
1 import * as tape from 'tape';
2 import * as ActivatableMixin from '@implab/core/components/ActivatableMixin';
2 import * as 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 } from '@implab/core/components/IActivationController';
4 import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces';
5 import { IActivatable } from '@implab/core/components/IActivatable';
5 import { Cancellation } from '@implab/core/Cancellation';
6 import { ICancellation } from '@implab/core/ICancellation';
7 import { EmptyCancellation } from '@implab/core/EmptyCancellation';
8
6
9 class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
7 class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
10
8
@@ -33,20 +31,20 class MockActivationController implement
33 await component.activate();
31 await component.activate();
34 }
32 }
35
33
36 async activating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
34 async activating(component: IActivatable, ct: ICancellation = Cancellation.none) {
37 if (component != this._active)
35 if (component != this._active)
38 await this.deactivate();
36 await this.deactivate();
39 }
37 }
40
38
41 async activated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
39 async activated(component: IActivatable, ct: ICancellation = Cancellation.none) {
42 this._active = component;
40 this._active = component;
43 }
41 }
44
42
45 async deactivating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
43 async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) {
46
44
47 }
45 }
48
46
49 async deactivated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
47 async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) {
50 if (this._active == component)
48 if (this._active == component)
51 this._active = null;
49 this._active = null;
52 }
50 }
@@ -1,5 +1,6
1 import * as TraceSource from '@implab/core/log/TraceSource'
1 import * as TraceSource from '@implab/core/log/TraceSource'
2 import * as tape from 'tape';
2 import * as tape from 'tape';
3 import * as ConsoleWriter from '@implab/core/log/writers/ConsoleWriter';
3
4
4 const sourceId = 'test/TraceSourceTests';
5 const sourceId = 'test/TraceSourceTests';
5
6
@@ -8,10 +9,10 tape('trace message', t => {
8
9
9 trace.level = TraceSource.DebugLevel;
10 trace.level = TraceSource.DebugLevel;
10
11
11 let h = trace.on((sender,level,msg) => {
12 let h = trace.on((ev) => {
12 t.equal(sender, trace, "sender should be the current trace source");
13 t.equal(ev.source, trace, "sender should be the current trace source");
13 t.equal(TraceSource.DebugLevel, level, "level should be debug level");
14 t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level");
14 t.equal(msg, "Hello, World!", "The message should be a formatted message");
15 t.equal(ev.arg, "Hello, World!", "The message should be a formatted message");
15
16
16 t.end();
17 t.end();
17 });
18 });
@@ -30,10 +31,10 tape('trace event', t => {
30 name: "custom event"
31 name: "custom event"
31 };
32 };
32
33
33 let h = trace.on((sender,level,msg) => {
34 let h = trace.on((ev) => {
34 t.equal(sender, trace, "sender should be the current trace source");
35 t.equal(ev.source, trace, "sender should be the current trace source");
35 t.equal(TraceSource.DebugLevel, level, "level should be debug level");
36 t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level");
36 t.equal(msg, event, "The message should be the specified object");
37 t.equal(ev.arg, event, "The message should be the specified object");
37
38
38 t.end();
39 t.end();
39 });
40 });
@@ -41,4 +42,21 tape('trace event', t => {
41 trace.traceEvent(TraceSource.DebugLevel, event);
42 trace.traceEvent(TraceSource.DebugLevel, event);
42
43
43 h.destroy();
44 h.destroy();
45 });
46
47 tape('console writer', async t => {
48 let writer = new ConsoleWriter();
49
50 let trace = TraceSource.get(sourceId);
51 trace.level = TraceSource.DebugLevel;
52
53 let p = writer.write(trace);
54
55 trace.log("Hello, {0}!", 'World');
56 trace.warn("Look at me!");
57 trace.error("DIE!");
58
59 console.log("DONE");
60
61 t.end();
44 }); No newline at end of file
62 });
General Comments 0
You need to be logged in to leave comments. Login now