Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base: fea090ceb0
...
compare: fa189f1703
Checking mergeability… Don't worry, you can still create the pull request.
  • 6 commits
  • 128 files changed
  • 0 commit comments
  • 2 contributors
Showing with 27,960 additions and 189 deletions.
  1. +5 −2 README.md
  2. +2 −2 app-cluster.js
  3. +1 −3 controllers/TwitterController.js
  4. +0 −182 lib/ExamplesController.js
  5. +2 −0  node_modules/hooks/.npmignore
  6. +9 −0 node_modules/hooks/Makefile
  7. +306 −0 node_modules/hooks/README.md
  8. +134 −0 node_modules/hooks/hooks.alt.js
  9. +161 −0 node_modules/hooks/hooks.js
  10. +38 −0 node_modules/hooks/package.json
  11. +691 −0 node_modules/hooks/test.js
  12. +22 −0 node_modules/log/History.md
  13. +14 −0 node_modules/log/Makefile
  14. +99 −0 node_modules/log/Readme.md
  15. +18 −0 node_modules/log/examples/file.js
  16. +16 −0 node_modules/log/examples/file.log
  17. +15 −0 node_modules/log/examples/reader.js
  18. +17 −0 node_modules/log/examples/stdout.js
  19. +329 −0 node_modules/log/index.html
  20. +2 −0  node_modules/log/index.js
  21. +253 −0 node_modules/log/lib/log.js
  22. +9 −0 node_modules/log/package.json
  23. +2 −0  node_modules/mkdirp/.gitignore.orig
  24. +5 −0 node_modules/mkdirp/.gitignore.rej
  25. +2 −0  node_modules/mkdirp/.npmignore
  26. +4 −0 node_modules/mkdirp/.travis.yml
  27. +21 −0 node_modules/mkdirp/LICENSE
  28. +61 −0 node_modules/mkdirp/README.markdown
  29. +6 −0 node_modules/mkdirp/examples/pow.js
  30. +6 −0 node_modules/mkdirp/examples/pow.js.orig
  31. +19 −0 node_modules/mkdirp/examples/pow.js.rej
  32. +88 −0 node_modules/mkdirp/index.js
  33. +23 −0 node_modules/mkdirp/package.json
  34. +38 −0 node_modules/mkdirp/test/chmod.js
  35. +37 −0 node_modules/mkdirp/test/clobber.js
  36. +28 −0 node_modules/mkdirp/test/mkdirp.js
  37. +32 −0 node_modules/mkdirp/test/perm.js
  38. +39 −0 node_modules/mkdirp/test/perm_sync.js
  39. +41 −0 node_modules/mkdirp/test/race.js
  40. +32 −0 node_modules/mkdirp/test/rel.js
  41. +25 −0 node_modules/mkdirp/test/return.js
  42. +24 −0 node_modules/mkdirp/test/return_sync.js
  43. +32 −0 node_modules/mkdirp/test/sync.js
  44. +28 −0 node_modules/mkdirp/test/umask.js
  45. +32 −0 node_modules/mkdirp/test/umask_sync.js
  46. +5 −0 node_modules/mongodb/.travis.yml
  47. +71 −0 node_modules/mongodb/Makefile
  48. +45 −0 node_modules/mongodb/external-libs/bson/Makefile
  49. +2,165 −0 node_modules/mongodb/external-libs/bson/bson.cc
  50. +105 −0 node_modules/mongodb/external-libs/bson/bson.h
  51. +20 −0 node_modules/mongodb/external-libs/bson/index.js
  52. +349 −0 node_modules/mongodb/external-libs/bson/test/test_bson.js
  53. +218 −0 node_modules/mongodb/external-libs/bson/test/test_full_bson.js
  54. +132 −0 node_modules/mongodb/external-libs/bson/test/test_stackless_bson.js
  55. +39 −0 node_modules/mongodb/external-libs/bson/wscript
  56. +1 −0  node_modules/mongodb/index.js
  57. +40 −0 node_modules/mongodb/install.js
  58. +390 −0 node_modules/mongodb/lib/mongodb/admin.js
  59. +1,508 −0 node_modules/mongodb/lib/mongodb/collection.js
  60. +27 −0 node_modules/mongodb/lib/mongodb/commands/base_command.js
  61. +207 −0 node_modules/mongodb/lib/mongodb/commands/db_command.js
  62. +111 −0 node_modules/mongodb/lib/mongodb/commands/delete_command.js
  63. +83 −0 node_modules/mongodb/lib/mongodb/commands/get_more_command.js
  64. +141 −0 node_modules/mongodb/lib/mongodb/commands/insert_command.js
  65. +98 −0 node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js
  66. +210 −0 node_modules/mongodb/lib/mongodb/commands/query_command.js
  67. +174 −0 node_modules/mongodb/lib/mongodb/commands/update_command.js
  68. +411 −0 node_modules/mongodb/lib/mongodb/connection/connection.js
  69. +250 −0 node_modules/mongodb/lib/mongodb/connection/connection_pool.js
  70. +23 −0 node_modules/mongodb/lib/mongodb/connection/connection_utils.js
  71. +1,195 −0 node_modules/mongodb/lib/mongodb/connection/repl_set.js
  72. +725 −0 node_modules/mongodb/lib/mongodb/connection/server.js
  73. +125 −0 node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js
  74. +40 −0 node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js
  75. +724 −0 node_modules/mongodb/lib/mongodb/cursor.js
  76. +141 −0 node_modules/mongodb/lib/mongodb/cursorstream.js
  77. +1,831 −0 node_modules/mongodb/lib/mongodb/db.js
  78. +209 −0 node_modules/mongodb/lib/mongodb/gridfs/chunk.js
  79. +98 −0 node_modules/mongodb/lib/mongodb/gridfs/grid.js
  80. +1,092 −0 node_modules/mongodb/lib/mongodb/gridfs/gridstore.js
  81. +179 −0 node_modules/mongodb/lib/mongodb/gridfs/readstream.js
  82. +151 −0 node_modules/mongodb/lib/mongodb/index.js
  83. +131 −0 node_modules/mongodb/lib/mongodb/responses/mongo_reply.js
  84. +74 −0 node_modules/mongodb/lib/mongodb/utils.js
  85. +5 −0 node_modules/mongodb/node_modules/bson/.travis.yml
  86. +31 −0 node_modules/mongodb/node_modules/bson/Makefile
  87. 0  node_modules/mongodb/node_modules/bson/README
  88. +28 −0 node_modules/mongodb/node_modules/bson/ext/Makefile
  89. +2,165 −0 node_modules/mongodb/node_modules/bson/ext/bson.cc
  90. +105 −0 node_modules/mongodb/node_modules/bson/ext/bson.h
  91. +20 −0 node_modules/mongodb/node_modules/bson/ext/index.js
  92. +39 −0 node_modules/mongodb/node_modules/bson/ext/wscript
  93. +41 −0 node_modules/mongodb/node_modules/bson/install.js
  94. +336 −0 node_modules/mongodb/node_modules/bson/lib/bson/binary.js
  95. +387 −0 node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js
  96. +1,483 −0 node_modules/mongodb/node_modules/bson/lib/bson/bson.js
  97. +27 −0 node_modules/mongodb/node_modules/bson/lib/bson/code.js
  98. +33 −0 node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js
  99. +35 −0 node_modules/mongodb/node_modules/bson/lib/bson/double.js
  100. +123 −0 node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js
  101. +74 −0 node_modules/mongodb/node_modules/bson/lib/bson/index.js
  102. +856 −0 node_modules/mongodb/node_modules/bson/lib/bson/long.js
  103. +15 −0 node_modules/mongodb/node_modules/bson/lib/bson/max_key.js
  104. +15 −0 node_modules/mongodb/node_modules/bson/lib/bson/min_key.js
  105. +257 −0 node_modules/mongodb/node_modules/bson/lib/bson/objectid.js
  106. +50 −0 node_modules/mongodb/node_modules/bson/lib/bson/symbol.js
  107. +855 −0 node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js
  108. +23 −0 node_modules/mongodb/node_modules/bson/package.json
  109. +242 −0 node_modules/mongodb/node_modules/bson/test/browser/bson_test.js
  110. +2,034 −0 node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js
  111. +13 −0 node_modules/mongodb/node_modules/bson/test/browser/suite2.js
  112. +7 −0 node_modules/mongodb/node_modules/bson/test/browser/suite3.js
  113. +30 −0 node_modules/mongodb/node_modules/bson/test/browser/test.html
  114. +240 −0 node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js
  115. +459 −0 node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js
  116. +1,626 −0 node_modules/mongodb/node_modules/bson/test/node/bson_test.js
