Skip to content

Commit

Permalink
Merge branch events into master.
Browse files Browse the repository at this point in the history
  • Loading branch information
flatheadmill committed Apr 24, 2013
2 parents c203c8f + 0154f16 commit c66611e
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 143 deletions.
74 changes: 21 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,70 +106,35 @@ If the error is `ENOENT`, we exit early by calling the step function directly as
a if it were itself an error/result callback, passing `null` to indicate no
error.

## Working with Events and `` EventEmittter ``
## Working with Events.

There are times when our API doesn't want us to give it a Node.js style
`error, result` callback. When working with DOM events or jQuery in the browser
or `` EventEmitter `` we are generally going to need two types of callbacks, an
event handling callback that accepts the result, plus an error handling callback
that handles an error, but only if there is an error.
Cadence also works with event emitting objects that do not accept an error as
the first parameter. These are event mechanisms like the DOM events or the
events generated by the Node.js `EventEmitter`.

When we want to handle events like this, we need create an event
Here is a unit test for working with `EventEmitter` illustrating the
`once` handler.
Here is a unit test for working with `EventEmitter` illustrating the use of
events in Cadence.

```javascript
var event = require('event'),
assert = require('assert'),
cadence = require('cadence'),
ee = new event.EventEmitter();
var cadence = require('cadence'), event = require('event')
, ee = new event.EventEmitter();

cadence(function (step, ee) {
var event = step([].shift);
step(function () {
ee.on('end', event());
ee.on('error', event([], 0));
}, function (end) {
assert.equal(end, 'done');
});
})(ee);

ee.emit('end', 'done');
```

When you invoke `once` an inverse future is created that collects the
emitted event value. Cadence will wait until the `once` value is emitted
before continuing to the next step. The value will be assigned to the
cadence context using the event name as a variable name.

Unlike the `once` handler, the `on` handler does not block the next step
in the cadence.

```javascript
var cadence = require('cadence'),
event = require('event'),
emitter = new event.EventEmitter();

cadence(function (emitter, step) {
step(function () {
step(emitter).once('end');
ee.on('data', step.event([]));
ee.on('end', step.event());
ee.on('error', step.error());
}, function (data) {
assert.deepEqual(data, [ 1, 2, 3 ]);
});
})(emitter);

emitter.emit('end', 'done');
```

When you invoke `on`, the results will be gathered in an array in the
cadence context, keyed by the name of the event. The callback will
gather results until the final callback for the step function is
invoked.
ee.emit('data', 1);
ee.emit('data', 2);
ee.emit('data', 3);

