1
0
mirror of https://github.com/mgerb/mywebsite synced 2026-01-11 18:32:50 +00:00

updated bunch of file paths and changed the way posts are loaded

This commit is contained in:
2016-01-05 12:28:04 -06:00
parent 4bb8cae81e
commit 6ab45fe935
13249 changed files with 317868 additions and 2101398 deletions

2
node_modules/hooks-fixed/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
**.swp
node_modules

9
node_modules/hooks-fixed/Makefile generated vendored Normal file
View File

@@ -0,0 +1,9 @@
test:
@NODE_ENV=test ./node_modules/expresso/bin/expresso \
$(TESTFLAGS) \
./test.js
test-cov:
@TESTFLAGS=--cov $(MAKE) test
.PHONY: test test-cov

369
node_modules/hooks-fixed/README.md generated vendored Normal file
View File

@@ -0,0 +1,369 @@
hooks
============
Add pre and post middleware hooks to your JavaScript methods.
## Installation
npm install hooks
## Motivation
Suppose you have a JavaScript object with a `save` method.
It would be nice to be able to declare code that runs before `save` and after `save`.
For example, you might want to run validation code before every `save`,
and you might want to dispatch a job to a background job queue after `save`.
One might have an urge to hard code this all into `save`, but that turns out to
couple all these pieces of functionality (validation, save, and job creation) more
tightly than is necessary. For example, what if someone does not want to do background
job creation after the logical save?
It is nicer to tack on functionality using what we call `pre` and `post` hooks. These
are functions that you define and that you direct to execute before or after particular
methods.
## Example
We can use `hooks` to add validation and background jobs in the following way:
```javascript
var hooks = require('hooks')
, Document = require('./path/to/some/document/constructor');
// Add hooks' methods: `hook`, `pre`, and `post`
for (var k in hooks) {
Document[k] = hooks[k];
}
// Define a new method that is able to invoke pre and post middleware
Document.hook('save', Document.prototype.save);
// Define a middleware function to be invoked before 'save'
Document.pre('save', function validate (next) {
// The `this` context inside of `pre` and `post` functions
// is the Document instance
if (this.isValid()) next(); // next() passes control to the next middleware
// or to the target method itself
else next(new Error("Invalid")); // next(error) invokes an error callback
});
// Define a middleware function to be invoked after 'save'
Document.post('save', function createJob (next) {
this.sendToBackgroundQueue();
next();
});
```
If you already have defined `Document.prototype` methods for which you want pres and posts,
then you do not need to explicitly invoke `Document.hook(...)`. Invoking `Document.pre(methodName, fn)`
or `Document.post(methodName, fn)` will automatically and lazily change `Document.prototype[methodName]`
so that it plays well with `hooks`. An equivalent way to implement the previous example is:
```javascript
var hooks = require('hooks')
, Document = require('./path/to/some/document/constructor');
// Add hooks' methods: `hook`, `pre`, and `post`
for (var k in hooks) {
Document[k] = hooks[k];
}
Document.prototype.save = function () {
// ...
};
// Define a middleware function to be invoked before 'save'
Document.pre('save', function validate (next) {
// The `this` context inside of `pre` and `post` functions
// is the Document instance
if (this.isValid()) next(); // next() passes control to the next middleware
// or to the target method itself
else next(new Error("Invalid")); // next(error) invokes an error callback
});
// Define a middleware function to be invoked after 'save'
Document.post('save', function createJob (next) {
this.sendToBackgroundQueue();
next();
});
```
## Pres and Posts as Middleware
We structure pres and posts as middleware to give you maximum flexibility:
1. You can define **multiple** pres (or posts) for a single method.
2. These pres (or posts) are then executed as a chain of methods.
3. Any functions in this middleware chain can choose to halt the chain's execution by `next`ing an Error from that middleware function. If this occurs, then none of the other middleware in the chain will execute, and the main method (e.g., `save`) will not execute. This is nice, for example, when we don't want a document to save if it is invalid.
## Defining multiple pres (or posts)
`pre` and `post` are chainable, so you can define multiple via:
```javascript
Document.pre('save', function (next) {
console.log("hello");
next();
}).pre('save', function (next) {
console.log("world");
next();
});
Document.post('save', function (next) {
console.log("hello");
next();
}).post('save', function (next) {
console.log("world");
next();
});
```
As soon as one pre finishes executing, the next one will be invoked, and so on.
## Error Handling
You can define a default error handler by passing a 2nd function as the 3rd argument to `hook`:
```javascript
Document.hook('set', function (path, val) {
this[path] = val;
}, function (err) {
// Handler the error here
console.error(err);
});
```
Then, we can pass errors to this handler from a pre or post middleware function:
```javascript
Document.pre('set', function (next, path, val) {
next(new Error());
});
```
If you do not set up a default handler, then `hooks` makes the default handler that just throws the `Error`.
The default error handler can be over-rided on a per method invocation basis.
If the main method that you are surrounding with pre and post middleware expects its last argument to be a function
with callback signature `function (error, ...)`, then that callback becomes the error handler, over-riding the default
error handler you may have set up.
```javascript
Document.hook('save', function (callback) {
// Save logic goes here
...
});
var doc = new Document();
doc.save( function (err, saved) {
// We can pass err via `next` in any of our pre or post middleware functions
if (err) console.error(err);
// Rest of callback logic follows ...
});
```
## Mutating Arguments via Middleware
`pre` and `post` middleware can also accept the intended arguments for the method
they augment. This is useful if you want to mutate the arguments before passing
them along to the next middleware and eventually pass a mutated arguments list to
the main method itself.
As a simple example, let's define a method `set` that just sets a key, value pair.
If we want to namespace the key, we can do so by adding a `pre` middleware hook
that runs before `set`, alters the arguments by namespacing the `key` argument, and passes them onto `set`:
```javascript
Document.hook('set', function (key, val) {
this[key] = val;
});
Document.pre('set', function (next, key, val) {
next('namespace-' + key, val);
});
var doc = new Document();
doc.set('hello', 'world');
console.log(doc.hello); // undefined
console.log(doc['namespace-hello']); // 'world'
```
As you can see above, we pass arguments via `next`.
If you are not mutating the arguments, then you can pass zero arguments
to `next`, and the next middleware function will still have access
to the arguments.
```javascript
Document.hook('set', function (key, val) {
this[key] = val;
});
Document.pre('set', function (next, key, val) {
// I have access to key and val here
next(); // We don't need to pass anything to next
});
Document.pre('set', function (next, key, val) {
// And I still have access to the original key and val here
next();
});
```
Finally, you can add arguments that downstream middleware can also see:
```javascript
// Note that in the definition of `set`, there is no 3rd argument, options
Document.hook('set', function (key, val) {
// But...
var options = arguments[2]; // ...I have access to an options argument
// because of pre function pre2 (defined below)
console.log(options); // '{debug: true}'
this[key] = val;
});
Document.pre('set', function pre1 (next, key, val) {
// I only have access to key and val arguments
console.log(arguments.length); // 3
next(key, val, {debug: true});
});
Document.pre('set', function pre2 (next, key, val, options) {
console.log(arguments.length); // 4
console.log(options); // '{ debug: true}'
next();
});
Document.pre('set', function pre3 (next, key, val, options) {
// I still have access to key, val, AND the options argument introduced via the preceding middleware
console.log(arguments.length); // 4
console.log(options); // '{ debug: true}'
next();
});
var doc = new Document()
doc.set('hey', 'there');
```
## Post middleware
Post middleware intercepts the callback originally sent to the asynchronous function you have hooked to.
This means that the following chain of execution will occur in a typical `save` operation:
(1) doc.save -> (2) pre --(next)--> (3) save calls back -> (4) post --(next)--> (5) targetFn
Illustrated below:
```
Document.pre('save', function (next) {
this.key = "value";
next();
});
// Post handler occurs before `set` calls back. This is useful if we need to grab something
// async before `set` finishes.
Document.post('set', function (next) {
var me = this;
getSomethingAsync(function(value){ // let's assume it returns "Hello Async"
me.key2 = value;
next();
});
});
var doc = new Document();
doc.save(function(err){
console.log(this.key); // "value" - this value was saved
console.log(this.key2); // "Hello Async" - this value was *not* saved
}
```
Post middleware must call `next()` or execution will stop.
## Parallel `pre` middleware
All middleware up to this point has been "serial" middleware -- i.e., middleware whose logic
is executed as a serial chain.
Some scenarios call for parallel middleware -- i.e., middleware that can wait for several
asynchronous services at once to respond.
For instance, you may only want to save a Document only after you have checked
that the Document is valid according to two different remote services.
We accomplish asynchronous middleware by adding a second kind of flow control callback
(the only flow control callback so far has been `next`), called `done`.
- `next` passes control to the next middleware in the chain
- `done` keeps track of how many parallel middleware have invoked `done` and passes
control to the target method when ALL parallel middleware have invoked `done`. If
you pass an `Error` to `done`, then the error is handled, and the main method that is
wrapped by pres and posts will not get invoked.
We declare pre middleware that is parallel by passing a 3rd boolean argument to our `pre`
definition method.
We illustrate via the parallel validation example mentioned above:
```javascript
Document.hook('save', function targetFn (callback) {
// Save logic goes here
// ...
// This only gets run once the two `done`s are both invoked via preOne and preTwo.
});
// true marks this as parallel middleware
Document.pre('save', true, function preOne (next, doneOne, callback) {
remoteServiceOne.validate(this.serialize(), function (err, isValid) {
// The code in here will probably be run after the `next` below this block
// and could possibly be run after the console.log("Hola") in `preTwo
if (err) return doneOne(err);
if (isValid) doneOne();
});
next(); // Pass control to the next middleware
});
// We will suppose that we need 2 different remote services to validate our document
Document.pre('save', true, function preTwo (next, doneTwo, callback) {
remoteServiceTwo.validate(this.serialize(), function (err, isValid) {
if (err) return doneTwo(err);
if (isValid) doneTwo();
});
next();
});
// While preOne and preTwo are parallel, preThree is a serial pre middleware
Document.pre('save', function preThree (next, callback) {
next();
});
var doc = new Document();
doc.save( function (err, doc) {
// Do stuff with the saved doc here...
});
```
In the above example, flow control may happen in the following way:
(1) doc.save -> (2) preOne --(next)--> (3) preTwo --(next)--> (4) preThree --(next)--> (wait for dones to invoke) -> (5) doneTwo -> (6) doneOne -> (7) targetFn
So what's happening is that:
1. You call `doc.save(...)`
2. First, your preOne middleware gets executed. It makes a remote call to the validation service and `next()`s to the preTwo middleware.
3. Now, your preTwo middleware gets executed. It makes a remote call to another validation service and `next()`s to the preThree middleware.
4. Your preThree middleware gets executed. It immediately `next()`s. But nothing else gets executing until both `doneOne` and `doneTwo` are invoked inside the callbacks handling the response from the two valiation services.
5. We will suppose that validation remoteServiceTwo returns a response to us first. In this case, we call `doneTwo` inside the callback to remoteServiceTwo.
6. Some fractions of a second later, remoteServiceOne returns a response to us. In this case, we call `doneOne` inside the callback to remoteServiceOne.
7. `hooks` implementation keeps track of how many parallel middleware has been defined per target function. It detects that both asynchronous pre middlewares (`preOne` and `preTwo`) have finally called their `done` functions (`doneOne` and `doneTwo`), so the implementation finally invokes our `targetFn` (i.e., our core `save` business logic).
## Removing Pres
You can remove a particular pre associated with a hook:
Document.pre('set', someFn);
Document.removePre('set', someFn);
And you can also remove all pres associated with a hook:
Document.removePre('set'); // Removes all declared `pre`s on the hook 'set'
## Tests
To run the tests:
make test
### Contributors
- [Brian Noguchi](https://github.com/bnoguchi)
### License
MIT License
---
### Author
Brian Noguchi

134
node_modules/hooks-fixed/hooks.alt.js generated vendored Normal file
View File

@@ -0,0 +1,134 @@
/**
* 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 = {
/**
* Declares a new hook to which you can add pres and posts
* @param {String} name of the function
* @param {Function} the method
* @param {Function} the error handler callback
*/
hook: function (name, fn, err) {
if (arguments.length === 1 && typeof name === 'object') {
for (var k in name) { // `name` is a hash of hookName->hookFn
this.hook(k, name[k]);
}
return;
}
if (!err) err = fn;
var proto = this.prototype || this
, pres = proto._pres = proto._pres || {}
, posts = proto._posts = proto._posts || {};
pres[name] = pres[name] || [];
posts[name] = posts[name] || [];
function noop () {}
proto[name] = function () {
var self = this
, pres = this._pres[name]
, posts = this._posts[name]
, numAsyncPres = 0
, hookArgs = [].slice.call(arguments)
, preChain = pres.map( function (pre, i) {
var wrapper = function () {
if (arguments[0] instanceof Error)
return err(arguments[0]);
if (numAsyncPres) {
// arguments[1] === asyncComplete
if (arguments.length)
hookArgs = [].slice.call(arguments, 2);
pre.apply(self,
[ preChain[i+1] || allPresInvoked,
asyncComplete
].concat(hookArgs)
);
} else {
if (arguments.length)
hookArgs = [].slice.call(arguments);
pre.apply(self,
[ preChain[i+1] || allPresDone ].concat(hookArgs));
}
}; // end wrapper = function () {...
if (wrapper.isAsync = pre.isAsync)
numAsyncPres++;
return wrapper;
}); // end posts.map(...)
function allPresInvoked () {
if (arguments[0] instanceof Error)
err(arguments[0]);
}
function allPresDone () {
if (arguments[0] instanceof Error)
return err(arguments[0]);
if (arguments.length)
hookArgs = [].slice.call(arguments);
fn.apply(self, hookArgs);
var postChain = posts.map( function (post, i) {
var wrapper = function () {
if (arguments[0] instanceof Error)
return err(arguments[0]);
if (arguments.length)
hookArgs = [].slice.call(arguments);
post.apply(self,
[ postChain[i+1] || noop].concat(hookArgs));
}; // end wrapper = function () {...
return wrapper;
}); // end posts.map(...)
if (postChain.length) postChain[0]();
}
if (numAsyncPres) {
complete = numAsyncPres;
function asyncComplete () {
if (arguments[0] instanceof Error)
return err(arguments[0]);
--complete || allPresDone.call(this);
}
}
(preChain[0] || allPresDone)();
};
return this;
},
pre: function (name, fn, isAsync) {
var proto = this.prototype
, pres = proto._pres = proto._pres || {};
if (fn.isAsync = isAsync) {
this.prototype[name].numAsyncPres++;
}
(pres[name] = pres[name] || []).push(fn);
return this;
},
post: function (name, fn, isAsync) {
var proto = this.prototype
, posts = proto._posts = proto._posts || {};
(posts[name] = posts[name] || []).push(fn);
return this;
}
};

191
node_modules/hooks-fixed/hooks.js generated vendored Normal file
View File

@@ -0,0 +1,191 @@
// TODO Add in pre and post skipping options
module.exports = {
/**
* Declares a new hook to which you can add pres and posts
* @param {String} name of the function
* @param {Function} the method
* @param {Function} the error handler callback
*/
hook: function (name, fn, errorCb) {
if (arguments.length === 1 && typeof name === 'object') {
for (var k in name) { // `name` is a hash of hookName->hookFn
this.hook(k, name[k]);
}
return;
}
var proto = this.prototype || this
, pres = proto._pres = proto._pres || {}
, posts = proto._posts = proto._posts || {};
pres[name] = pres[name] || [];
posts[name] = posts[name] || [];
proto[name] = function () {
var self = this
, hookArgs // arguments eventually passed to the hook - are mutable
, lastArg = arguments[arguments.length-1]
, pres = this._pres[name]
, posts = this._posts[name]
, _total = pres.length
, _current = -1
, _asyncsLeft = proto[name].numAsyncPres
, _asyncsDone = function(err) {
if (err) {
return handleError(err);
}
--_asyncsLeft || _done.apply(self, hookArgs);
}
, handleError = function(err) {
if ('function' == typeof lastArg)
return lastArg(err);
if (errorCb) return errorCb.call(self, err);
throw err;
}
, _next = function () {
if (arguments[0] instanceof Error) {
return handleError(arguments[0]);
}
var _args = Array.prototype.slice.call(arguments)
, currPre
, preArgs;
if (_args.length && !(arguments[0] == null && typeof lastArg === 'function'))
hookArgs = _args;
if (++_current < _total) {
currPre = pres[_current]
if (currPre.isAsync && currPre.length < 2)
throw new Error("Your pre must have next and done arguments -- e.g., function (next, done, ...)");
if (currPre.length < 1)
throw new Error("Your pre must have a next argument -- e.g., function (next, ...)");
preArgs = (currPre.isAsync
? [once(_next), once(_asyncsDone)]
: [once(_next)]).concat(hookArgs);
return currPre.apply(self, preArgs);
} else if (!_asyncsLeft) {
return _done.apply(self, hookArgs);
}
}
, _done = function () {
var args_ = Array.prototype.slice.call(arguments)
, ret, total_, current_, next_, done_, postArgs;
if (_current === _total) {
next_ = function () {
if (arguments[0] instanceof Error) {
return handleError(arguments[0]);
}
var args_ = Array.prototype.slice.call(arguments, 1)
, currPost
, postArgs;
if (args_.length) hookArgs = args_;
if (++current_ < total_) {
currPost = posts[current_]
if (currPost.length < 1)
throw new Error("Your post must have a next argument -- e.g., function (next, ...)");
postArgs = [once(next_)].concat(hookArgs);
return currPost.apply(self, postArgs);
} else if (typeof lastArg === 'function'){
// All post handlers are done, call original callback function
return lastArg.apply(self, arguments);
}
};
// We are assuming that if the last argument provided to the wrapped function is a function, it was expecting
// a callback. We trap that callback and wait to call it until all post handlers have finished.
if(typeof lastArg === 'function'){
args_[args_.length - 1] = once(next_);
}
total_ = posts.length;
current_ = -1;
ret = fn.apply(self, args_); // Execute wrapped function, post handlers come afterward
if (total_ && typeof lastArg !== 'function') return next_(); // no callback provided, execute next_() manually
return ret;
}
};
return _next.apply(this, arguments);
};
proto[name].numAsyncPres = 0;
return this;
},
pre: function (name, isAsync, fn, errorCb) {
if ('boolean' !== typeof arguments[1]) {
errorCb = fn;
fn = isAsync;
isAsync = false;
}
var proto = this.prototype || this
, pres = proto._pres = proto._pres || {};
this._lazySetupHooks(proto, name, errorCb);
if (fn.isAsync = isAsync) {
proto[name].numAsyncPres++;
}
(pres[name] = pres[name] || []).push(fn);
return this;
},
post: function (name, isAsync, fn) {
if (arguments.length === 2) {
fn = isAsync;
isAsync = false;
}
var proto = this.prototype || this
, posts = proto._posts = proto._posts || {};
this._lazySetupHooks(proto, name);
(posts[name] = posts[name] || []).push(fn);
return this;
},
removePre: function (name, fnToRemove) {
var proto = this.prototype || this
, pres = proto._pres || (proto._pres || {});
if (!pres[name]) return this;
if (arguments.length === 1) {
// Remove all pre callbacks for hook `name`
pres[name].length = 0;
} else {
pres[name] = pres[name].filter( function (currFn) {
return currFn !== fnToRemove;
});
}
return this;
},
removePost: function (name, fnToRemove) {
var proto = this.prototype || this
, posts = proto._posts || (proto._posts || {});
if (!posts[name]) return this;
if (arguments.length === 1) {
// Remove all post callbacks for hook `name`
posts[name].length = 0;
} else {
posts[name] = posts[name].filter( function (currFn) {
return currFn !== fnToRemove;
});
}
return this;
},
_lazySetupHooks: function (proto, methodName, errorCb) {
if ('undefined' === typeof proto[methodName].numAsyncPres) {
this.hook(methodName, proto[methodName], errorCb);
}
}
};
function once (fn, scope) {
return function fnWrapper () {
if (fnWrapper.hookCalled) return;
fnWrapper.hookCalled = true;
var ret = fn.apply(scope, arguments);
if (ret && ret.then) {
ret.then(function() {}, function() {});
}
};
}

91
node_modules/hooks-fixed/package.json generated vendored Normal file
View File

@@ -0,0 +1,91 @@
{
"_args": [
[
"hooks-fixed@1.1.0",
"/home/mitchell/Desktop/test-mywebsite/mywebsite/node_modules/mongoose"
]
],
"_from": "hooks-fixed@1.1.0",
"_id": "hooks-fixed@1.1.0",
"_inCache": true,
"_installable": true,
"_location": "/hooks-fixed",
"_nodeVersion": "0.12.3",
"_npmUser": {
"email": "val@karpov.io",
"name": "vkarpov15"
},
"_npmVersion": "2.9.1",
"_phantomChildren": {},
"_requested": {
"name": "hooks-fixed",
"raw": "hooks-fixed@1.1.0",
"rawSpec": "1.1.0",
"scope": null,
"spec": "1.1.0",
"type": "version"
},
"_requiredBy": [
"/mongoose"
],
"_resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz",
"_shasum": "0e8c15336708e6611185fe390b44687dd5230dbb",
"_shrinkwrap": null,
"_spec": "hooks-fixed@1.1.0",
"_where": "/home/mitchell/Desktop/test-mywebsite/mywebsite/node_modules/mongoose",
"author": {
"email": "brian.noguchi@gmail.com",
"name": "Brian Noguchi",
"url": "https://github.com/bnoguchi/"
},
"bugs": {
"url": "https://github.com/vkarpov15/hooks-fixed/issues"
},
"dependencies": {},
"description": "Adds pre and post hook functionality to your JavaScript methods.",
"devDependencies": {
"expresso": ">=0.7.6",
"should": ">=0.2.1",
"underscore": ">=1.1.4"
},
"directories": {
"lib": "."
},
"dist": {
"shasum": "0e8c15336708e6611185fe390b44687dd5230dbb",
"tarball": "http://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz"
},
"engines": {
"node": ">=0.4.0"
},
"gitHead": "17ef258ae58d7aa6de7cec5b57f76233a4ba1c96",
"homepage": "https://github.com/vkarpov15/hooks-fixed/",
"keywords": [
"hooks",
"middleware",
"node",
"post",
"pre"
],
"licenses": [
"MIT"
],
"main": "./hooks.js",
"maintainers": [
{
"name": "vkarpov15",
"email": "valkar207@gmail.com"
}
],
"name": "hooks-fixed",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/vkarpov15/hooks-fixed.git"
},
"scripts": {
"test": "make test"
},
"version": "1.1.0"
}

786
node_modules/hooks-fixed/test.js generated vendored Normal file
View File

@@ -0,0 +1,786 @@
var hooks = require('./hooks')
, should = require('should')
, assert = require('assert')
, _ = 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 () {};
_.extend(A, hooks);
A.hook({
hook1: function (a) {},
hook2: function (b) {}
});
var a = new A();
assert.equal(typeof a.hook1, 'function');
assert.equal(typeof a.hook2, 'function');
},
'should run without pres and posts when not present': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
var a = new A();
a.save();
a.value.should.equal(1);
},
'should run with pres when present': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.pre('save', function (next) {
this.preValue = 2;
next();
});
var a = new A();
a.save();
a.value.should.equal(1);
a.preValue.should.equal(2);
},
'should run with posts when present': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.post('save', function (next) {
this.value = 2;
next();
});
var a = new A();
a.save();
a.value.should.equal(2);
},
'should run pres and posts when present': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.pre('save', function (next) {
this.preValue = 2;
next();
});
A.post('save', function (next) {
this.value = 3;
next();
});
var a = new A();
a.save();
a.value.should.equal(3);
a.preValue.should.equal(2);
},
'should run posts after pres': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.pre('save', function (next) {
this.override = 100;
next();
});
A.post('save', function (next) {
this.override = 200;
next();
});
var a = new A();
a.save();
a.value.should.equal(1);
a.override.should.equal(200);
},
'should not run a hook if a pre fails': function () {
var A = function () {};
_.extend(A, hooks);
var counter = 0;
A.hook('save', function () {
this.value = 1;
}, function (err) {
counter++;
});
A.pre('save', true, function (next, done) {
next(new Error());
});
var a = new A();
a.save();
counter.should.equal(1);
assert.equal(typeof a.value, 'undefined');
},
'should be able to run multiple pres': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.pre('save', function (next) {
this.v1 = 1;
next();
}).pre('save', function (next) {
this.v2 = 2;
next();
});
var a = new A();
a.save();
a.v1.should.equal(1);
a.v2.should.equal(2);
},
'should run multiple pres until a pre fails and not call the hook': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
}, function (err) {});
A.pre('save', function (next) {
this.v1 = 1;
next();
}).pre('save', function (next) {
next(new Error());
}).pre('save', function (next) {
this.v3 = 3;
next();
});
var a = new A();
a.save();
a.v1.should.equal(1);
assert.equal(typeof a.v3, 'undefined');
assert.equal(typeof a.value, 'undefined');
},
'should be able to run multiple posts': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.post('save', function (next) {
this.value = 2;
next();
}).post('save', function (next) {
this.value = 3.14;
next();
}).post('save', function (next) {
this.v3 = 3;
next();
});
var a = new A();
a.save();
assert.equal(a.value, 3.14);
assert.equal(a.v3, 3);
},
'should run only posts up until an error': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
}, function (err) {});
A.post('save', function (next) {
this.value = 2;
next();
}).post('save', function (next) {
this.value = 3;
next(new Error());
}).post('save', function (next) {
this.value = 4;
next();
});
var a = new A();
a.save();
a.value.should.equal(3);
},
"should fall back first to the hook method's last argument as the error handler if it is a function of arity 1 or 2": function () {
var A = function () {};
_.extend(A, hooks);
var counter = 0;
A.hook('save', function (callback) {
this.value = 1;
});
A.pre('save', true, function (next, done) {
next(new Error());
});
var a = new A();
a.save( function (err) {
if (err instanceof Error) counter++;
});
counter.should.equal(1);
should.deepEqual(undefined, a.value);
},
'should fall back second to the default error handler if specified': function () {
var A = function () {};
_.extend(A, hooks);
var counter = 0;
A.hook('save', function (callback) {
this.value = 1;
}, function (err) {
if (err instanceof Error) counter++;
});
A.pre('save', true, function (next, done) {
next(new Error());
});
var a = new A();
a.save();
counter.should.equal(1);
should.deepEqual(undefined, a.value);
},
'fallback default error handler should scope to the object': function () {
var A = function () {
this.counter = 0;
};
_.extend(A, hooks);
var counter = 0;
A.hook('save', function (callback) {
this.value = 1;
}, function (err) {
if (err instanceof Error) this.counter++;
});
A.pre('save', true, function (next, done) {
next(new Error());
});
var a = new A();
a.save();
a.counter.should.equal(1);
should.deepEqual(undefined, a.value);
},
'should fall back last to throwing the error': function () {
var A = function () {};
_.extend(A, hooks);
var counter = 0;
A.hook('save', function (err) {
if (err instanceof Error) return counter++;
this.value = 1;
});
A.pre('save', true, function (next, done) {
next(new Error());
});
var a = new A();
var didCatch = false;
try {
a.save();
} catch (e) {
didCatch = true;
e.should.be.an.instanceof(Error);
counter.should.equal(0);
assert.equal(typeof a.value, 'undefined');
}
didCatch.should.be.true;
},
"should proceed without mutating arguments if `next(null|undefined)` is called in a serial pre, and the last argument of the target method is a callback with node-like signature function (err, obj) {...} or function (err) {...}": function () {
var A = function () {};
_.extend(A, hooks);
var counter = 0;
A.prototype.save = function (callback) {
this.value = 1;
callback();
};
A.pre('save', function (next) {
next(null);
});
A.pre('save', function (next) {
next(undefined);
});
var a = new A();
a.save( function (err) {
if (err instanceof Error) counter++;
else counter--;
});
counter.should.equal(-1);
a.value.should.eql(1);
},
"should proceed with mutating arguments if `next(null|undefined)` is callback in a serial pre, and the last argument of the target method is not a function": function () {
var A = function () {};
_.extend(A, hooks);
A.prototype.set = function (v) {
this.value = v;
};
A.pre('set', function (next) {
next(undefined);
});
A.pre('set', function (next) {
next(null);
});
var a = new A();
a.set(1);
should.strictEqual(null, a.value);
},
'should not run any posts if a pre fails': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 2;
}, function (err) {});
A.pre('save', function (next) {
this.value = 1;
next(new Error());
}).post('save', function (next) {
this.value = 3;
next();
});
var a = new A();
a.save();
a.value.should.equal(1);
},
"can pass the hook's arguments verbatim to pres": function () {
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val) {
this[path] = val;
});
A.pre('set', function (next, path, val) {
path.should.equal('hello');
val.should.equal('world');
next();
});
var a = new A();
a.set('hello', 'world');
a.hello.should.equal('world');
},
// "can pass the hook's arguments as an array to pres": function () {
// // Great for dynamic arity - e.g., slice(...)
// var A = function () {};
// _.extend(A, hooks);
// A.hook('set', function (path, val) {
// this[path] = val;
// });
// A.pre('set', function (next, hello, world) {
// hello.should.equal('hello');
// world.should.equal('world');
// next();
// });
// var a = new A();
// a.set('hello', 'world');
// assert.equal(a.hello, 'world');
// },
"can pass the hook's arguments verbatim to posts": function () {
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val) {
this[path] = val;
});
A.post('set', function (next, path, val) {
path.should.equal('hello');
val.should.equal('world');
next();
});
var a = new A();
a.set('hello', 'world');
assert.equal(a.hello, 'world');
},
// "can pass the hook's arguments as an array to posts": function () {
// var A = function () {};
// _.extend(A, hooks);
// A.hook('set', function (path, val) {
// this[path] = val;
// });
// A.post('set', function (next, halt, args) {
// assert.equal(args[0], 'hello');
// assert.equal(args[1], 'world');
// next();
// });
// var a = new A();
// a.set('hello', 'world');
// assert.equal(a.hello, 'world');
// },
"pres should be able to modify and pass on a modified version of the hook's arguments": function () {
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val) {
this[path] = val;
assert.equal(arguments[2], 'optional');
});
A.pre('set', function (next, path, val) {
next('foo', 'bar');
});
A.pre('set', function (next, path, val) {
assert.equal(path, 'foo');
assert.equal(val, 'bar');
next('rock', 'says', 'optional');
});
A.pre('set', function (next, path, val, opt) {
assert.equal(path, 'rock');
assert.equal(val, 'says');
assert.equal(opt, 'optional');
next();
});
var a = new A();
a.set('hello', 'world');
assert.equal(typeof a.hello, 'undefined');
a.rock.should.equal('says');
},
'posts should see the modified version of arguments if the pres modified them': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val) {
this[path] = val;
});
A.pre('set', function (next, path, val) {
next('foo', 'bar');
});
A.post('set', function (next, path, val) {
path.should.equal('foo');
val.should.equal('bar');
});
var a = new A();
a.set('hello', 'world');
assert.equal(typeof a.hello, 'undefined');
a.foo.should.equal('bar');
},
'should pad missing arguments (relative to expected arguments of the hook) with null': function () {
// Otherwise, with hookFn = function (a, b, next, ),
// if we use hookFn(a), then because the pre functions are of the form
// preFn = function (a, b, next, ), then it actually gets executed with
// preFn(a, next, ), so when we call next() from within preFn, we are actually
// calling ()
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val, opts) {
this[path] = val;
});
A.pre('set', function (next, path, val, opts) {
next('foo', 'bar');
assert.equal(typeof opts, 'undefined');
});
var a = new A();
a.set('hello', 'world');
},
'should not invoke the target method until all asynchronous middleware have invoked dones': function () {
var counter = 0;
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val) {
counter++;
this[path] = val;
counter.should.equal(7);
});
A.pre('set', function (next, path, val) {
counter++;
next();
});
A.pre('set', true, function (next, done, path, val) {
counter++;
setTimeout(function () {
counter++;
done();
}, 1000);
next();
});
A.pre('set', function (next, path, val) {
counter++;
next();
});
A.pre('set', true, function (next, done, path, val) {
counter++;
setTimeout(function () {
counter++;
done();
}, 500);
next();
});
var a = new A();
a.set('hello', 'world');
},
'invoking a method twice should run its async middleware twice': function () {
var counter = 0;
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val) {
this[path] = val;
if (path === 'hello') counter.should.equal(1);
if (path === 'foo') counter.should.equal(2);
});
A.pre('set', true, function (next, done, path, val) {
setTimeout(function () {
counter++;
done();
}, 1000);
next();
});
var a = new A();
a.set('hello', 'world');
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', true, function (next, done) {
next();
done();
done();
});
A.pre('ack', true, function (next, done) {
next();
// Notice that done() is not invoked here
});
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 (beforeExit) {
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) {
// force a throw to re-exec next()
try {
next(new Error('bam'));
} catch (err) {
next();
}
});
A.pre('ack', function (next) {
next();
});
var a = new A();
a.ack();
beforeExit( function () {
a.acked.should.be.false;
});
},
'asynchronous middleware should be able to pass an error via `done`, stopping the middleware chain': function () {
var counter = 0;
var A = function () {};
_.extend(A, hooks);
A.hook('set', function (path, val, fn) {
counter++;
this[path] = val;
fn(null);
});
A.pre('set', true, function (next, done, path, val, fn) {
setTimeout(function () {
counter++;
done(new Error);
}, 1000);
next();
});
var a = new A();
a.set('hello', 'world', function (err) {
err.should.be.an.instanceof(Error);
should.strictEqual(undefined, a['hello']);
counter.should.eql(1);
});
},
'should be able to remove a particular pre': function () {
var A = function () {}
, preTwo;
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.pre('save', function (next) {
this.preValueOne = 2;
next();
});
A.pre('save', preTwo = function (next) {
this.preValueTwo = 4;
next();
});
A.removePre('save', preTwo);
var a = new A();
a.save();
a.value.should.equal(1);
a.preValueOne.should.equal(2);
should.strictEqual(undefined, a.preValueTwo);
},
'should be able to remove all pres associated with a hook': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.pre('save', function (next) {
this.preValueOne = 2;
next();
});
A.pre('save', function (next) {
this.preValueTwo = 4;
next();
});
A.removePre('save');
var a = new A();
a.save();
a.value.should.equal(1);
should.strictEqual(undefined, a.preValueOne);
should.strictEqual(undefined, a.preValueTwo);
},
'should be able to remove a particular post': function () {
var A = function () {}
, postTwo;
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.post('save', function (next) {
this.postValueOne = 2;
next();
});
A.post('save', postTwo = function (next) {
this.postValueTwo = 4;
next();
});
A.removePost('save', postTwo);
var a = new A();
a.save();
a.value.should.equal(1);
a.postValueOne.should.equal(2);
should.strictEqual(undefined, a.postValueTwo);
},
'should be able to remove all posts associated with a hook': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function () {
this.value = 1;
});
A.post('save', function (next) {
this.postValueOne = 2;
next();
});
A.post('save', function (next) {
this.postValueTwo = 4;
next();
});
A.removePost('save');
var a = new A();
a.save();
a.value.should.equal(1);
should.strictEqual(undefined, a.postValueOne);
should.strictEqual(undefined, a.postValueTwo);
},
'#pre should lazily make a method hookable': function () {
var A = function () {};
_.extend(A, hooks);
A.prototype.save = function () {
this.value = 1;
};
A.pre('save', function (next) {
this.preValue = 2;
next();
});
var a = new A();
a.save();
a.value.should.equal(1);
a.preValue.should.equal(2);
},
'#pre lazily making a method hookable should be able to provide a default errorHandler as the last argument': function () {
var A = function () {};
var preValue = "";
_.extend(A, hooks);
A.prototype.save = function () {
this.value = 1;
};
A.pre('save', function (next) {
next(new Error);
}, function (err) {
preValue = 'ERROR';
});
var a = new A();
a.save();
should.strictEqual(undefined, a.value);
preValue.should.equal('ERROR');
},
'#post should lazily make a method hookable': function () {
var A = function () {};
_.extend(A, hooks);
A.prototype.save = function () {
this.value = 1;
};
A.post('save', function (next) {
this.value = 2;
next();
});
var a = new A();
a.save();
a.value.should.equal(2);
},
"a lazy hooks setup should handle errors via a method's last argument, if it's a callback": function () {
var A = function () {};
_.extend(A, hooks);
A.prototype.save = function (fn) {};
A.pre('save', function (next) {
next(new Error("hi there"));
});
var a = new A();
a.save( function (err) {
err.should.be.an.instanceof(Error);
});
},
'should intercept method callbacks for post handlers': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function (val, callback) {
this.value = val;
callback();
});
A.post('save', function (next) {
assert.equal(a.value, 2);
this.value += 2;
setTimeout(next, 10);
}).post('save', function (next) {
assert.equal(a.value, 4);
this.value += 3;
setTimeout(next, 10);
}).post('save', function (next) {
assert.equal(a.value, 7);
this.value2 = 3;
setTimeout(next, 10);
});
var a = new A();
a.save(2, function(){
assert.equal(a.value, 7);
assert.equal(a.value2, 3);
});
},
'should handle parallel followed by serial': function () {
var A = function () {};
_.extend(A, hooks);
A.hook('save', function (val, callback) {
this.value = val;
callback();
});
A.pre('save', true, function(next, done) {
process.nextTick(function() {
done();
});
next();
}).pre('save', function(done) {
process.nextTick(function() {
done();
});
});
var a = new A();
a.save(2, function(){
assert.ok(true);
});
}
};