Events implemented and tested, without buffering

This commit is contained in:
Fabrice Marsaud 2015-04-01 15:06:39 +02:00
parent 92d7d61926
commit a3d7e541d3
4 changed files with 300 additions and 62 deletions

View File

@ -0,0 +1,210 @@
var chai = require('chai');
var expect = chai.expect;
var dirtyChai = require('dirty-chai');
chai.use(dirtyChai);
var leche = require('leche');
var sinon = require('sinon');
var Collection = require('./collection');
var col = new Collection.Collection();
describe('collection events', function () {
// ============================================================
var data1 = {
'primitive value': ['foo1', 1],
'array value': ['bar1', [1,2]],
'object value': ['baz1', {a:1, b:2}]
};
var addSpy;
var updateSpy;
var removeSpy;
beforeEach(function () {
addSpy = sinon.spy();
updateSpy = sinon.spy();
removeSpy = sinon.spy();
col.on('add', addSpy);
col.on('update', updateSpy);
col.on('remove', removeSpy);
});
afterEach(function () {
col.removeAllListeners();
});
// Collection is empty =======================================================
describe('add', function () {
leche.withData(data1, function (key, value) {
it('Emits an add event transporting the key and value', function () {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.add(key, value)).to.eql(col);
expect(addSpy.called).to.be.true();
expect(addSpy.calledWith({key: value})).to.be.true();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
});
});
});
var updateData1 = {
'primitive value': ['foo1', 3],
'array value': ['bar1', [3,4]],
'object value': ['baz1', {c:2, d:4}]
};
// Collection contains data1 =================================================
describe('update', function () {
leche.withData(updateData1, function (key, value) {
it('Emits an update event transporting the key and value', function () {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.update(key, value)).to.eql(col);
expect(updateSpy.called).to.be.true();
expect(updateSpy.calledWith({key: value})).to.be.true();
expect(addSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
});
});
});
var data2 = {
'primitive value': ['foo2', 1],
'array value': ['bar2', [1,2]],
'object value': ['baz2', {a:1, b:2}]
};
// Collection contains data1 updated =========================================
describe('set', function () {
leche.withData(data1, function (key, value) {
it('Emits an update event for pre-existing keys', function () {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.update(key, value)).to.eql(col);
expect(updateSpy.called).to.be.true();
expect(updateSpy.calledWith({key: value})).to.be.true();
expect(addSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
});
});
// Collection contains data1 =============================================
leche.withData(data2, function (key, value) {
it('Emits an add event for unexisting keys', function () {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.add(key, value)).to.eql(col);
expect(addSpy.called).to.be.true();
expect(addSpy.calledWith({key: value})).to.be.true();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
});
});
});
// Collection contains data1 & data 2 ========================================
describe('remove', function () {
leche.withData(data1, function (key, value) {
it('Emits an remove event transporting the key and removed value', function () {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.remove(key)).to.eql(col);
expect(removeSpy.called).to.be.true();
expect(removeSpy.calledWith({key: value})).to.be.true();
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
});
});
});
// Collection contains data 2 ================================================
describe('clear', function () {
var clearedData;
it('Emits a remove event', function() {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.clear()).to.eq(col);
expect(removeSpy.calledOnce).to.be.true();
clearedData = removeSpy.lastCall.args[0];
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
});
it('Emits no event if collection is empty', function() {
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
expect(col.clear()).to.eq(col);
expect(addSpy.called).to.be.false();
expect(updateSpy.called).to.be.false();
expect(removeSpy.called).to.be.false();
});
leche.withData(data2, function (key, value) {
it('Emits a remove event with all cleared data', function () {
expect(clearedData[key]).to.eq(value);
});
});
});
});

View File

