# HG changeset patch # User cin # Date 2018-09-16 21:29:22 # Node ID 93dca6f27f52a31d38cd8b75cbc0b953219d48cd # Parent dd8f8dfcd934daa44544761f8755e9dd98a32510 Code cleanup, remove explicit export object from modules initial work docs/observable.ru.md diff --git a/docs/ActivatableMixin/observable.ru.md b/docs/ActivatableMixin/observable.ru.md new file mode 100644 --- /dev/null +++ b/docs/ActivatableMixin/observable.ru.md @@ -0,0 +1,55 @@ +# Observable + +Универсальный способ организации потока сообщений. Данный механизм может +использоваться для оповещения об изменениях состояний объектов или для доставки +самостоятельных событий, например, связанных с действиями пользователя. + +Является реализацией классического наблюдателя с возможность сообщить о коце +потока событий. Данная реализация не содержит никаких дополнительных функций, +таких как фильтрация, канал с состоянием, преобразования сообщений и т.п. Это +сделано специально, чтобы реализация оставалась максимально простой. + +Пример того, как можно создать последовательность из 10 событий: + +```ts + +var events = new Observable(async (notify, error, complete) => { + // цикл в котором возникает событие + for(let i = 0; i < 10; i++) { + await delay(1000); + // в качестве данных передается номер события + notify(i); + } + // по окончании последовательности информируем, что событий больше не будет + compelte(); +}); + +// создаем окно с отображением хода событий +var progress = showProgress({ min: 0, max: 9, current: 0}); + +// подписываемся на события +events.on( + // обработчик очередного события + msg => { + progress.setValue(msg); + }. + // обработчик ошибки + e => { + progress.showError(e); + }, + // обработчик конца потока + () => { + progress.close(); + } +); + +``` + +```ts + +// превращаем события dom в Observable +var events = new Observable((notify) => { + on(domNode,'mousemove', notify); +}); + +``` \ No newline at end of file diff --git a/src/js/log/trace.js b/src/js/log/trace.js --- a/src/js/log/trace.js +++ b/src/js/log/trace.js @@ -1,6 +1,8 @@ -define(["./TraceSource"], function (TraceSource) { +define(["./TraceSource"], function (TraceSource_1) { 'use strict'; + var TraceSource = TraceSource_1.TraceSource; + return { on: function (filter, cb) { diff --git a/src/ts/components/ActivatableMixin.ts b/src/ts/components/ActivatableMixin.ts --- a/src/ts/components/ActivatableMixin.ts +++ b/src/ts/components/ActivatableMixin.ts @@ -1,13 +1,13 @@ import { IActivationController, IActivatable, ICancellation } from '../interfaces'; import { AsyncComponent } from './AsyncComponent'; import { Cancellation } from '../Cancellation'; -import * as TraceSource from '../log/TraceSource'; +import { TraceSource } from '../log/TraceSource'; type Constructor = new (...args: any[]) => T; const log = TraceSource.get('@implab/core/components/ActivatableMixin'); -function ActivatableMixin>(Base: TBase) { +export function ActivatableMixin>(Base: TBase) { return class extends Base implements IActivatable { _controller: IActivationController; @@ -80,8 +80,4 @@ function ActivatableMixin void; + _completion: Promise = Promise.resolve(); getCompletion() { return this._completion }; runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) { + // create inner cancellation bound to the passed cancellation token + let h: IDestroyable; + let inner = new Cancellation(cancel => { + + this._cancel = cancel; + h = ct.register(cancel); + }); + // TODO create cancellation source here - async function guard() { - await op(ct); + let guard = async () => { + try { + await op(inner); + } finally { + // after the operation is complete we need to cleanup the + // resources + destroy(h); + this._cancel = null; + } } return this._completion = guard(); } + + cancel(reason) { + if (this._cancel) + this._cancel(reason); + } } \ No newline at end of file diff --git a/src/ts/components/Observable.ts b/src/ts/components/Observable.ts --- a/src/ts/components/Observable.ts +++ b/src/ts/components/Observable.ts @@ -22,7 +22,7 @@ interface IObserver { const noop = () => {}; -class Observable implements IObservable { +export class Observable implements IObservable { private _once = new Array>(); private _observers = new Array>(); @@ -190,9 +190,4 @@ class Observable implements IObservab this._notify(guard); this._observers = []; } -} - -namespace Observable { -} - -export = Observable; \ No newline at end of file +} \ No newline at end of file diff --git a/src/ts/log/TraceEvent.ts b/src/ts/log/TraceEvent.ts deleted file mode 100644 --- a/src/ts/log/TraceEvent.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as TraceSource from './TraceSource' - -class TraceEvent { - readonly source: TraceSource; - - readonly level: Number; - - readonly arg: any; - - constructor(source: TraceSource, level: Number, arg: any) { - this.source = source; - this.level = level; - this.arg = arg; - } -} - -namespace TraceEvent { - -} - -export = TraceEvent \ No newline at end of file diff --git a/src/ts/log/TraceSource.ts b/src/ts/log/TraceSource.ts --- a/src/ts/log/TraceSource.ts +++ b/src/ts/log/TraceSource.ts @@ -1,8 +1,31 @@ import * as format from '../text/format' import { argumentNotNull } from '../safe'; -import * as Observable from '../components/Observable' +import { Observable } from '../components/Observable' import { IDestroyable } from '../interfaces'; -import * as TraceEvent from './TraceEvent' + +export const DebugLevel = 400; + +export const LogLevel = 300; + +export const WarnLevel = 200; + +export const ErrorLevel = 100; + +export const SilentLevel = 0; + +export class TraceEvent { + readonly source: TraceSource; + + readonly level: Number; + + readonly arg: any; + + constructor(source: TraceSource, level: Number, arg: any) { + this.source = source; + this.level = level; + this.arg = arg; + } +} class Registry { static readonly instance = new Registry(); @@ -56,14 +79,21 @@ class Registry { } } -class TraceSource extends Observable { +export class TraceSource { readonly id: any level: number + readonly events: Observable + + _notifyNext: (arg: TraceEvent) => void + constructor(id: any) { - super(); + this.id = id || new Object(); + this.events = new Observable((next) => { + this._notifyNext = next; + }) } protected emit(level: number, arg: any) { @@ -71,37 +101,37 @@ class TraceSource extends Observable= TraceSource.DebugLevel; + return this.level >= DebugLevel; } debug(msg: string, ...args: any[]) { - if (this.isEnabled(TraceSource.DebugLevel)) - this.emit(TraceSource.DebugLevel, format(msg, args)); + if (this.isEnabled(DebugLevel)) + this.emit(DebugLevel, format(msg, args)); } isLogEnabled() { - return this.level >= TraceSource.LogLevel; + return this.level >= LogLevel; } log(msg: string, ...args: any[]) { - if (this.isEnabled(TraceSource.LogLevel)) - this.emit(TraceSource.LogLevel, format(msg, args)); + if (this.isEnabled(LogLevel)) + this.emit(LogLevel, format(msg, args)); } isWarnEnabled() { - return this.level >= TraceSource.WarnLevel; + return this.level >= WarnLevel; } warn(msg: string, ...args: any[]) { - if (this.isEnabled(TraceSource.WarnLevel)) - this.emit(TraceSource.WarnLevel, format(msg, args)); + if (this.isEnabled(WarnLevel)) + this.emit(WarnLevel, format(msg, args)); } /** * returns true if errors will be recorded. */ isErrorEnabled() { - return this.level >= TraceSource.ErrorLevel; + return this.level >= ErrorLevel; } /** @@ -111,8 +141,8 @@ class TraceSource extends Observable(); writeEvents(source: IObservable, ct: ICancellation = Cancellation.none) { @@ -15,9 +14,9 @@ class ConsoleWriter implements IDestroya } writeEvent(next: TraceEvent) { - if (next.level >= TraceSource.LogLevel) { + if (next.level >= LogLevel) { console.log(next.source.id.toString(), next.arg); - } else if(next.level >= TraceSource.WarnLevel) { + } else if(next.level >= WarnLevel) { console.warn(next.source.id.toString(), next.arg); } else { console.error(next.source.id.toString(), next.arg); @@ -27,9 +26,4 @@ class ConsoleWriter implements IDestroya destroy() { this._subscriptions.forEach(x => x.destroy()); } -} - -namespace ConsoleWriter { -} - -export = ConsoleWriter; \ No newline at end of file +} \ No newline at end of file diff --git a/src/ts/safe.ts b/src/ts/safe.ts --- a/src/ts/safe.ts +++ b/src/ts/safe.ts @@ -228,4 +228,9 @@ export function first(sequence: any, cb: return err(new Error("The sequence is required")); else throw new Error("The sequence is required"); +} + +export function destroy(d: any) { + if (d && 'destroy' in d) + d.destroy(); } \ No newline at end of file diff --git a/test/js/trace-test.js b/test/js/trace-test.js --- a/test/js/trace-test.js +++ b/test/js/trace-test.js @@ -3,7 +3,8 @@ define(["tape"], function(tape) { var sourceId = '73a633f3-eab8-49b0-8601-07cae710f234'; var sourceId2 = '3ba9c7cd-ed77-437b-9a2f-1cbeb1226b5b'; tape('Load TraceSource for the module', function(t) { - require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource) { + require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource_1) { + var TraceSource = TraceSource_1.TraceSource; t.equal(trace && trace.id, sourceId, "trace should be taken from the loader plugin parameter"); var count = 0; diff --git a/test/ts/ActivatableTests.ts b/test/ts/ActivatableTests.ts --- a/test/ts/ActivatableTests.ts +++ b/test/ts/ActivatableTests.ts @@ -1,5 +1,5 @@ import * as tape from 'tape'; -import * as ActivatableMixin from '@implab/core/components/ActivatableMixin'; +import { ActivatableMixin} from '@implab/core/components/ActivatableMixin'; import { AsyncComponent } from '@implab/core/components/AsyncComponent'; import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces'; import { Cancellation } from '@implab/core/Cancellation'; diff --git a/test/ts/TestTraits.ts b/test/ts/TestTraits.ts --- a/test/ts/TestTraits.ts +++ b/test/ts/TestTraits.ts @@ -1,9 +1,8 @@ -import { IObservable, ICancellation, IDestroyable } from "../../build/dist/interfaces"; -import * as TraceEvent from '../../build/dist/log/TraceEvent'; -import { Cancellation } from "../../build/dist/Cancellation"; -import * as TraceSource from "../../build/dist/log/TraceSource"; +import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces"; +import { Cancellation } from "@implab/core/Cancellation"; +import { TraceEvent, LogLevel, WarnLevel } from "@implab/core/log/TraceSource"; import * as tape from 'tape'; -import { argumentNotNull } from "../../build/dist/safe"; +import { argumentNotNull } from "@implab/core/safe"; export class TapeWriter implements IDestroyable { readonly _tape: tape.Test @@ -24,9 +23,9 @@ export class TapeWriter implements IDest } writeEvent(next: TraceEvent) { - if (next.level >= TraceSource.LogLevel) { + if (next.level >= LogLevel) { this._tape.comment("LOG " + next.arg); - } else if (next.level >= TraceSource.WarnLevel) { + } else if (next.level >= WarnLevel) { this._tape.comment("WARN " + next.arg); } else { this._tape.comment("ERROR " + next.arg); diff --git a/test/ts/TraceSourceTests.ts b/test/ts/TraceSourceTests.ts --- a/test/ts/TraceSourceTests.ts +++ b/test/ts/TraceSourceTests.ts @@ -1,4 +1,4 @@ -import * as TraceSource from '@implab/core/log/TraceSource' +import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource' import * as tape from 'tape'; import { TapeWriter } from './TestTraits'; @@ -7,11 +7,11 @@ const sourceId = 'test/TraceSourceTests' tape('trace message', t => { let trace = TraceSource.get(sourceId); - trace.level = TraceSource.DebugLevel; + trace.level = DebugLevel; - let h = trace.on((ev) => { + let h = trace.events.on((ev) => { t.equal(ev.source, trace, "sender should be the current trace source"); - t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level"); + t.equal(ev.level, DebugLevel, "level should be debug level"); t.equal(ev.arg, "Hello, World!", "The message should be a formatted message"); t.end(); @@ -25,21 +25,21 @@ tape('trace message', t => { tape('trace event', t => { let trace = TraceSource.get(sourceId); - trace.level = TraceSource.DebugLevel; + trace.level = DebugLevel; let event = { name: "custom event" }; - let h = trace.on((ev) => { + let h = trace.events.on((ev) => { t.equal(ev.source, trace, "sender should be the current trace source"); - t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level"); + t.equal(ev.level, DebugLevel, "level should be debug level"); t.equal(ev.arg, event, "The message should be the specified object"); t.end(); }); - trace.traceEvent(TraceSource.DebugLevel, event); + trace.traceEvent(DebugLevel, event); h.destroy(); }); @@ -48,11 +48,11 @@ tape('tape comment writer', async t => { let writer = new TapeWriter(t); TraceSource.on(ts => { - writer.writeEvents(ts); + writer.writeEvents(ts.events); }); let trace = TraceSource.get(sourceId); - trace.level = TraceSource.DebugLevel; + trace.level = DebugLevel; trace.log("Hello, {0}!", 'World'); trace.log("Multi\n line");