**TODO**: Currently there is no support for events that emit more than
one argument. You can +1 [this
issue](https://github.com/bigeasy/cadence/issues/50) if this is blocking
your project.
ee.emit('end');
```

Below we use the example of splitting an HTTP server log for many hosts
into a log file for each host.
Expand All @@ -181,7 +146,8 @@ cadence(function (step) {
step(function () {
var readable = fs.readableStream(__dirname + '/logins.txt');
readable.setEncoding('utf8');
step(readable).on('data').once('end');
readable.on('data', step.event([]));
readable.on('end');
}, function (data) {
var hosts = {};
data.join('').split(/\n/).foreach(function (line) {
Expand All @@ -191,12 +157,14 @@ cadence(function (step) {
for (var host in hosts) {
var writable = fs.writableStream(__dirname + '/' + host + '.log');
writable.end(hosts[host].join('\n') + '\n');
step(writable).once('drain');
writable.on('drain', step.event());
}
});
})();
```

This is a horrible example. Try again.

## Change Log

Changes for each release.
Expand Down
54 changes: 9 additions & 45 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,6 @@ function cadence () {
if (fixup = (vargs[0] === async)) {
vargs.shift();
}
if (typeof vargs[0] == "string") {
callback.event = vargs.shift();
}
if (typeof vargs[0] == "object" && !Array.isArray(vargs[0])) {
callback.target = vargs.shift();
}
if (!isNaN(parseInt(vargs[0], 10))) {
callback.arity = +(vargs.shift());
}
Expand All @@ -107,7 +101,6 @@ function cadence () {
callback.shifted = !! vargs.shift();
}
callback.cadence = vargs;
if (callback.event) return createEvent(invocations[0], callback);
invocations[0].callbacks.push(callback);
if (vargs.length) {
if (!vargs.every(function (arg) { return typeof arg == "function" })) {
Expand All @@ -121,6 +114,15 @@ function cadence () {
return createCallback(invocations[0], callback, 0);
}

async.event = function () {
var callback = async.apply(null, arguments);
return function () {
return callback.apply(null, [ null ].concat(__slice.call(arguments)));
}
}

async.error = function () { return async.apply(null, [0, []].concat(__slice.call(arguments))) }

// Create a sub-cadence.
function createCadence (invocation, callback) {
var index = 0;
Expand All @@ -140,44 +142,6 @@ function cadence () {
}
}

// Create an event callback.
function createEvent (invocation, prototype) {
return function () {
var vargs = __slice.call(arguments),
callback = Object.create(prototype, { errors: { value: [] }, results: { value: [] } }),
targets = [], event, fn;
invocations[0].callbacks.push(callback);
while (vargs[0] && vargs[0][callback.event]) {
targets.push(vargs.shift());
}
if (!targets.length && callback.target) {
targets.push(callback.target);
}
if (typeof vargs[0] == "string") {
event = vargs.shift();
} else {
throw new Error("event name required");
}
if (Array.isArray(vargs[0])) {
callback.arrayed = !! vargs.shift();
} else if (event == "error") {
callback.arrayed = true;
callback.arity = 0;
}
if (!isNaN(+(vargs[0]))) {
callback.arity = +(vargs.shift());
}
callback.shifted = event != "error";
fn = callback.arrayed
? createCallback(invocations[0], callback, -1)
: createCallback(invocations[0], callback, 0);
while (targets.length) {
targets.shift()[callback.event](event, fn);
}
return fn;
}
}

// Create a scalar callback.
function createCallback (invocation, callback, index) {
if (-1 < index) invocation.count++;
Expand Down
69 changes: 24 additions & 45 deletions t/cadence/events.t.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
#!/usr/bin/env node

require('proof')(9, function (equal, ok, step, deepEqual) {
require('proof')(5, function (equal, ok, step, deepEqual) {
var EventEmitter = require('events').EventEmitter,
ee = new EventEmitter(),
cadence = require('../..');
cadence = require('../..'), ee;

ee = new EventEmitter();

cadence(function (step, ee) {
var on = step('on');
step(function () {
on(ee, 'data', []);
on(ee, 'end');
ee.on('data', step.event([]));
ee.on('end', step.event());
}, function (data) {
deepEqual(data, [ 1, 2, 3 ], 'events');
});
Expand All @@ -21,51 +21,38 @@ require('proof')(9, function (equal, ok, step, deepEqual) {

ee.emit('end');

cadence(function (step, ee) {
step(function () {
var on = step('on', ee);
on('data', []);
on('end');
}, function (data) {
deepEqual(data, [ 1, 2, 3 ], 'events bound to specific object');
});
})(ee, step());

ee.emit('data', 1);
ee.emit('data', 2);
ee.emit('data', 3);

ee.emit('end');
ee = new EventEmitter();

cadence(function (step, ees) {
cadence(function (step, ee) {
step(function () {
var on = step('on');
on(ees[0], ees[1], ees[2], 'error', Error);
on(ees[0], 'end');
ee.on('error', step.error());
ee.on('end', step.event());
});
})([ ee, new EventEmitter, new EventEmitter ], function (error) {
})(ee, function (error) {
equal(error.message, 'error', 'error event');
});

ee.emit('error', new Error('error'));

ee = new EventEmitter();

cadence(function (step, ees) {
step(function () {
var on = step('on');
on(ees[0], ees[1], ees[2], 'error', Error);
on(ees[0], 'end');
ee.on('error', step.error());
ee.on('end', step.event());
}, function (end) {
equal(end, 'ended', 'errors ignored');
equal(end, 'ended', 'error ignored');
});
})([ ee, new EventEmitter, new EventEmitter ], step());

ee.emit('end', 'ended');

ee = new EventEmitter();

cadence(function (step, ee) {
step(function () {
var on = step('on', ee);
on('data', []);
on('end');
ee.on('data', step.event([]));
ee.on('end', step.event());
}, function (data, ended) {
deepEqual(data, [], 'arrayed event with no values');
equal(ended, 'ended', 'arrayed event with no values ended');
Expand All @@ -74,25 +61,17 @@ require('proof')(9, function (equal, ok, step, deepEqual) {

ee.emit('end', 'ended');

ee = new EventEmitter();

cadence(function (step, ee) {
step(function () {
var on = step('on', ee);
on('data', [], 2);
on('end');
ee.on('data', step.event(2, []));
ee.on('end', step.event());
}, function (first, second, ended) {
deepEqual(second, [], 'arrayed event with specific arity');
equal(ended, 'ended', 'arrayed event with specific arity ended');
});
})(ee, step());

ee.emit('end', 'ended');

cadence(function (step) {
var on = step('on');
try {
on(1);
} catch (e) {
equal(e.message, 'event name required', 'name required');
}
})(step());
});

0 comments on commit c66611e

Please sign in to comment.