@ -1,4 +1,6 @@
import makeError from 'make-error'; import makeError from 'make-error';
import events from 'events';
import _forEach from 'lodash.foreach';
const AlreadyBuffering = makeError('AlreadyBuffering'); const AlreadyBuffering = makeError('AlreadyBuffering');
const NotBuffering = makeError('NotBuffering'); const NotBuffering = makeError('NotBuffering');
@ -6,10 +8,12 @@ const IllegalAdd = makeError('IllegalAdd');
const DuplicateEntry = makeError('DuplicateEntry'); const DuplicateEntry = makeError('DuplicateEntry');
const NoSuchEntry = makeError('NoSuchEntry'); const NoSuchEntry = makeError('NoSuchEntry');
class Collection { class Collection extends events.EventEmitter {
constructor () { constructor () {
super();
this._map = {}; this._map = {};
this._buffering = false; this._buffering = false;
this._size = 0; this._size = 0;
@ -18,11 +22,7 @@ class Collection {
_initBuffer () { _initBuffer () {
this._buffer = { this._buffer = {};
remove: [],
add: [],
update: []
};
} }
@ -50,25 +50,11 @@ class Collection {
} }
_touch (key, action) { _touch (action, key, value) {
// TODO Buffers changes or emits an event // TODO enable buffering
} this.emit(action, {key: value});
_set (key, value) {
this._map[key] = value;
this._touch(key, 'update');
return this;
}
_unset (key) {
delete this._map[key];
this._touch(key, 'remove');
return this;
} }
@ -127,9 +113,13 @@ class Collection {
const [key, value] = this.resolveEntry.apply(this, arguments); const [key, value] = this.resolveEntry.apply(this, arguments);
this._assertHasNot(key); this._assertHasNot(key);
this._size++;
return this._set(key, value); this._map[key] = value;
this._size++;
this._touch('add', key, value);
return this;
} }
@ -137,11 +127,15 @@ class Collection {
const [key, value] = this.resolveEntry.apply(this, arguments); const [key, value] = this.resolveEntry.apply(this, arguments);
if (!this.has(key)) { const action = this.has(key) ? 'update' : 'add';
this._map[key] = value;
if ('add' === action) {
this._size++; this._size++;
} }
return this._set(key, value); this._touch(action, key, value);
return this;
} }
@ -158,7 +152,10 @@ class Collection {
this._assertHas(key); this._assertHas(key);
return this._set(key, value); this._map[key] = value;
this._touch('update', key, value);
return this;
} }
@ -167,9 +164,27 @@ class Collection {
const [key] = this.resolveEntry(keyOrObjectWithId, null); const [key] = this.resolveEntry(keyOrObjectWithId, null);
this._assertHas(key); this._assertHas(key);
const oldValue = this.get(key);
delete this._map[key];
this._size--; this._size--;
return this._unset(key); this._touch('remove', key, oldValue);
return this;
}
clear () {
if (this._size > 0) {
this.emit('remove', this._map);
}
this._map = {};
this._size = 0;
return this;
} }

View File