Sorry, we could not display the entire diff because it was too big.
View
7 README.md
@@ -15,14 +15,17 @@ Make a copy of the sample config:
$ cp conf/conf.js.sample conf/conf.js
-Run MongoDB
+Note: If you are using the provided API keys/tokens, you MUST use "local.saturn.io:3000" (set local.saturn.io -> 127.0.0.1 in hosts). If you [set up a new dev app on Twitter](https://dev.twitter.com/apps/), you can set the callback URL et al to anything you want, including localhost, 127.0.0.1, etc.
+
+Run MongoDB.
+
Run the app:
$ eb
Ignore the sidebar that shows up when you're not logged in. Log in first and then use it.
-One important thing is to create the cron job tasks once you log in for the first time:
+One important thing is to create the cron job tasks (http://{{domain/ip, port}}/cron/manage) once you log in for the first time:
<table>
<tr>
View
4 app-cluster.js
@@ -50,7 +50,7 @@ exports.boot = function(port,path){
}
});
- // In 2 seconds, try to launch the twitter stream
+ // In 3 seconds, try to launch the twitter stream
setTimeout(function () {
var host = "local.saturn.io";
var port = 3000;
@@ -59,7 +59,7 @@ exports.boot = function(port,path){
var rand = Math.round(Math.random()*1000);
var request = client.request("GET", url, {host: host, port: port});
client.addListener('error', function(error) {
- console.log('ERROR' + error.message);
+ console.log('ERROR: ' + error.message);
});
request.addListener("response", function(response) {
var body = "";
View
4 controllers/TwitterController.js
@@ -33,12 +33,10 @@ module.exports = {
if (err || !task) {
return res.send("No task set up for monitoring a twitter stream..");
}
- //console.log("Task: "+task.id);
var attr = task.attributes || {};
- if (!attr.connected || attr.last_ping.getTime() > Date.now()-stream_timeout) {
- //console.log("Already streaming ("+attr.last_ping.getTime()+" > "+(Date.now()-stream_timeout)+")");
+ if (attr.connected || attr.last_ping.getTime() > Date.now() - stream_timeout) {
return res.send("Already streaming...");
}
View
182 lib/ExamplesController.js
@@ -1,182 +0,0 @@
-
-/**
- * Examples Controller
- * Created by create-controller script @ Fri Mar 11 2011 21:16:50 GMT+0000 (GMT)
- **/
- var mongoose = require('mongoose'),
- Example = mongoose.model('Example'),
- pager = require('../utils/pager.js'),
- ViewTemplatePath = 'examples';
-
-module.exports = {
-
- init: function(params) {
- ViewTemplatePath = params.viewPath // Enable over-ride of view path for testing
- },
-
- /**
- * Index action, returns a list either via the views/examples/index.html view or via json
- * Default mapping to GET '/examples'
- * For JSON use '/examples.json'
- **/
- index: function(req, res, next) {
-
- var from = req.params.from ? parseInt(req.params.from) - 1 : 0;
- var to = req.params.to ? parseInt(req.params.to) : 10;
- var total = 0;
-
- Example.count({}, function (err, count) {
- total = count;
- var pagerHtml = pager.render(from,to,total,'/examples');
-
- Example.find({})
- .sort('name', 1)
- .skip(from).limit(to)
- .find(function (err, examples) {
-
- if(err) return next(err);
-
- switch (req.params.format) {
- case 'json':
- res.send(examples.map(function(u) {
- return u.toObject();
- }));
- break;
-
- default:
- res.render(ViewTemplatePath,{examples:examples,pagerHtml:pagerHtml});
- }
-
- });
-
- });
-
- },
-
- /**
- * Show action, returns shows a single item via views/examples/show.html view or via json
- * Default mapping to GET '/example/:id'
- * For JSON use '/example/:id.json'
- **/
- show: function(req, res, next) {
-
- Example.findById(req.params.id, function(err, example) {
-
- if(err) return next(err);
-
- switch (req.params.format) {
- case 'json':
- res.send(example.toObject());
- break;
-
- default:
- res.render(ViewTemplatePath + "/show",{example:example});
- }
-
- });
-
- },
-
- /**
- * Edit action, returns a form via views/examples/edit.html view no JSON view.
- * Default mapping to GET '/example/:id/edit'
- **/
- edit: function(req, res, next){
- Example.findById(req.params.id, function(err, example) {
- if(err) return next(err);
- res.render(ViewTemplatePath + "/edit",{example:example});
- });
- },
-
- /**
- * Update action, updates a single item and redirects to Show or returns the object as json
- * Default mapping to PUT '/example/:id', no GET mapping
- **/
- update: function(req, res, next){
-
-
-
- Example.findById(req.params.id, function(err, example) {
-
- if (!example) return next(err);
-
- example.name = req.body.example.name;
-
- example.save(function(err) {
-
- if (err) {
- console.log(err);
- req.flash('error','Could not update example: ' + err);
- res.redirect('/examples');
- return;
- }
-
- switch (req.params.format) {
- case 'json':
- res.send(example.toObject());
- break;
- default:
- req.flash('info', 'Example updated');
- res.redirect('/example/' + req.params.id);
- }
- });
- });
- },
-
- /**
- * Create action, creates a single item and redirects to Show or returns the object as json
- * Default mapping to POST '/examples', no GET mapping
- **/
- create: function(req, res, next){
-
- var example = new Example(req.body.example);
-
- example.save(function(err) {
-
- if (err) {
- req.flash('error','Could not create example: ' + err);
- res.redirect('/examples');
- return;
- }
-
- switch (req.params.format) {
- case 'json':
- res.send(example.toObject());
- break;
-
- default:
- req.flash('info','Example created');
- res.redirect('/example/' + example.id);
- }
- });
-
- },
-
- /**
- * Delete action, deletes a single item and redirects to index
- * Default mapping to DEL '/example/:id', no GET mapping
- **/
- destroy: function(req, res, next){
-
- Example.findById(req.params.id, function(err, example) {
-
- if (!example) {
- req.flash('error','Unable to locate the example to delete!');
- res.render('404');
- return false;
- };
-
- example.remove(function(err) {
- if(err) {
- req.flash('error','There was an error deleting the example!');
- res.send('false');
- } else {
- req.flash('info','Example deleted');
- res.send('true');
- }
- });
- });
-
- }
-
-};
View
2  node_modules/hooks/.npmignore
@@ -0,0 +1,2 @@
+**.swp
+node_modules
View
9 node_modules/hooks/Makefile
@@ -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
View
306 node_modules/hooks/README.md
@@ -0,0 +1,306 @@
+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:
+
+ 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 () {
+ this.sendToBackgroundQueue();
+ });
+
+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 () {
+ this.sendToBackgroundQueue();
+});
+```
+
+## 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` 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");
+ });
+
+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`:
+ 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());
+ });
+
+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`:
+
+ 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.
+
+ 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');
+
+## 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:
+
+ 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
View
134 node_modules/hooks/hooks.alt.js
@@ -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;
+ }
+};
View
161 node_modules/hooks/hooks.js
@@ -0,0 +1,161 @@
+// 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
+ , _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 (!proto[name].numAsyncPres) {
+ return _done.apply(self, hookArgs);
+ }
+ }
+ , _done = function () {
+ var args_ = Array.prototype.slice.call(arguments)
+ , ret, total_, current_, next_, done_, postArgs;
+ if (_current === _total) {
+ ret = fn.apply(self, args_);
+ total_ = posts.length;
+ current_ = -1;
+ 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);
+ }
+ };
+ if (total_) return next_();
+ return ret;
+ }
+ };
+ if (_asyncsLeft) {
+ function _asyncsDone (err) {
+ if (err && err instanceof Error) {
+ return handleError(err);
+ }
+ --_asyncsLeft || _done.apply(self, hookArgs);
+ }
+ }
+ function handleError (err) {
+ if ('function' == typeof lastArg)
+ return lastArg(err);
+ if (errorCb) return errorCb.call(self, err);
+ throw err;
+ }
+ 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;
+ },
+ _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;
+ fn.apply(scope, arguments);
+ };
+}
View
38 node_modules/hooks/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "hooks",
+ "description": "Adds pre and post hook functionality to your JavaScript methods.",
+ "version": "0.2.1",
+ "keywords": [
+ "node",
+ "hooks",
+ "middleware",
+ "pre",
+ "post"
+ ],
+ "homepage": "https://github.com/bnoguchi/hooks-js/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/bnoguchi/hooks-js.git"
+ },
+ "author": "Brian Noguchi <brian.noguchi@gmail.com> (https://github.com/bnoguchi/)",
+ "main": "./hooks.js",
+ "directories": {
+ "lib": "."
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "expresso": ">=0.7.6",
+ "should": ">=0.2.1",
+ "underscore": ">=1.1.4"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "licenses": [
+ "MIT"
+ ],
+ "optionalDependencies": {}
+}
View
691 node_modules/hooks/test.js
@@ -0,0 +1,691 @@
+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);
+ },
+
+ '#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);
+ });
+ }
+};
View
22 node_modules/log/History.md
@@ -0,0 +1,22 @@
+
+1.3.0 / 2012-02-10
+==================
+
+ * Changed to use local time zone instead of GMT [thakkar-rushikesh]
+
+1.2.0 / 2011-05-23
+==================
+
+ * Added sprintf-like `%s` support
+
+1.1.1 / 2010-09-26
+==================
+
+ * Fixed `Log()` initialization without giving a stream [bentruyman]
+
+1.1.0 / 2010-09-26
+==================
+
+ * Added streaming reader capabilities (_pass a readable stream_)
+ * Added `Log()` log level as string support (_alternative of constants_)
+ * Added _./index.js_ so people can clone
View
14 node_modules/log/Makefile
@@ -0,0 +1,14 @@
+
+docs: index.html
+
+index.html: lib/log.js
+ dox \
+ --title "Log.js" \
+ --desc "Tiny logger for [NodeJS](http://nodejs.org)." \
+ --ribbon "http://github.com/visionmedia/log.js" \
+ lib/log.js > $@
+
+docclean:
+ rm -f index.html
+
+.PHONY: docs docclean
View
99 node_modules/log/Readme.md
@@ -0,0 +1,99 @@
+
+# Log.js
+
+ Light-weight logging for [NodeJS](http://nodejs.org), including a
+ streaming log reader.
+
+## Installation
+
+ $ npm install log
+
+## Example
+
+Log level defaults to __DEBUG__ however here we specify __info__, and the stream defaults to _stdout_:
+
+ var Log = require('log')
+ , log = new Log('info');
+
+ log.debug('preparing email');
+ log.info('sending email');
+ log.error('failed to send email');
+
+Specifying a specific stream:
+
+ var fs = require('fs')
+ , Log = require('log')
+ , log = new Log('debug', fs.createWriteStream('my.log'));
+
+Instead of the log level constants, you may also supply a string:
+
+ var Log = require('log')
+ , log = new Log('warning');
+
+ We can also use `%s` much like `console.log()` to pass arguments:
+
+ log.error('oh no, failed to send mail to %s.', user.email);
+
+## Reader
+
+ To stream a log, simply pass a readable stream instead of a writable:
+
+ var Log = require('log')
+ , fs = require('fs')
+ , stream = fs.createReadStream(__dirname + '/file.log')
+ , log = new Log('debug', stream);
+
+ log.on('line', function(line){
+ console.log(line);
+ });
+
+Example stdout:
+
+ { date: Sun, 26 Sep 2010 01:26:14 GMT
+ , level: 1
+ , levelString: 'ALERT'
+ , msg: 'a alert message'
+ }
+ { date: Sun, 26 Sep 2010 01:26:14 GMT
+ , level: 0
+ , levelString: 'EMERGENCY'
+ , msg: 'a emergency message'
+ }
+
+## Log Levels
+
+ Mirror that of syslog:
+
+ - 0 __EMERGENCY__ system is unusable
+ - 1 __ALERT__ action must be taken immediately
+ - 2 __CRITICAL__ the system is in critical condition
+ - 3 __ERROR__ error condition
+ - 4 __WARNING__ warning condition
+ - 5 __NOTICE__ a normal but significant condition
+ - 6 __INFO__ a purely informational message
+ - 7 __DEBUG__ messages to debug an application
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2009-2010 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
18 node_modules/log/examples/file.js
@@ -0,0 +1,18 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Log = require('../lib/log')
+ , fs = require('fs')
+ , stream = fs.createWriteStream(__dirname + '/file.log', { flags: 'a' })
+ , log = new Log('debug', stream);
+
+log.debug('a debug message');
+log.info('a info message');
+log.notice('a notice message');
+log.warning('a warning message');
+log.error('a error message');
+log.critical('a critical message');
+log.alert('a alert message');
+log.emergency('a emergency message');
View
16 node_modules/log/examples/file.log
@@ -0,0 +1,16 @@
+[Sun, 26 Sep 2010 01:26:14 GMT] DEBUG a debug message
+[Sun, 26 Sep 2010 01:26:14 GMT] INFO a info message
+[Sun, 26 Sep 2010 01:26:14 GMT] NOTICE a notice message
+[Sun, 26 Sep 2010 01:26:14 GMT] WARNING a warning message
+[Sun, 26 Sep 2010 01:26:14 GMT] ERROR a error message
+[Sun, 26 Sep 2010 01:26:14 GMT] CRITICAL a critical message
+[Sun, 26 Sep 2010 01:26:14 GMT] ALERT a alert message
+[Sun, 26 Sep 2010 01:26:14 GMT] EMERGENCY a emergency message
+[Thu, 03 Feb 2011 04:54:39 GMT] DEBUG a debug message
+[Thu, 03 Feb 2011 04:54:39 GMT] INFO a info message
+[Thu, 03 Feb 2011 04:54:39 GMT] NOTICE a notice message
+[Thu, 03 Feb 2011 04:54:39 GMT] WARNING a warning message
+[Thu, 03 Feb 2011 04:54:39 GMT] ERROR a error message
+[Thu, 03 Feb 2011 04:54:39 GMT] CRITICAL a critical message
+[Thu, 03 Feb 2011 04:54:39 GMT] ALERT a alert message
+[Thu, 03 Feb 2011 04:54:39 GMT] EMERGENCY a emergency message
View
15 node_modules/log/examples/reader.js
@@ -0,0 +1,15 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Log = require('../lib/log')
+ , fs = require('fs')
+ , stream = fs.createReadStream(__dirname + '/file.log')
+ , log = new Log('debug', stream);
+
+log.on('line', function(line){
+ console.log(line);
+}).on('end', function(){
+ console.log('done');
+});;
View
17 node_modules/log/examples/stdout.js
@@ -0,0 +1,17 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Log = require('../lib/log')
+ , log = new Log('notice');
+
+log.debug('a debug message');
+log.info('a info message');
+log.notice('a notice message');
+log.warning('a warning message');
+log.error('a error message');
+log.critical('a critical message');
+log.alert('a alert message');
+log.emergency('a emergency %s', 'message');
+log.emergency('a really %s emergency %s', 'bad', 'message');
View
329 node_modules/log/index.html
@@ -0,0 +1,329 @@
+<a href="http://github.com/visionmedia/log.js"><img alt="Fork me on GitHub" id="ribbon" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"></a><html>
+ <head>
+ <title>Log.js</title>
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
+ <style>body {
+ margin: 0;
+ padding: 0;
+ font: 14px/1.5 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
+ color: #252519;
+}
+a {
+ color: #252519;
+}
+a:hover {
+ text-decoration: underline;
+ color: #19469D;
+}
+p {
+ margin: 12px 0;
+}
+h1, h2, h3 {
+ margin: 0;
+ padding: 0;
+}
+table#source {
+ width: 100%;
+ border-collapse: collapse;
+}
+table#source td:first-child {
+ padding: 30px 40px 30px 40px;
+ vertical-align: top;
+}
+table#source td:first-child,
+table#source td:first-child pre {
+ width: 450px;
+}
+table#source td:last-child {
+ padding: 30px 0 30px 40px;
+ border-left: 1px solid #E5E5EE;
+ background: #F5F5FF;
+}
+table#source tr {
+ border-bottom: 1px solid #E5E5EE;
+}
+table#source tr.filename {
+ padding-top: 40px;
+ border-top: 1px solid #E5E5EE;
+}
+table#source tr.filename td:first-child {
+ text-transform: capitalize;
+}
+table#source tr.filename td:last-child {
+ font-size: 12px;
+}
+table#source tr.filename h2 {
+ margin: 0;
+ padding: 0;
+ cursor: pointer;
+}
+table#source tr.code h1,
+table#source tr.code h2,
+table#source tr.code h3 {
+ margin-top: 30px;
+ font-family: "Lucida Grande", "Helvetica Nueue", Arial, sans-serif;
+ font-size: 18px;
+}
+table#source tr.code h2 {
+ font-size: 16px;
+}
+table#source tr.code h3 {
+ font-size: 14px;
+}
+table#source tr.code ul {
+ margin: 15px 0 15px 35px;
+ padding: 0;
+}
+table#source tr.code ul li {
+ margin: 0;
+ padding: 1px 0;
+}
+table#source tr.code ul li p {
+ margin: 0;
+ padding: 0;
+}
+table#source tr.code td:first-child pre {
+ padding: 20px;
+}
+#ribbon {
+ position: fixed;
+ top: 0;
+ right: 0;
+}
+code .string { color: #219161; }
+code .regexp { color: #219161; }
+code .keyword { color: #954121; }
+code .number { color: #19469D; }
+code .comment { color: #bbb; }
+code .this { color: #19469D; }</style>
+ <script>
+ $(function(){
+ $('tr.code').hide();
+ $('tr.filename').toggle(function(){
+ $(this).nextUntil('.filename').fadeIn();
+ }, function(){
+ $(this).nextUntil('.filename').fadeOut();
+ });
+ });
+ </script>
+ </head>
+ <body>
+<table id="source"><tbody><tr><td><h1>Log.js</h1><p>Tiny logger for <a href="http://nodejs.org">NodeJS</a>.</p></td><td></td></tr><tr class="filename"><td><h2 id="lib/log.js"><a href="#">log</a></h2></td><td>lib/log.js</td></tr><tr class="code">
+<td class="docs">
+<p>Initialize a <code>Loggeer</code> with the given log <code>level</code> defaulting
+to <strong>DEBUG</strong> and <code>stream</code> defaulting to <em>stdout</em>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>Number</em> level </p></li><li><p><strong>param</strong>: <em>Object</em> stream </p></li><li><p><strong>api</strong>: <em>public</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="keyword">var</span> <span class="class">Log</span> = <span class="variable">exports</span> = <span class="variable">module</span>.<span class="variable">exports</span> = <span class="keyword">function</span> <span class="class">Log</span>(<span class="variable">level</span>, <span class="variable">stream</span>){
+ <span class="this">this</span>.<span class="variable">level</span> = <span class="variable">level</span> || <span class="variable">exports</span>.<span class="class">DEBUG</span>;
+ <span class="this">this</span>.<span class="variable">stream</span> = <span class="variable">stream</span> || <span class="variable">process</span>.<span class="variable">stdout</span>;
+};</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>System is unusable.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">EMERGENCY</span> = <span class="number integer">0</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Action must be taken immediately.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">ALERT</span> = <span class="number integer">1</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Critical condition.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">CRITICAL</span> = <span class="number integer">2</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Error condition.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">ERROR</span> = <span class="number integer">3</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Warning condition.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">WARNING</span> = <span class="number integer">4</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Normal but significant condition.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">NOTICE</span> = <span class="number integer">5</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Purely informational message.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">INFO</span> = <span class="number integer">6</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Application debug messages.</p>
+
+<ul><li><p><strong>type</strong>: <em>Number</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">exports</span>.<span class="class">DEBUG</span> = <span class="number integer">7</span>;</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>prototype.
+ </p>
+</td>
+<td class="code">
+<pre><code><span class="class">Log</span>.<span class="variable">prototype</span> = {</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Log emergency <code>msg</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> msg</p></li><li><p><strong>api</strong>: <em>public</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">emergency</span>: <span class="keyword">function</span>(<span class="variable">msg</span>){
+ <span class="this">this</span>.<span class="variable">log</span>(<span class="string">'EMERGENCY'</span>, <span class="variable">msg</span>);
+ },</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Log alert <code>msg</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> msg</p></li><li><p><strong>api</strong>: <em>public</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">alert</span>: <span class="keyword">function</span>(<span class="variable">msg</span>){
+ <span class="this">this</span>.<span class="variable">log</span>(<span class="string">'ALERT'</span>, <span class="variable">msg</span>);
+ },</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Log critical <code>msg</code>.</p>
+
+<h2></h2>
+
+<ul><li><p><strong>param</strong>: <em>String</em> msg</p></li><li><p><strong>api</strong>: <em>public</em></p></li></ul>
+</td>
+<td class="code">
+<pre><code><span class="variable">critical</span>: <span class="keyword">function</span>(<span class="variable">msg</span>){
+ <span class="this">this</span>.<span class="variable">log</span>(<span class="string">'CRITICAL'</span>, <span class="variable">msg</span>);
+ },</code></pre>
+</td>
+</tr>
+<tr class="code">
+<td class="docs">
+<p>Log error <code>msg</code>.</p>
+
+<h2></h2>
+