Skip to content

Commit

Permalink
Breaking: Improve API & implementation based on suggestions (closes #1,
Browse files Browse the repository at this point in the history
closes #2)
  • Loading branch information
phated committed May 14, 2018
1 parent 7a03563 commit a17a119
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 143 deletions.
77 changes: 15 additions & 62 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,33 @@

var EventEmitter = require('events').EventEmitter;

var defaultNamespace = '__sparklesEventEmitter';
var sparklesNamespace = 'store@sparkles';
var defaultNamespace = 'default';

function getEmitter(namespace){

namespace = namespace || defaultNamespace;

var ee;

if(global[namespace]){
ee = global[namespace];
} else {
ee = new EventEmitter();

ee.setMaxListeners(0);
}
var store = global[sparklesNamespace];

function attach(type, listener){
// node will never fire the newListener on itself,
// so only check for remove/detach combo
if(type === 'removeListener' && listener === detach){
return;
}

if(!global[namespace]){
global[namespace] = ee;
}
if(!store){
store = global[sparklesNamespace] = {};
}

function detach(){
var events = Object.keys(ee._events);
var removeListeners = ee.listeners('removeListener');
var newListeners = ee.listeners('newListener');

// exit if we have events other than
// `removeListener` and `newListener`
if(events.length > 2){
return;
}

// exit if someone attached another
// listener to `removeListener`
if(removeListeners.length > 1){
return;
}
namespace = namespace || defaultNamespace;

// exit if someone attached another
// listener to `newListener`
if(newListeners.length > 1){
return;
}
var ee = store[namespace];

delete global[namespace];
if(!ee){
ee = store[namespace] = new EventEmitter();
ee.setMaxListeners(0);
ee.remove = function remove(){
ee.removeAllListeners();
delete store[namespace];
};
}

function rewire(){
var removeListeners = ee.listeners('removeListener');
var newListeners = ee.listeners('newListener');

// if we know our `removeListener` isn't
// attached, we add it
if(!removeListeners.length){
ee.on('removeListener', detach);
}

// if we know our `newListener` isn't
// attached, we add it
if(!newListeners.length){
ee.on('newListener', attach);
}
}
return ee;

rewire();

return ee;
}

module.exports = getEmitter;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"LICENSE"
],
"scripts": {
"test": "lab -cv",
"test": "lab -cv --ignore store@sparkles",
"lint": "jshint test index.js --reporter node_modules/jshint-stylish/stylish.js --exclude node_modules"
},
"dependencies": {},
Expand Down
20 changes: 13 additions & 7 deletions test/exists.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,33 @@ var EventEmitter = require('events').EventEmitter;

describe('namespace', function(){

beforeEach(function(done){
global['store@sparkles'] = {};
done();
});

afterEach(function(done){
delete global['store@sparkles'];
done();
});

it('should use an EE from sparkles namespace if it already exists', function(done){
var ee = global.__sparklesEventEmitter = new EventEmitter();
var ee = global['store@sparkles'].default = new EventEmitter();
ee.custom = 'ee';

var sparkles = require('../')();

expect(sparkles.custom).to.equal('ee');
sparkles.removeAllListeners();
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});

it('should allow custom namespaces', function(done){
var ee = global.__myCustomNamespace = new EventEmitter();
var ee = global['store@sparkles'].customNamespace = new EventEmitter();
ee.custom = true;

var sparkles = require('../')('__myCustomNamespace');
var sparkles = require('../')('customNamespace');

expect(sparkles.custom).to.equal(true);
sparkles.removeAllListeners();
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});
});
81 changes: 8 additions & 73 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,93 +19,28 @@ describe('sparkles', function(){

beforeEach(function(done){
sparkles = require('../')();
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});

afterEach(function(done){
sparkles.removeAllListeners();
sparkles.remove();
done();
});

it('will attach an event emitter to global upon the first `on` call', function(done){
sparkles.on('test', noop);
expect(global.__sparklesEventEmitter).to.exist();
expect(global.__sparklesEventEmitter.on).to.be.a.function();
done();
});

it('removes the event emitter from global if no more listeners exist it', function(done){
sparkles.on('test', noop);
expect(global.__sparklesEventEmitter).to.exist();
sparkles.removeListener('test', noop);
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});

it('even works with removeAllListeners', function(done){
sparkles.on('test1', noop);
sparkles.on('test2', noop2);
expect(global.__sparklesEventEmitter).to.exist();
sparkles.removeAllListeners();
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});

it('handles removing all newListeners', function(done){
sparkles.on('newListener', noop);
expect(global.__sparklesEventEmitter).to.exist();
sparkles.removeAllListeners('newListener');
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});

it('gracefully handles newListeners being added and removed', function(done){
sparkles.on('newListener', noop);
sparkles.on('newListener', noop2);
expect(global.__sparklesEventEmitter).to.exist();
sparkles.removeListener('newListener', noop);
expect(global.__sparklesEventEmitter).to.exist();
done();
});

it('handles removing all removeListeners', function(done){
sparkles.on('removeListener', noop);
expect(global.__sparklesEventEmitter).to.exist();
sparkles.removeAllListeners('removeListener');
expect(global.__sparklesEventEmitter).to.not.exist();
done();
});

it('gracefully handles removeListeners being added and removed', function(done){
sparkles.on('removeListener', noop);
sparkles.on('removeListener', noop2);
expect(global.__sparklesEventEmitter).to.exist();
sparkles.removeListener('removeListener', noop);
expect(global.__sparklesEventEmitter).to.exist();
it('will attach the sparkles store namespace to global', function(done){
expect(global['store@sparkles']).to.exist();
done();
});

it('recovers from removeAllListeners on removeListener upon new sparkles', function(done){
sparkles.on('test', noop);
sparkles.removeAllListeners('removeListener');
expect(sparkles.listeners('removeListener')).to.have.length(0);
var sparkles2 = require('../')();
expect(sparkles2.listeners('removeListener')).to.have.length(1);
it('will attach an event emitter to the sparkles store default namespace', function(done){
expect(global['store@sparkles']).to.include('default');
done();
});

it('does not add attach and detach more than once', function(done){
it('removes the event emitter from the store when remove is called', function(done){
sparkles.on('test', noop);
expect(sparkles.listeners('removeListener')).to.have.length(1);
var sparkles2 = require('../')();
expect(sparkles2.listeners('removeListener')).to.have.length(1);
done();
});

it('will not attach to global if the detach listener is added more than once', function(done){
sparkles.on('removeListener', sparkles._events.removeListener);
expect(global.__sparklesEventEmitter).to.not.exist();
sparkles.remove();
expect(global['store@sparkles']).to.not.include('default');
done();
});
});

0 comments on commit a17a119

Please sign in to comment.