Comparing version 0.3.0 to 0.3.1
@@ -172,4 +172,4 @@ // TODO Add in pre and post skipping options | ||
fnWrapper.hookCalled = true; | ||
fn.apply(scope, arguments); | ||
return fn.apply(scope, arguments); | ||
}; | ||
} |
{ | ||
"name": "hooks", | ||
"description": "Adds pre and post hook functionality to your JavaScript methods.", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "node", |
307
README.md
@@ -19,3 +19,3 @@ hooks | ||
tightly than is necessary. For example, what if someone does not want to do background | ||
job creation after the logical save? | ||
job creation after the logical save? | ||
@@ -29,26 +29,29 @@ It is nicer to tack on functionality using what we call `pre` and `post` hooks. These | ||
var hooks = require('hooks') | ||
, Document = require('./path/to/some/document/constructor'); | ||
```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]; | ||
} | ||
// 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 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 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 () { | ||
this.sendToBackgroundQueue(); | ||
}); | ||
// Define a middleware function to be invoked after 'save' | ||
Document.post('save', function createJob (next) { | ||
this.sendToBackgroundQueue(); | ||
next(); | ||
}); | ||
``` | ||
@@ -64,3 +67,3 @@ If you already have defined `Document.prototype` methods for which you want pres and posts, | ||
// Add hooks' methods: `hook`, `pre`, and `post` | ||
// Add hooks' methods: `hook`, `pre`, and `post` | ||
for (var k in hooks) { | ||
@@ -84,4 +87,5 @@ Document[k] = hooks[k]; | ||
// Define a middleware function to be invoked after 'save' | ||
Document.post('save', function createJob () { | ||
Document.post('save', function createJob (next) { | ||
this.sendToBackgroundQueue(); | ||
next(); | ||
}); | ||
@@ -98,9 +102,21 @@ ``` | ||
## Defining multiple pres (or posts) | ||
`pre` is chainable, so you can define multiple pres via: | ||
Document.pre('save', function (next, halt) { | ||
console.log("hello"); | ||
}).pre('save', function (next, halt) { | ||
console.log("world"); | ||
}); | ||
`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. | ||
@@ -110,13 +126,17 @@ | ||
You can define a default error handler by passing a 2nd function as the 3rd argument to `hook`: | ||
Document.hook('set', function (path, val) { | ||
this[path] = val; | ||
}, function (err) { | ||
// Handler the error here | ||
console.error(err); | ||
}); | ||
```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: | ||
Document.pre('set', function (next, path, val) { | ||
next(new Error()); | ||
}); | ||
```javascript | ||
Document.pre('set', function (next, path, val) { | ||
next(new Error()); | ||
}); | ||
``` | ||
@@ -130,3 +150,3 @@ If you do not set up a default handler, then `hooks` makes the default handler that just throws the `Error`. | ||
error handler you may have set up. | ||
```javascript | ||
@@ -142,3 +162,3 @@ Document.hook('save', function (callback) { | ||
if (err) console.error(err); | ||
// Rest of callback logic follows ... | ||
@@ -158,12 +178,14 @@ }); | ||
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' | ||
```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' | ||
``` | ||
@@ -176,44 +198,83 @@ As you can see above, we pass arguments via `next`. | ||
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(); | ||
}); | ||
```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: | ||
// 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'); | ||
```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 | ||
@@ -244,38 +305,40 @@ | ||
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. | ||
}); | ||
```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... | ||
}); | ||
// 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: | ||
@@ -282,0 +345,0 @@ |
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
43848
370