Skip to content
This repository has been archived by the owner on Jan 16, 2024. It is now read-only.

Commit

Permalink
Fixing the Perishable
Browse files Browse the repository at this point in the history
This now follows the spec that Annie wants.
  • Loading branch information
pspeter3 committed Jan 22, 2015
1 parent bfe89a3 commit d8dcb23
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 26 deletions.
67 changes: 41 additions & 26 deletions src/perishable.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
import Handle = require("./handle");

interface PerishableNodeIterator<T> {
callback: () => any;
next: PerishableNode<T>;
}

class PerishableNode<T> implements Handle<T> {
private _value: T;
private _callback: () => any;
private _prev: PerishableNode<T>;
private _next: PerishableNode<T>;
_value: T;
_callback: () => any;
_prev: PerishableNode<T>;
_next: PerishableNode<T>;

constructor(value: T, callback: () => any, prev: PerishableNode<T> = null) {
this._value = value;
this._callback = callback;
this._prev = prev;
this._next = null;
if (this.hasPrev()) {
this._next = this._prev._next;
this._prev._next = this;
if (this._hasPrev()) {
this._setNext(this._prev._next);
this._prev._setNext(this);
}
if (this.hasNext()) {
this._next._prev = this;
if (this._hasNext()) {
this._next._setPrev(this);
}
}

hasPrev(): boolean {
_setPrev(prev: PerishableNode<T>): void {
this._prev = prev;
}

_setNext(next: PerishableNode<T>): void {
this._next = next;
// We only automatically call the callback when the head is unused
if (!this._hasPrev() && !this._hasNext()) {
this._callback();
}
}

_hasPrev(): boolean {
return this._prev !== null;
}

hasNext(): boolean {
_hasNext(): boolean {
return this._next !== null;
}

Expand All @@ -33,20 +50,10 @@ class PerishableNode<T> implements Handle<T> {
}

release(): void {
if (this.hasPrev()) {
this._prev._next = this._next;
}
if (this.hasNext()) {
this._next._prev = this._prev;
}
}

pop(): void {
if (this.hasNext()) {
this._next.pop();
this._next = null;
this._prev._setNext(this._next);
if (this._hasNext()) {
this._next._setPrev(this._prev);
}
this._callback();
}
}

Expand Down Expand Up @@ -88,7 +95,7 @@ class Perishable<T> {
* @returns {boolean}
*/
isUnused(): boolean {
return this.isStale() || !this._head.hasNext();
return this.isStale() || !this._head._hasNext();
}

/**
Expand All @@ -108,8 +115,16 @@ class Perishable<T> {
*/
makeStale(): void {
if (!this.isStale()) {
this._head.pop();
var node = this._head;
this._head = null;
var callbacks: Function[] = [];
while (node !== null) {
callbacks.push(node._callback);
node = node._next;
}
for (var i = callbacks.length - 1; i >= 0; i--) {
callbacks[i]();
}
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions test/perishable_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ describe("Perishable", () => {
perishable.createHandle(null).release();
sinon.assert.calledOnce(spy);
});

it("should call onUnused every time", () => {
var spy = sinon.spy();
var perishable = new tsutil.Perishable(VALUE, spy);
perishable.createHandle(null).release();
perishable.createHandle(null).release();
sinon.assert.calledTwice(spy);
});
});

describe("value", () => {
Expand Down Expand Up @@ -164,5 +172,46 @@ describe("Perishable", () => {
perishable.makeStale();
second.release();
});

it("should be stale once make stale is called", () => {
var perishable = new tsutil.Perishable(VALUE, sinon.spy());
perishable.createHandle(() => { assert.isTrue(perishable.isStale()); });
perishable.createHandle(() => { assert.isTrue(perishable.isStale()); });
perishable.makeStale();
});

it("should call onUnused when becoming stale", () => {
var spy = sinon.spy();
var perishable = new tsutil.Perishable(VALUE, spy);
perishable.makeStale();
sinon.assert.calledOnce(spy);
});

it("should handle a release call during makeStale", () => {
var onUnused = sinon.spy();
var spy = sinon.spy();
var perishable = new tsutil.Perishable(VALUE, onUnused);
var toRelease = perishable.createHandle(spy);
perishable.createHandle(() => {
toRelease.release();
});
perishable.makeStale();
sinon.assert.calledOnce(onUnused);
sinon.assert.calledOnce(spy);
});

it("should handle another release call during makeStale", () => {
var onUnused = sinon.spy();
var spy = sinon.spy();
var perishable = new tsutil.Perishable(VALUE, onUnused);
var toRelease: tsutil.Releasable;
perishable.createHandle(() => {
toRelease.release();
});
toRelease = perishable.createHandle(spy);
perishable.makeStale();
sinon.assert.calledOnce(onUnused);
sinon.assert.calledOnce(spy);
});
});
});

0 comments on commit d8dcb23

Please sign in to comment.