-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 85c5102
Showing
7 changed files
with
374 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/** | ||
* | ||
* Event Emitter | ||
* | ||
* Publisher/Subscriber Pattern implementation. | ||
* | ||
* Differences from Node's EventEmitter: | ||
* .after(cb) method (see below) | ||
* Listeners that throw errors are caught and logged to console. | ||
* Listeners happen during their own turn. | ||
* Listener can only listen a maximum of one time with .on() | ||
* "removeListener" is called "off" | ||
* No way to see if the listener is listening. (unused) | ||
* No "addListener" alias | ||
* No domains (unused) | ||
* No erroring on unhandled error event. (unused) | ||
* No maximum listener count. (unused) | ||
* No prototype/No `new` needed to create. | ||
* | ||
* after(callback: function (err: Error U undefined, res: Any U undefined, type: String, ...args: Any)): undefined | ||
* callback is ran after every listener returns. | ||
* First parameter to the callback is the error of the listener, if there is one. | ||
* Second parameter to the callback is the return value of the listener, if there is one. | ||
* The rest of the parameters are the parameters sent to the .emit() that triggered the listener. | ||
* | ||
* If you need one of the features this lacks that is marked unused, feel free to send a pull request/file an issue. | ||
*/ | ||
|
||
|
||
const Set = require('simplesets').Set; // Still waiting on that ES6 Set API. | ||
const Promise = require('bluebird'); | ||
|
||
const EventEmitter = function () { | ||
const events = {}; // Map (Set Fn) | ||
var postListenerCallbacks = []; // [Error -> any -> string -> ...any -> void] | ||
|
||
return { | ||
on: function (type, listener) { | ||
if (!events[type]) { | ||
events[type] = new Set(); | ||
} | ||
|
||
events[type].add(listener); | ||
}, | ||
once: function (type, listener) { | ||
const that = this; | ||
|
||
function o () { | ||
that.off(type, o); | ||
return listener.apply(null, Array.prototype.slice.call(arguments)); | ||
} | ||
|
||
this.on(type, o); | ||
}, | ||
off: function (type, listener) { | ||
if (events[type]) { | ||
events[type].remove(listener); | ||
} | ||
}, | ||
emit: function (type) { | ||
const args = Array.prototype.slice.call(arguments, 1); | ||
|
||
if (!events[type]) { | ||
return; | ||
} | ||
|
||
events[type].each(function (listener) { | ||
setImmediate(function () { | ||
Promise.try(listener, args) | ||
// Catch both return results and errors thrown in listener. | ||
.then(function (res) { | ||
postListenerCallbacks.forEach(function (lcb) { | ||
lcb.apply(null, [undefined, res, type].concat(args)); | ||
}); | ||
}, function (err) { | ||
postListenerCallbacks.forEach(function (lcb) { | ||
lcb.apply(null, [err, undefined, type].concat(args)); | ||
}); | ||
}) | ||
// Catch errors in after chain | ||
.catch(function (err) { | ||
console.log(err.name); | ||
console.log(err.stack); | ||
throw err; | ||
}) | ||
.done(); | ||
}); | ||
}); | ||
}, | ||
|
||
after: function (callback) { | ||
postListenerCallbacks.push(callback); | ||
} | ||
}; | ||
}; | ||
|
||
module.exports = EventEmitter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/usr/bin/env node | ||
|
||
var fez = require('fez'); | ||
var sweetjs = require('fez-sweet.js'); | ||
|
||
exports.build = function(rule) { | ||
rule('test.sjs', 'test.js', sweetjs({'modules': ['sweet-bdd'], 'readableNames': true})); | ||
}; | ||
|
||
exports.default = exports.build; | ||
|
||
fez(module); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "after-events", | ||
"version": "1.0.0", | ||
"description": "Event Emitter with hooks for listener returnn values", | ||
"main": "after-events.js", | ||
"scripts": { | ||
"test": "mocha test.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/Havvy/after-events.git" | ||
}, | ||
"keywords": [ | ||
"events", | ||
"event-emitter" | ||
], | ||
"author": "Ryan Scheel", | ||
"license": "ISC", | ||
"bugs": { | ||
"url": "https://github.com/Havvy/after-events/issues" | ||
}, | ||
"homepage": "https://github.com/Havvy/after-events", | ||
"dependencies": { | ||
"bluebird": "~1.0.0", | ||
"simplesets": "~1.2.0" | ||
}, | ||
"devDependencies": { | ||
"fez": "git://github.com/fez/fez", | ||
"fez-sweet.js": "~0.6.0", | ||
"sweet-bdd": "~1.0.0", | ||
"mocha": "~1.17.0", | ||
"better-assert": "~1.0.0", | ||
"sinon": "~1.7.3", | ||
"deep-eql": "~0.1.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# After Event Emitter | ||
|
||
``` | ||
npm install after-events | ||
``` | ||
|
||
Publisher/Subscriber Pattern implementation. | ||
|
||
## Differences from Node's EventEmitter | ||
|
||
* .after(cb) method (see below) | ||
* Listeners that throw errors are caught and logged to console. | ||
* Listeners happen during their own turn. | ||
* Listener can only listen a maximum of one time with .on() | ||
* "removeListener" is called "off" | ||
* No way to see if the listener is listening. (unused) | ||
* No "addListener" alias | ||
* No domains (unused) | ||
* No erroring on unhandled error event. (unused) | ||
* No maximum listener count. (unused) | ||
* No prototype/No `new` needed to create. | ||
|
||
If you need one of the features this lacks that is marked unused, feel free to send a pull request/file an issue. | ||
|
||
### after(callback: function (err: Error U undefined, res: Any U undefined, type: String, ...args: Any)): undefined | ||
|
||
* callback is ran after every listener returns. | ||
* First parameter to the callback is the error/rejected value of the listener, if there is one. | ||
* Second parameter to the callback is the return value of the listener, if there is one. | ||
* The rest of the parameters are the parameters sent to the .emit() that triggered the listener. | ||
* Calling it multiple times adds more callbacks to be called in order of addition. | ||
|
||
### Example | ||
|
||
``` | ||
// This is really contrived! | ||
var eventEmitter = require('after-events'); | ||
var format = require('util').format; | ||
eventEmitter.after(function (err, ret, eventname, eventarg) { | ||
console.log(format('%s + 1 = %s', eventarg, ret)); | ||
}); | ||
eventEmitter.on('number', function (number) { | ||
return number + 1; | ||
}); | ||
eventEmitter.emit('number', 2); | ||
// Console logs '2 + 1 = 3' | ||
eventEmitter.emit('number', 5); | ||
// Console.logs '5 + 1 = 6' | ||
``` | ||
|
||
For a realistic example, see [https://github.com/Tennu/tennu/blob/master/lib/command-handler.js](Tennu's Command Handler). | ||
|
||
### Tests and Building | ||
|
||
To build the test file, use `./fez.js`. | ||
|
||
To run the test file, use `mocha test.js`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
const sinon = require('sinon'); | ||
const assert = require('better-assert'); | ||
const equal = require('deep-eql'); | ||
const inspect = require('util').inspect; | ||
const format = require('util').format; | ||
const debug = false; | ||
const logfn = debug ? console.log.bind(console) : function () { | ||
}; | ||
const EventEmiter = require('./after-events.js'); | ||
describe('After Event Emitter', function () { | ||
var EE; | ||
beforeEach(function () { | ||
logfn(); | ||
EE = EventEmiter(); | ||
}); | ||
it('works as an event emitter.', function (done) { | ||
EE.on('x', function (arg1, arg2) { | ||
assert(arg1 === true); | ||
assert(arg2 === false); | ||
done(); | ||
}); | ||
EE.emit('x', true, false); | ||
}); | ||
it('does not throw on non-existent events.', function (done) { | ||
EE.emit('y'); | ||
done(); | ||
}); | ||
describe('#after', function () { | ||
it('takes a function, which it calls after the listener returns.', function (done) { | ||
EE.on('x', function () { | ||
return true; | ||
}); | ||
EE.after(function (err, ret, emitted, arg1, arg2) { | ||
assert(err === undefined); | ||
assert(ret === true); | ||
assert(emitted === 'x'); | ||
assert(arg1 === true); | ||
assert(arg2 === false); | ||
done(); | ||
}); | ||
EE.emit('x', true, false); | ||
}); | ||
it('passes the error to err if an error is thrown', function (done) { | ||
const error = new Error(); | ||
EE.on('x', function () { | ||
throw error; | ||
}); | ||
EE.after(function (err, ret, emitted) { | ||
assert(err === error); | ||
assert(ret === undefined); | ||
done(); | ||
}); | ||
EE.emit('x'); | ||
}); | ||
it('can take multiple functions, and call them in order', function (done) { | ||
var callCount = 0; | ||
EE.on('x', function () { | ||
console.log('My!'); | ||
return true; | ||
}); | ||
EE.after(function (err, ret, emitted) { | ||
console.log('Hi!'); | ||
try { | ||
assert(callCount === 0); | ||
callCount += 1; | ||
} catch (e) { | ||
done(e); | ||
} | ||
}); | ||
EE.after(function (err, ret, emitted) { | ||
console.log('Bye!'); | ||
try { | ||
assert(callCount === 1); | ||
done(); | ||
} catch (e) { | ||
done(e); | ||
} | ||
}); | ||
EE.emit('x'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
const sinon = require('sinon'); | ||
const assert = require('better-assert'); | ||
const equal = require('deep-eql'); | ||
const inspect = require('util').inspect; | ||
const format = require('util').format; | ||
|
||
const debug = false; | ||
const log = debug ? console.log.bind(console) : function () {}; | ||
|
||
const EventEmiter = require('./after-events.js'); | ||
|
||
describe 'After Event Emitter' { | ||
var EE; | ||
|
||
beforeEach { | ||
log(/* newline */); | ||
EE = EventEmiter(); | ||
} | ||
|
||
it 'works as an event emitter.' (done) { | ||
EE.on('x', function (arg1, arg2) { | ||
assert(arg1 === true); | ||
assert(arg2 === false); | ||
done() | ||
}); | ||
|
||
EE.emit('x', true, false); | ||
} | ||
|
||
it 'does not throw on non-existent events.' (done) { | ||
EE.emit('y'); | ||
done(); | ||
} | ||
|
||
describe '#after' { | ||
it 'takes a function, which it calls after the listener returns.' (done) { | ||
EE.on('x', function () {return true;}); | ||
EE.after(function (err, ret, emitted, arg1, arg2) { | ||
assert(err === undefined); | ||
assert(ret === true); | ||
assert(emitted === 'x'); | ||
assert(arg1 === true); | ||
assert(arg2 === false); | ||
done(); | ||
}); | ||
EE.emit('x', true, false); | ||
} | ||
|
||
it 'passes the error to err if an error is thrown' (done) { | ||
const error = new Error(); | ||
EE.on('x', function () {throw error}); | ||
EE.after(function (err, ret, emitted) { | ||
assert(err === error); | ||
assert(ret === undefined); | ||
done(); | ||
}); | ||
EE.emit('x'); | ||
} | ||
|
||
it 'can take multiple functions, and call them in order' (done) { | ||
var callCount = 0; | ||
|
||
EE.on('x', function () { return true; }); | ||
EE.after(function (err, ret, emitted) { | ||
try { | ||
assert(callCount === 0); | ||
callCount += 1; | ||
} catch (e) { | ||
done(e); | ||
} | ||
}); | ||
EE.after(function (err, ret, emitted) { | ||
try { | ||
assert(callCount === 1); | ||
done(); | ||
} catch (e) { | ||
done(e); | ||
} | ||
}); | ||
|
||
EE.emit('x'); | ||
} | ||
} | ||
} |