Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Now each next() and done() only executes its logic once, no matter ho…

…w many times it may be called within the same pre or post.
  • Loading branch information...
commit f71806e2770e4033f6fe33f31efcf6116c8fbf3c 1 parent 88d8395
@bnoguchi authored
Showing with 60 additions and 26 deletions.
  1. +11 −26 hooks.js
  2. +49 −0 test.js
View
37 hooks.js
@@ -1,26 +1,3 @@
-/**
- * Hooks are useful if we want to add a method that automatically has `pre` and `post` hooks.
- * For example, it would be convenient to have `pre` and `post` hooks for `save`.
- * _.extend(Model, mixins.hooks);
- * Model.hook('save', function () {
- * console.log('saving');
- * });
- * Model.pre('save', function (next, done) {
- * console.log('about to save');
- * next();
- * });
- * Model.post('save', function (next, done) {
- * console.log('saved');
- * next();
- * });
- *
- * var m = new Model();
- * m.save();
- * // about to save
- * // saving
- * // saved
- */
-
// TODO Add in pre and post skipping options
module.exports = {
/**
@@ -67,8 +44,8 @@ module.exports = {
if (currPre.length < 1)
throw new Error("Your pre must have a next argument -- e.g., function (next, ...)");
preArgs = (currPre.isAsync
- ? [_next, _asyncsDone]
- : [_next]).concat(hookArgs);
+ ? [once(_next), once(_asyncsDone)]
+ : [once(_next)]).concat(hookArgs);
return currPre.apply(self, preArgs);
} else if (!proto[name].numAsyncPres) {
return _done.apply(self, hookArgs);
@@ -93,7 +70,7 @@ module.exports = {
currPost = posts[current_]
if (currPost.length < 1)
throw new Error("Your post must have a next argument -- e.g., function (next, ...)");
- postArgs = [next_].concat(hookArgs);
+ postArgs = [once(next_)].concat(hookArgs);
return currPost.apply(self, postArgs);
}
};
@@ -164,3 +141,11 @@ module.exports = {
}
}
};
+
+function once (fn, scope) {
+ return function fnWrapper () {
+ if (fnWrapper.hookCalled) return;
+ fn.apply(scope, arguments);
+ fnWrapper.hookCalled = true;
+ };
+}
View
49 test.js
@@ -4,6 +4,7 @@ var hooks = require('./hooks')
, _ = require('underscore');
// TODO Add in test for making sure all pres get called if pre is defined directly on an instance.
+// TODO Test for calling `done` twice or `next` twice in the same function counts only once
module.exports = {
'should be able to assign multiple hooks at once': function () {
var A = function () {};
@@ -406,6 +407,54 @@ module.exports = {
a.set('foo', 'bar');
},
+ 'calling the same done multiple times should have the effect of only calling it once': function () {
+ var A = function () {
+ this.acked = false;
+ };
+ _.extend(A, hooks);
+ A.hook('ack', function () {
+ console.log("UH OH, YOU SHOULD NOT BE SEEING THIS");
+ this.acked = true;
+ });
+ A.pre('ack', function (next, done) {
+ next();
+ done();
+ done();
+ }, true);
+ A.pre('ack', function (next, done) {
+ next();
+ // Notice that done() is not invoked here
+ }, true);
+ var a = new A();
+ a.ack();
+ setTimeout( function () {
+ a.acked.should.be.false;
+ }, 1000);
+ },
+
+ 'calling the same next multiple times should have the effect of only calling it once': function () {
+ var A = function () {
+ this.acked = false;
+ };
+ _.extend(A, hooks);
+ A.hook('ack', function () {
+ console.log("UH OH, YOU SHOULD NOT BE SEEING THIS");
+ this.acked = true;
+ });
+ A.pre('ack', function (next) {
+ next();
+ next();
+ });
+ A.pre('ack', function (next) {
+ // Notice that next() is not invoked here
+ });
+ var a = new A();
+ a.ack();
+ setTimeout( function () {
+ a.acked.should.be.false;
+ }, 1000);
+ },
+
'asynchronous middleware should be able to pass an error via `done`, stopping the middleware chain': function () {
var counter = 0;
var A = function () {};
Please sign in to comment.
Something went wrong with that request. Please try again.