diff --git a/.travis.yml b/.travis.yml index d398fba..075f27a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ install: "npm install" before_script: "./node_modules/.bin/cake test-prepare" script: "./node_modules/.bin/cake test" node_js: - - 0.4 - - 0.6 - - 0.8 - - 0.10 + - "0.4" + - "0.6" + - "0.8" + - "0.10" notifications: irc: - "irc.freenode.org#bevry-dev" diff --git a/History.md b/History.md index 3999e39..e13ecac 100755 --- a/History.md +++ b/History.md @@ -1,5 +1,15 @@ ## History +- v1.16.13 March 23, 2013 + - `balUtilEvents` changes: + - `EventEmitterEnhanced` changes: + - Now works with `once` calls in node 0.10.0 + - Closes [bevry/docpad#462](https://github.com/bevry/docpad/issues/462) + - Changed `emitSync` to be an alias to `emitSerial` and `emitAsync` to be an alias to `emitParallel` + - Added new `getListenerGroup` function + - `balUtilFlow` changes: + - `fireWithOptionalCallback` can now take the method as an array of `[fireMethod,introspectMethod]` useful for pesly binds + - v1.16.12 March 18, 2013 - `balUtilFlow` changes: - `Groups::run` signature changed from no arguments to a single `mode` argument diff --git a/out/lib/events.js b/out/lib/events.js index 85a52c3..13f1b81 100644 --- a/out/lib/events.js +++ b/out/lib/events.js @@ -21,38 +21,49 @@ return _ref; } - EventEmitterEnhanced.prototype.emitAsync = function(eventName, data, next) { - var listener, listeners, tasks, _i, _len; + EventEmitterEnhanced.prototype.getListenerGroup = function(eventName, data, next) { + var listeners, me, tasks; + me = this; listeners = this.listeners(eventName); tasks = new balUtilFlow.Group(next); - for (_i = 0, _len = listeners.length; _i < _len; _i++) { - listener = listeners[_i]; - tasks.push({ - listener: listener - }, function(complete) { - return balUtilFlow.fireWithOptionalCallback(this.listener, [data, complete]); + balUtilFlow.each(listeners, function(listener) { + if (listener.listener) { + listener = [listener, listener.listener]; + } + return tasks.push(function(complete) { + return balUtilFlow.fireWithOptionalCallback(listener, [data, complete], me); }); - } - tasks.async(); - return this; + }); + return tasks; }; - EventEmitterEnhanced.prototype.emitSync = function(eventName, data, next) { - var listener, listeners, tasks, _i, _len; + EventEmitterEnhanced.prototype.emitSync = function() { + var args; - listeners = this.listeners(eventName); - tasks = new balUtilFlow.Group(next); - for (_i = 0, _len = listeners.length; _i < _len; _i++) { - listener = listeners[_i]; - tasks.push({ - listener: listener - }, function(complete) { - return balUtilFlow.fireWithOptionalCallback(this.listener, [data, complete]); - }); - } - tasks.sync(); - return this; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.emitSerial.apply(this, args); + }; + + EventEmitterEnhanced.prototype.emitSerial = function() { + var args; + + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.getListenerGroup.apply(this, args).run('serial'); + }; + + EventEmitterEnhanced.prototype.emitAsync = function() { + var args; + + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.emitParallel.apply(this, args); + }; + + EventEmitterEnhanced.prototype.emitParallel = function() { + var args; + + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.getListenerGroup.apply(this, args).run('parallel'); }; return EventEmitterEnhanced; diff --git a/out/lib/flow.js b/out/lib/flow.js index 6cc4f32..b03b7ac 100644 --- a/out/lib/flow.js +++ b/out/lib/flow.js @@ -22,15 +22,20 @@ return [opts, next]; }, fireWithOptionalCallback: function(method, args, context) { - var callback, caughtError, err, result; + var callback, caughtError, err, fireMethod, introspectMethod, result; args || (args = []); callback = args[args.length - 1]; context || (context = null); result = null; - if (method.length === args.length) { + if (balUtilTypes.isArray(method)) { + fireMethod = method[0], introspectMethod = method[1]; + } else { + fireMethod = introspectMethod = method; + } + if (introspectMethod.length === args.length) { try { - result = method.apply(context, args); + result = fireMethod.apply(context, args); } catch (_error) { caughtError = _error; callback(caughtError); @@ -38,7 +43,7 @@ } else { err = null; try { - result = method.apply(context, args); + result = fireMethod.apply(context, args); if (balUtilTypes.isError(result)) { err = result; } diff --git a/out/test/events.test.js b/out/test/events.test.js index 42399ea..0ebde3e 100644 --- a/out/test/events.test.js +++ b/out/test/events.test.js @@ -1,23 +1,102 @@ // Generated by CoffeeScript 1.6.2 (function() { - var EventSystem, Person, assert, debug, joe, _ref, + var EventEmitterEnhanced, EventSystem, Person, assert, debug, expect, joe, _ref, _ref1, _ref2, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - assert = require('assert'); + _ref = require('chai'), expect = _ref.expect, assert = _ref.assert; joe = require('joe'); - EventSystem = require(__dirname + '/../lib/balutil').EventSystem; + _ref1 = require(__dirname + '/../lib/balutil'), EventEmitterEnhanced = _ref1.EventEmitterEnhanced, EventSystem = _ref1.EventSystem; debug = false; + joe.describe('EventEmitterEnhanced', function(describe, it) { + var eventEmitter; + + eventEmitter = null; + it('should construct', function() { + return eventEmitter = new EventEmitterEnhanced(); + }); + it('should work in serial', function(done) { + var first, second; + + first = second = 0; + eventEmitter.on('serial', function(opts, next) { + first++; + return setTimeout(function() { + expect(second).to.eql(0); + first++; + return next(); + }, 500); + }); + eventEmitter.on('serial', function() { + expect(first).to.eql(2); + return second += 2; + }); + expect(eventEmitter.listeners('serial').length).to.eql(2); + return eventEmitter.emitSerial('serial', null, function(err) { + expect(first).to.eql(2); + expect(second).to.eql(2); + return done(); + }); + }); + it('should work in parallel', function(done) { + var first, second; + + first = second = 0; + eventEmitter.on('parallel', function(opts, next) { + first++; + return setTimeout(function() { + expect(second).to.eql(2); + first++; + return next(); + }, 500); + }); + eventEmitter.on('parallel', function() { + expect(first).to.eql(1); + return second += 2; + }); + expect(eventEmitter.listeners('parallel').length).to.eql(2); + return eventEmitter.emitParallel('parallel', null, function(err) { + expect(first).to.eql(2); + expect(second).to.eql(2); + return done(); + }); + }); + return it('should work with once', function(done) { + var first, second; + + first = second = 0; + eventEmitter.once('once', function(opts, next) { + first++; + return setTimeout(function() { + expect(second).to.eql(2); + first++; + return next(); + }, 500); + }); + eventEmitter.once('once', function() { + expect(first).to.eql(1); + return second += 2; + }); + expect(eventEmitter.listeners('once').length).to.eql(2); + return eventEmitter.emitParallel('once', null, function(err) { + expect(first).to.eql(2); + expect(second).to.eql(2); + expect(eventEmitter.listeners('once').length).to.eql(0); + return done(); + }); + }); + }); + Person = (function(_super) { __extends(Person, _super); function Person() { - _ref = Person.__super__.constructor.apply(this, arguments); - return _ref; + _ref2 = Person.__super__.constructor.apply(this, arguments); + return _ref2; } /* diff --git a/package.json b/package.json index 49170fd..5d3e88b 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bal-util", - "version": "1.16.12", + "version": "1.16.13", "description": "Common utility functions for Node.js used and maintained by Benjamin Lupton", "homepage": "https://github.com/balupton/bal-util", "keywords": [ @@ -45,7 +45,8 @@ "dependencies": {}, "devDependencies": { "coffee-script": "~1.6.2", - "joe": "~1.1.1" + "joe": "~1.1.1", + "chai": "~1.5.0" }, "directories": { "lib": "./out/lib" diff --git a/src/lib/events.coffee b/src/lib/events.coffee index 1d4d8c7..99b0304 100755 --- a/src/lib/events.coffee +++ b/src/lib/events.coffee @@ -12,39 +12,39 @@ debug = false class EventEmitterEnhanced extends EventEmitter - # Emit a group of listeners asynchronously - # next(err,result,results) - emitAsync: (eventName,data,next) -> + # Get Listener Group + # Fetch the listeners for a particular event as a task group + getListenerGroup: (eventName,data,next) -> # Get listeners + me = @ listeners = @listeners(eventName) - # Prepare tasks - tasks = new balUtilFlow.Group(next) - # Add the tasks for the listeners - for listener in listeners - tasks.push {listener}, (complete) -> - # Fire the listener, treating the callback as optional - balUtilFlow.fireWithOptionalCallback(@listener,[data,complete]) - # Trigger asynchronously - tasks.async() - # Chain - @ - # Emit a group of listeners synchronously - # next(err,result,results) - emitSync: (eventName,data,next) -> - # Get listeners - listeners = @listeners(eventName) # Prepare tasks tasks = new balUtilFlow.Group(next) + # Add the tasks for the listeners - for listener in listeners - tasks.push {listener}, (complete) -> + balUtilFlow.each listeners, (listener) -> + # Once will actually wrap around the original listener, which isn't what we want for the introspection + # So we must pass fireWithOptionalCallback an array of the method to fire, and the method to introspect + # https://github.com/bevry/docpad/issues/462 + # https://github.com/joyent/node/commit/d1b4dcd6acb1d1c66e423f7992dc6eec8a35c544 + listener = [listener,listener.listener] if listener.listener + + # Bind to the task + tasks.push (complete) -> # Fire the listener, treating the callback as optional - balUtilFlow.fireWithOptionalCallback(@listener,[data,complete]) - # Trigger synchronously - tasks.sync() - # Chain - @ + balUtilFlow.fireWithOptionalCallback(listener,[data,complete],me) + + # Return + return tasks + + # Emit Serial + emitSync: (args...) -> @emitSerial(args...) + emitSerial: (args...) -> @getListenerGroup(args...).run('serial') + + # Emit Parallel + emitAsync: (args...) -> @emitParallel(args...) + emitParallel: (args...) -> @getListenerGroup(args...).run('parallel') # ===================================== diff --git a/src/lib/flow.coffee b/src/lib/flow.coffee index 8ab1913..f022181 100755 --- a/src/lib/flow.coffee +++ b/src/lib/flow.coffee @@ -36,13 +36,17 @@ balUtilFlow = callback = args[args.length-1] context or= null result = null + if balUtilTypes.isArray(method) + [fireMethod,introspectMethod] = method + else + fireMethod = introspectMethod = method # We have the callback # assume it is async - if method.length is args.length + if introspectMethod.length is args.length # Fire the function try - result = method.apply(context,args) + result = fireMethod.apply(context,args) catch caughtError callback(caughtError) @@ -54,7 +58,7 @@ balUtilFlow = # Fire the function try - result = method.apply(context,args) + result = fireMethod.apply(context,args) err = result if balUtilTypes.isError(result) catch caughtError err = caughtError diff --git a/src/test/events.test.coffee b/src/test/events.test.coffee index 535beaf..e6d8405 100755 --- a/src/test/events.test.coffee +++ b/src/test/events.test.coffee @@ -1,12 +1,115 @@ # Requires -assert = require('assert') +{expect,assert} = require('chai') joe = require('joe') -EventSystem = require(__dirname+'/../lib/balutil').EventSystem +{EventEmitterEnhanced,EventSystem} = require(__dirname+'/../lib/balutil') debug = false # ===================================== -# Configuration +# Event Emitter Enhanced + +joe.describe 'EventEmitterEnhanced', (describe,it) -> + eventEmitter = null + + it 'should construct', -> + eventEmitter = new EventEmitterEnhanced() + + # Serial + it 'should work in serial', (done) -> + # Prepare + first = second = 0 + + # Asynchronous + eventEmitter.on 'serial', (opts,next) -> + first++ + setTimeout( + -> + expect(second).to.eql(0) + first++ + next() + 500 + ) + + # Synchronous + eventEmitter.on 'serial', -> + expect(first).to.eql(2) + second += 2 + + # Correct amount of listeners? + expect(eventEmitter.listeners('serial').length).to.eql(2) + + # Emit and check + eventEmitter.emitSerial 'serial', null, (err) -> + expect(first).to.eql(2) + expect(second).to.eql(2) + done() + + # Parallel + it 'should work in parallel', (done) -> + # Prepare + first = second = 0 + + # Asynchronous + eventEmitter.on 'parallel', (opts,next) -> + first++ + setTimeout( + -> + expect(second).to.eql(2) + first++ + next() + 500 + ) + + # Synchronous + eventEmitter.on 'parallel', -> + expect(first).to.eql(1) + second += 2 + + # Correct amount of listeners? + expect(eventEmitter.listeners('parallel').length).to.eql(2) + + # Emit and check + eventEmitter.emitParallel 'parallel', null, (err) -> + expect(first).to.eql(2) + expect(second).to.eql(2) + done() + + # Parallel + it 'should work with once', (done) -> + # Prepare + first = second = 0 + + # Asynchronous + eventEmitter.once 'once', (opts,next) -> + first++ + setTimeout( + -> + expect(second).to.eql(2) + first++ + next() + 500 + ) + + # Synchronous + eventEmitter.once 'once', -> + expect(first).to.eql(1) + second += 2 + + # Correct amount of listeners? + expect(eventEmitter.listeners('once').length).to.eql(2) + + # Emit and check + eventEmitter.emitParallel 'once', null, (err) -> + expect(first).to.eql(2) + expect(second).to.eql(2) + expect(eventEmitter.listeners('once').length).to.eql(0) + done() + + + + +# ===================================== +# Event System class Person extends EventSystem ### @@ -85,7 +188,7 @@ class Person extends EventSystem ,1*1000) -# ===================================== +# ------------------------------------- # Tests joe.describe 'EventSystem', (describe,it) ->