Skip to content
This repository has been archived by the owner on Nov 6, 2019. It is now read-only.

Commit

Permalink
Merge pull request #373 from afshin/observable-disposable
Browse files Browse the repository at this point in the history
Add observable disposable interface, delegate, and set.
  • Loading branch information
sccolbert committed Jun 11, 2019
2 parents 5a944b2 + 39e2c14 commit 0d84258
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 9 deletions.
3 changes: 2 additions & 1 deletion packages/disposable/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"watch": "tsc --build --watch"
},
"dependencies": {
"@phosphor/algorithm": "^1.1.3"
"@phosphor/algorithm": "^1.1.3",
"@phosphor/signaling": "^1.2.3"
},
"devDependencies": {
"@types/chai": "^3.4.35",
Expand Down
104 changes: 100 additions & 4 deletions packages/disposable/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
IterableOrArrayLike, each
} from '@phosphor/algorithm';

import {
ISignal, Signal
} from '@phosphor/signaling';


/**
* An object which implements the disposable pattern.
Expand Down Expand Up @@ -38,6 +42,18 @@ interface IDisposable {
}


/**
* A disposable object with an observable `disposed` signal.
*/
export
interface IObservableDisposable extends IDisposable {
/**
* A signal emitted when the object is disposed.
*/
readonly disposed: ISignal<this, void>;
}


/**
* A disposable object which delegates to a callback function.
*/
Expand Down Expand Up @@ -75,6 +91,34 @@ class DisposableDelegate implements IDisposable {
}


/**
* An observable disposable object which delegates to a callback function.
*/
export
class ObservableDisposableDelegate extends DisposableDelegate implements IObservableDisposable {
/**
* A signal emitted when the delegate is disposed.
*/
get disposed(): ISignal<this, void> {
return this._disposed;
}

/**
* Dispose of the delegate and invoke the callback function.
*/
dispose(): void {
if (this.isDisposed) {
return;
}
super.dispose();
this._disposed.emit(undefined);
Signal.clearData(this);
}

private _disposed = new Signal<this, void>(this);
}


/**
* An object which manages a collection of disposable items.
*/
Expand All @@ -89,7 +133,7 @@ class DisposableSet implements IDisposable {
* Test whether the set has been disposed.
*/
get isDisposed(): boolean {
return this._disposed;
return this._isDisposed;
}

/**
Expand All @@ -99,10 +143,10 @@ class DisposableSet implements IDisposable {
* Items are disposed in the order they are added to the set.
*/
dispose(): void {
if (this._disposed) {
if (this._isDisposed) {
return;
}
this._disposed = true;
this._isDisposed = true;
this._items.forEach(item => { item.dispose(); });
this._items.clear();
}
Expand Down Expand Up @@ -149,7 +193,7 @@ class DisposableSet implements IDisposable {
this._items.clear();
}

private _disposed = false;
private _isDisposed = false;
private _items = new Set<IDisposable>();
}

Expand All @@ -173,3 +217,55 @@ namespace DisposableSet {
return set;
}
}


/**
* An observable object which manages a collection of disposable items.
*/
export
class ObservableDisposableSet extends DisposableSet implements IObservableDisposable {
/**
* A signal emitted when the set is disposed.
*/
get disposed(): ISignal<this, void> {
return this._disposed;
}

/**
* Dispose of the set and the items it contains.
*
* #### Notes
* Items are disposed in the order they are added to the set.
*/
dispose(): void {
if (this.isDisposed) {
return;
}
super.dispose();
this._disposed.emit(undefined);
Signal.clearData(this);
}

private _disposed = new Signal<this, void>(this);
}


/**
* The namespace for the `ObservableDisposableSet` class statics.
*/
export
namespace ObservableDisposableSet {
/**
* Create an observable disposable set from an iterable of items.
*
* @param items - The iterable or array-like object of interest.
*
* @returns A new disposable initialized with the given items.
*/
export
function from(items: IterableOrArrayLike<IDisposable>): ObservableDisposableSet {
let set = new ObservableDisposableSet();
each(items, item => { set.add(item) });
return set;
}
}
59 changes: 58 additions & 1 deletion packages/disposable/tests/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
} from 'chai';

import {
DisposableDelegate, DisposableSet, IDisposable
DisposableDelegate, DisposableSet, IDisposable,
ObservableDisposableDelegate, ObservableDisposableSet
} from '@phosphor/disposable';


Expand Down Expand Up @@ -80,6 +81,22 @@ describe('@phosphor/disposable', () => {

});

describe('ObservableDisposableDelegate', () => {

describe('#disposed', () => {

it('should be emitted when the delegate is disposed', () => {
let called = false;
let delegate = new ObservableDisposableDelegate(() => { });
delegate.disposed.connect(() => { called = true; });
delegate.dispose();
expect(called).to.equal(true);
});

});

});

describe('DisposableSet', () => {

describe('#constructor()', () => {
Expand Down Expand Up @@ -277,4 +294,44 @@ describe('@phosphor/disposable', () => {

});

describe('ObservableDisposableSet', () => {

describe('#disposed', () => {

it('should be emitted when the set is disposed', () => {
let called = false;
let set = new ObservableDisposableSet();
let item1 = new TestDisposable();
let item2 = new TestDisposable();
let item3 = new TestDisposable();
set.add(item1);
set.add(item2);
set.add(item3);
set.disposed.connect(() => {
expect(set.contains(item1)).to.equal(false);
expect(set.contains(item2)).to.equal(false);
expect(set.contains(item3)).to.equal(false);
expect(item1.isDisposed).to.equal(true);
expect(item2.isDisposed).to.equal(true);
expect(item3.isDisposed).to.equal(true);
called = true;
});
set.dispose();
expect(called).to.equal(true);
});

});

describe('.from()', () => {

it('should accept an iterable of disposable items', () => {
let item1 = new TestDisposable();
let item2 = new TestDisposable();
let item3 = new TestDisposable();
let set = ObservableDisposableSet.from([item1, item2, item3]);
expect(set).to.be.an.instanceof(ObservableDisposableSet);
});

});
});
});
3 changes: 3 additions & 0 deletions packages/disposable/tests/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
"references": [
{
"path": "../../algorithm"
},
{
"path": "../../signaling"
}
]
}
5 changes: 4 additions & 1 deletion packages/disposable/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"references": [
{
"path": "../algorithm"
}
},
{
"path": "../signaling"
},
]
}
4 changes: 2 additions & 2 deletions packages/widgets/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@phosphor/algorithm';

import {
IDisposable
IObservableDisposable
} from '@phosphor/disposable';

import {
Expand Down Expand Up @@ -43,7 +43,7 @@ import {
* content.
*/
export
class Widget implements IDisposable, IMessageHandler {
class Widget implements IMessageHandler, IObservableDisposable {
/**
* Construct a new widget.
*
Expand Down

0 comments on commit 0d84258

Please sign in to comment.