@ -4,17 +4,15 @@ var dirtyChai = require('dirty-chai');
chai.use(dirtyChai); chai.use(dirtyChai);
var leche = require('leche'); var leche = require('leche');
console.log(expect);
var Collection = require('./collection'); var Collection = require('./collection');
var col = new Collection.Collection(); var col = new Collection.Collection();
describe('collection', function () { describe('collection', function () {
// ============================================================ // Collection is empty =======================================================
var fixtureValues1 = { var data1 = {
'primitive value': ['foo1', 1], 'primitive value': ['foo1', 1],
'array value': ['bar1', [1,2]], 'array value': ['bar1', [1,2]],
'object value': ['baz1', {a:1, b:2}] 'object value': ['baz1', {a:1, b:2}]
@ -22,7 +20,7 @@ describe('collection', function () {
describe('add', function () { describe('add', function () {
leche.withData(fixtureValues1, function (key, value) { leche.withData(data1, function (key, value) {
it('Adds a new entry to the collection', function () { it('Adds a new entry to the collection', function () {
expect(col.add(key, value)).to.eql(col); expect(col.add(key, value)).to.eql(col);
@ -30,7 +28,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues1, function (key, value) { leche.withData(data1, function (key, value) {
it('We cannot add on a pre-existing key', function () { it('We cannot add on a pre-existing key', function () {
expect(function () { expect(function () {
@ -42,9 +40,9 @@ describe('collection', function () {
}); });
// ============================================================ // Collection contains data 1 ================================================
var fixtureValues2 = { var data2 = {
'primitive value': ['foo2', 1], 'primitive value': ['foo2', 1],
'array value': ['bar2', [1,2]], 'array value': ['bar2', [1,2]],
'object value': ['baz2', {a:1, b:2}] 'object value': ['baz2', {a:1, b:2}]
@ -52,7 +50,7 @@ describe('collection', function () {
describe('set', function () { describe('set', function () {
leche.withData(fixtureValues2, function (key, value) { leche.withData(data2, function (key, value) {
it('Sets an entry of the collection...', function () { it('Sets an entry of the collection...', function () {
expect(col.set(key, value)).to.eql(col); expect(col.set(key, value)).to.eql(col);
@ -60,7 +58,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues2, function (key, value) { leche.withData(data2, function (key, value) {
it('...would it already exists or not', function () { it('...would it already exists or not', function () {
expect(col.set(key, value)).to.eql(col); expect(col.set(key, value)).to.eql(col);
@ -70,15 +68,15 @@ describe('collection', function () {
}); });
// ============================================================ // Collection contains data 1 & 2 ============================================
var fixtureUnexisting = { var unexistingData = {
'Unexisting key/entry': ['wat', 'any'] 'Unexisting key/entry': ['wat', 'any']
}; };
describe('get', function () { describe('get', function () {
leche.withData(fixtureValues1, function (key, value) { leche.withData(data1, function (key, value) {
it('Returns the value of an entry of the collection...', function () { it('Returns the value of an entry of the collection...', function () {
expect(col.get(key)).to.eql(value); expect(col.get(key)).to.eql(value);
@ -86,7 +84,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues2, function (key, value) { leche.withData(data2, function (key, value) {
it('Returns the value of an entry of the collection...', function () { it('Returns the value of an entry of the collection...', function () {
expect(col.get(key)).to.eql(value); expect(col.get(key)).to.eql(value);
@ -94,7 +92,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureUnexisting, function (key) { leche.withData(unexistingData, function (key) {
it('...or throws if it does not exist', function () { it('...or throws if it does not exist', function () {
expect(function () { expect(function () {
@ -106,9 +104,9 @@ describe('collection', function () {
}); });
// ============================================================ // Collection contains data 1 & 2 ============================================
var fixtureUpdates = { var updateData2 = {
'primitive value': ['foo2', 3], 'primitive value': ['foo2', 3],
'array value': ['bar2', [3,4]], 'array value': ['bar2', [3,4]],
'object value': ['baz2', {c:3, d:4}] 'object value': ['baz2', {c:3, d:4}]
@ -116,7 +114,7 @@ describe('collection', function () {
describe('update', function () { describe('update', function () {
leche.withData(fixtureUpdates, function (key, value) { leche.withData(updateData2, function (key, value) {
it('updates the given entries...', function () { it('updates the given entries...', function () {
expect(col.update(key, value)).to.eql(col); expect(col.update(key, value)).to.eql(col);
@ -124,7 +122,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureUpdates, function (key, value) { leche.withData(updateData2, function (key, value) {
it('...so we can see the values we get have changed accordingly', function () { it('...so we can see the values we get have changed accordingly', function () {
expect(col.get(key)).to.eql(value); expect(col.get(key)).to.eql(value);
@ -132,7 +130,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureUnexisting, function (key, value) { leche.withData(unexistingData, function (key, value) {
it('If the entry does not exist, updating throws', function () { it('If the entry does not exist, updating throws', function () {
expect(function () { expect(function () {
@ -144,11 +142,11 @@ describe('collection', function () {
}); });
// ============================================================ // Collection contains data 1 & 2 updated ====================================
describe('remove', function () { describe('remove', function () {
leche.withData(fixtureValues2, function (key) { leche.withData(data2, function (key) {
it('removes the given entries...', function () { it('removes the given entries...', function () {
expect(col.remove(key)).to.eql(col); expect(col.remove(key)).to.eql(col);
@ -156,7 +154,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues2, function (key) { leche.withData(data2, function (key) {
it('...so trying to get them again throws...', function () { it('...so trying to get them again throws...', function () {
expect(function () { expect(function () {
@ -166,7 +164,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues2, function (key) { leche.withData(data2, function (key) {
it('...and trying to remove them again also throws', function () { it('...and trying to remove them again also throws', function () {
expect(function () { expect(function () {
@ -178,11 +176,11 @@ describe('collection', function () {
}); });
// ============================================================ // Collection contains data 1 ================================================
describe('has', function () { describe('has', function () {
leche.withData(fixtureValues1, function (key) { leche.withData(data1, function (key) {
it('Tells us if an entry exists...', function () { it('Tells us if an entry exists...', function () {
expect(col.has(key)).to.be.true(); expect(col.has(key)).to.be.true();
@ -190,7 +188,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues2, function (key) { leche.withData(data2, function (key) {
it('...or not', function () { it('...or not', function () {
expect(col.has(key)).to.be.false(); expect(col.has(key)).to.be.false();
@ -200,21 +198,21 @@ describe('collection', function () {
}); });
// ============================================================ // Collection contains data 1 ================================================
describe('size', function () { describe('size', function () {
it('Reveals the number of existing entries', function () { it('Reveals the number of existing entries', function () {
expect(col.size).to.eq(Object.keys(fixtureValues1).length); expect(col.size).to.eq(Object.keys(data1).length);
}); });
}); });
// ============================================================ // Collection contains data 1 ================================================
describe('all', function () { describe('all', function () {
leche.withData(fixtureValues1, function (key, value) { leche.withData(data1, function (key, value) {
it('Gives access to the internal collection...', function () { it('Gives access to the internal collection...', function () {
expect(col.all).to.have.ownProperty(key); expect(col.all).to.have.ownProperty(key);
@ -223,7 +221,7 @@ describe('collection', function () {
}); });
leche.withData(fixtureValues2, function (key) { leche.withData(data2, function (key) {
it('Gives access to the internal collection...', function () { it('Gives access to the internal collection...', function () {
expect(col.all).to.not.have.ownProperty(key); expect(col.all).to.not.have.ownProperty(key);
@ -238,4 +236,17 @@ describe('collection', function () {
}); });
// Collection contains data 1 ================================================
describe('clear', function () {
it('wipes out all the collection', function () {
expect(col.clear()).to.eq(col);
expect(col.size).to.eq(0);
expect(col.all).to.eql({});
});
});
}); });

View File

@ -4,6 +4,7 @@
"description": "A generice batch collection attempt", "description": "A generice batch collection attempt",
"main": "collection.js", "main": "collection.js",
"dependencies": { "dependencies": {
"lodash.foreach": "^3.0.2",
"make-error": "^0.3.0" "make-error": "^0.3.0"
}, },
"devDependencies": { "devDependencies": {
@ -11,7 +12,8 @@
"chai": "^2.2.0", "chai": "^2.2.0",
"dirty-chai": "^1.2.0", "dirty-chai": "^1.2.0",
"leche": "^2.1.1", "leche": "^2.1.1",
"mocha": "^2.2.1" "mocha": "^2.2.1",
"sinon": "^1.14.1"
}, },
"scripts": { "scripts": {
"test": "mocha --require babel/register *.spec.js" "test": "mocha --require babel/register *.spec.js"