Comparing version 0.0.9 to 0.0.10
@@ -5,8 +5,22 @@ var async = require('async'), | ||
var eventflow = module.exports = function eventflow (eventEmitter) { | ||
// We were passed a 'class'. | ||
if (eventEmitter && eventEmitter.prototype && eventEmitter.prototype.on) { | ||
if (typeof eventEmitter === 'undefined') { | ||
eventEmitter = new EventEmitter(); | ||
} | ||
else if (eventEmitter.prototype && eventEmitter.prototype.on) { | ||
eventEmitter = eventEmitter.prototype; | ||
} | ||
else if (typeof eventEmitter === 'undefined') { | ||
eventEmitter = new EventEmitter; | ||
else if (!eventEmitter.on) { | ||
var tempEmitter = new EventEmitter(); | ||
Object.keys(tempEmitter).forEach(function (prop) { | ||
if (typeof eventEmitter[prop] !== 'undefined') { | ||
throw new Error('Conflict converting obj to eventflow emitter on property`' + prop + '`'); | ||
} | ||
eventEmitter[prop] = tempEmitter[prop]; | ||
}); | ||
Object.keys(EventEmitter.prototype).forEach(function (method) { | ||
if (typeof eventEmitter[method] !== 'undefined') { | ||
throw new Error('Conflict converting obj to eventflow emitter on method`' + method + '`'); | ||
} | ||
eventEmitter[method] = tempEmitter[method]; | ||
}); | ||
} | ||
@@ -67,3 +81,9 @@ | ||
if (fn.length <= args.length) { | ||
done(null, fn.apply(thisArg, args)); | ||
var result = fn.apply(thisArg, args); | ||
if (result instanceof Error) { | ||
done(result); | ||
} | ||
else { | ||
done(null, result); | ||
} | ||
} | ||
@@ -70,0 +90,0 @@ else { |
{ | ||
"name": "eventflow", | ||
"version": "0.0.9", | ||
"version": "0.0.10", | ||
"description": "Flow control for your event emitters", | ||
@@ -5,0 +5,0 @@ "main": "eventflow.js", |
169
README.md
@@ -10,2 +10,3 @@ EventFlow | ||
----- | ||
EventEmitters are an important part of well-designed node.js applications. | ||
@@ -20,19 +21,40 @@ `on()` and `emit()` can get you pretty far, but wouldn't it be great if you | ||
----- | ||
Attach eventflow to your event emitter: | ||
### Creating an EventFlow emitter | ||
Create a new emitter. | ||
```js | ||
var emitter = require('eventflow')(); | ||
``` | ||
Or, extend an existing emitter with EventFlow functionality. | ||
```js | ||
var EventEmitter = require('events').EventEmitter, | ||
require('eventflow')(EventEmitter), | ||
emitter = new EventEmitter(); | ||
require('eventflow')(emitter); | ||
``` | ||
Or, if you prefer not to extend the prototype: | ||
Or, extend an EventEmitter class with EventFlow functionality. | ||
```js | ||
var EventEmitter = require('events').EventEmitter, | ||
require('eventflow')(EventEmitter), | ||
emitter = new EventEmitter(); | ||
``` | ||
Or, convert any object into an EventFlow emitter. | ||
```js | ||
var emitter = { | ||
type: 'car', | ||
name: 'Honda' | ||
}; | ||
require('eventflow')(emitter); | ||
``` | ||
### Listen | ||
Listen for some events, with or without continuation callbacks. EventFlow does | ||
@@ -54,2 +76,4 @@ some simple introspection of your listeners to see if they accept a callback | ||
### Invoke listeners | ||
Now use one of the flow control methods to invoke your handlers and respond | ||
@@ -74,2 +98,31 @@ when they are done. | ||
Errors | ||
------ | ||
In synchronous listeners, you can return `Error` objects. | ||
```js | ||
emitter.on('foo', function () { | ||
return new Error('Something broke'); | ||
}); | ||
``` | ||
In async listeners, you should pass an `Error` as the first argument to the | ||
callback. | ||
```js | ||
emitter.on('foo', function (cb) { | ||
cb(new Error('Something broke')); | ||
}); | ||
``` | ||
No matter whether your listeners are sync or async, Errors will always be | ||
passed back as the first argument in the callback of the invocation. | ||
```js | ||
emitter.series('foo', function (err) { | ||
// `err` is the first error encountered. | ||
}); | ||
``` | ||
Advanced | ||
@@ -122,5 +175,27 @@ -------- | ||
Waterfall | ||
--------- | ||
The waterfall method allows listeners to modify a variable in a series. The | ||
first listener receives an initial value, and each subsequent listener modifies | ||
the return of the last listener: | ||
```js | ||
emitter.on('foo', function(n) { | ||
// sync task | ||
return n + 1; | ||
}); | ||
emitter.on('foo', function(n, callback) { | ||
// async task | ||
cb(null, n * 3); | ||
}); | ||
emitter.waterfall('foo', 2, function(err, n) { | ||
// n = 9 | ||
}); | ||
``` | ||
Invoke | ||
------ | ||
EventFlow also exposes the method `emitter.invoke(event, [args...], callback)`. | ||
EventFlow also attaches the method `emitter.invoke(event, [args...], callback)`. | ||
Invoke executes using the following rules: | ||
@@ -158,20 +233,80 @@ | ||
Waterfall | ||
--------- | ||
Example Use Case: Model API | ||
--------------------------- | ||
The waterfall method allows listeners to modify a variable in a series. The | ||
first listener receives an initial value, and each subsequent listener modifies | ||
the return of the last listener: | ||
Lets say you are designing a simple model api around redis (or whatever db you | ||
use). It has the following API: | ||
```js | ||
emitter.on('foo', function(n) { | ||
// sync task | ||
return n + 1; | ||
function Model () { | ||
// Constructor stuff. | ||
} | ||
Model.prototype = { | ||
load: function (id, cb) { | ||
// Load a model from the db. | ||
}, | ||
save: function (cb) { | ||
// Save the model. | ||
} | ||
} | ||
module.exports = Model; | ||
``` | ||
You know your app will need to support validation, but you dont want | ||
this Model module to include any of the app-specific validation logic. Using | ||
EventFlow, you could just use a 'validate' event to abstract it away. | ||
```js | ||
var eventflow = require('eventflow'); | ||
function Model () { | ||
// Constructor stuff. | ||
} | ||
eventflow(Model); | ||
Model.prototype = { | ||
load: function (id, cb) { | ||
// Load a model from the db. | ||
}, | ||
save: function (cb) { | ||
Model.parallel('validate', this, function (err) { | ||
if (err) { | ||
// There was an error validating the model or it was invalid. | ||
return cb(err); | ||
} | ||
else { | ||
// Save the model and eventually call `cb(null)`. | ||
} | ||
}); | ||
} | ||
} | ||
module.exports = Model; | ||
``` | ||
Now your app could do something like the following: | ||
```js | ||
var Model = require('./path/to/model'); | ||
// Simple validation. | ||
Model.on('validate', function (model) { | ||
if (model.title.length > 50) { | ||
return new Error('Titles should be 50 chars or less.'); | ||
} | ||
}); | ||
emitter.on('foo', function(n, callback) { | ||
// async task | ||
cb(null, n * 3); | ||
// Async validation that hits a db or something. | ||
Model.on('validate', function (model, cb) { | ||
Model.load(model.id, function (err, model) { | ||
if (err) return cb(err); | ||
if (model) return cb(new Error('A model already exists for this id.')); | ||
cb(null); | ||
}); | ||
}); | ||
emitter.waterfall('foo', 2, function(err, n) { | ||
// n = 9 | ||
var thing = new Model(); | ||
thing.save(function (err) { | ||
// Validation errors would appear here. | ||
}); | ||
@@ -178,0 +313,0 @@ ``` |
@@ -1,12 +0,13 @@ | ||
var eventflow = require('../'), | ||
EventEmitter = require('events').EventEmitter, | ||
assert = require('assert'); | ||
var eventflow = require('../') | ||
, assert = require('assert') | ||
, emitter; | ||
eventflow(EventEmitter); | ||
beforeEach(function() { | ||
emitter = eventflow(); | ||
}); | ||
describe('series', function() { | ||
var emitter, result; | ||
var result; | ||
beforeEach(function() { | ||
emitter = new EventEmitter(); | ||
result = []; | ||
@@ -90,2 +91,12 @@ }); | ||
}); | ||
it('should support sync listeners returning errors', function (done) { | ||
emitter.on('eat', function () { | ||
return new Error('I am full'); | ||
}); | ||
emitter.series('eat', function (err, results) { | ||
assert.equal(err.message, 'I am full'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -95,6 +106,5 @@ | ||
describe('parallel', function () { | ||
var emitter, result; | ||
var result; | ||
beforeEach(function () { | ||
emitter = new EventEmitter(); | ||
result = {}; | ||
@@ -138,8 +148,2 @@ }); | ||
describe('invoke', function () { | ||
var emitter; | ||
beforeEach(function () { | ||
emitter = new EventEmitter(); | ||
}); | ||
it('should cause an error if there are no listeners', function (done) { | ||
@@ -235,8 +239,2 @@ emitter.invoke('timestamp', function (err, timestamp) { | ||
describe('waterfall', function() { | ||
var emitter; | ||
beforeEach(function() { | ||
emitter = new EventEmitter(); | ||
}); | ||
it('should pass a value between handlers', function (done) { | ||
@@ -243,0 +241,0 @@ emitter.on('foo', function (n) { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19810
372
336