{"_id":"understudy","_rev":"291404","name":"understudy","description":"action interceptor for dynamic extensible systems","dist-tags":{"latest":"4.1.0"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"time":{"modified":"2021-06-03T18:51:21.000Z","created":"2012-08-04T19:38:51.747Z","4.1.0":"2015-09-25T00:53:30.755Z","4.0.0":"2015-05-28T01:44:12.069Z","3.1.0":"2015-03-26T18:45:35.836Z","3.0.0":"2014-12-10T23:25:28.023Z","2.0.0":"2014-12-10T19:25:05.675Z","1.1.0":"2013-01-11T10:07:16.085Z","1.0.1":"2012-10-14T23:14:08.894Z","1.0.0":"2012-10-13T15:33:11.202Z","0.1.1":"2012-08-31T14:18:59.628Z","0.1.0":"2012-08-23T19:58:05.239Z","0.0.2":"2012-08-09T20:26:25.905Z","0.0.1":"2012-08-04T19:38:51.747Z"},"users":{"jeseab":true},"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"repository":{"type":"git","url":"git+ssh://git@github.com/bmeck/understudy.git"},"versions":{"4.1.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["hooks","before","after","flow control"],"version":"4.1.0","main":"index.js","repository":{"type":"git","url":"git+ssh://git@github.com/bmeck/understudy.git"},"dependencies":{},"devDependencies":{"accounting":"~0.4.1","benchmark":"~1.0.0","microtime":"^2.0.0"},"optionalDependencies":{},"scripts":{"test":"for SCRIPT in test/*.js; do echo Testing $SCRIPT; node $SCRIPT; done"},"engines":{"node":"*"},"license":"MIT","gitHead":"dc06eac5770435242821a32f685bab91b33004f8","bugs":{"url":"https://github.com/bmeck/understudy/issues"},"homepage":"https://github.com/bmeck/understudy#readme","_id":"understudy@4.1.0","_shasum":"b6d6e9624ccbb7fa2e51969f5be2c748e969ef4a","_from":".","_npmVersion":"2.14.3","_nodeVersion":"4.1.0","_npmUser":{"name":"jcrugzz","email":"jcrugzz@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"dist":{"shasum":"b6d6e9624ccbb7fa2e51969f5be2c748e969ef4a","size":8925,"noattachment":false,"key":"/understudy/-/understudy-4.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-4.1.0.tgz"},"directories":{},"publish_time":1443142410755,"_cnpm_publish_time":1443142410755,"_hasShrinkwrap":false},"4.0.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["hooks","before","after","flow control"],"version":"4.0.0","main":"index.js","repository":{"type":"git","url":"git@github.com:bmeck/understudy"},"dependencies":{},"devDependencies":{"accounting":"~0.4.1","benchmark":"~1.0.0","microtime":"~1.0.1"},"optionalDependencies":{},"scripts":{"test":"for SCRIPT in test/*.js; do echo Testing $SCRIPT; node $SCRIPT; done"},"engines":{"node":"*"},"license":"MIT","gitHead":"b322e64b9f1efe2a727b9d76df494b06108950d1","bugs":{"url":"https://github.com/bmeck/understudy/issues"},"homepage":"https://github.com/bmeck/understudy","_id":"understudy@4.0.0","_shasum":"8ec2d09d733c4332987eac6def50b01a4b9a71dd","_from":".","_npmVersion":"2.6.1","_nodeVersion":"0.10.36","_npmUser":{"name":"indexzero","email":"charlie.robbins@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"dist":{"shasum":"8ec2d09d733c4332987eac6def50b01a4b9a71dd","size":8103,"noattachment":false,"key":"/understudy/-/understudy-4.0.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-4.0.0.tgz"},"directories":{},"publish_time":1432777452069,"_cnpm_publish_time":1432777452069,"_hasShrinkwrap":false},"3.1.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["hooks","before","after","flow control"],"version":"3.1.0","main":"index.js","repository":{"type":"git","url":"git@github.com:bmeck/understudy"},"dependencies":{},"devDependencies":{"accounting":"~0.4.1","benchmark":"~1.0.0","microtime":"~1.0.1"},"optionalDependencies":{},"scripts":{"test":"for SCRIPT in test/*.js; do echo Testing $SCRIPT; node $SCRIPT; done"},"engines":{"node":"*"},"license":"MIT","gitHead":"5557cad9e07ae5d328fda82b66722d06d355cf1b","bugs":{"url":"https://github.com/bmeck/understudy/issues"},"homepage":"https://github.com/bmeck/understudy","_id":"understudy@3.1.0","_shasum":"84df77edd26bc69e1cb1b695d7751942fb787f73","_from":".","_npmVersion":"2.6.1","_nodeVersion":"0.10.36","_npmUser":{"name":"indexzero","email":"charlie.robbins@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"dist":{"shasum":"84df77edd26bc69e1cb1b695d7751942fb787f73","size":7503,"noattachment":false,"key":"/understudy/-/understudy-3.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-3.1.0.tgz"},"directories":{},"publish_time":1427395535836,"_cnpm_publish_time":1427395535836,"_hasShrinkwrap":false},"3.0.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["hooks","before","after","flow control"],"version":"3.0.0","main":"index.js","repository":{"type":"git","url":"git@github.com:bmeck/node-understudy"},"dependencies":{},"devDependencies":{},"optionalDependencies":{},"scripts":{"test":"for SCRIPT in test/*.js; do echo Testing $SCRIPT; node $SCRIPT; done"},"engines":{"node":"*"},"license":"MIT","gitHead":"ba673222dbf17bc64769b2986fe30ec861ae22fb","bugs":{"url":"https://github.com/bmeck/node-understudy/issues"},"homepage":"https://github.com/bmeck/node-understudy","_id":"understudy@3.0.0","_shasum":"9a02f7f41fb79912041a9786a5747a1cd2c9230d","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"jcrugzz","email":"jcrugzz@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"dist":{"shasum":"9a02f7f41fb79912041a9786a5747a1cd2c9230d","size":4784,"noattachment":false,"key":"/understudy/-/understudy-3.0.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-3.0.0.tgz"},"directories":{},"publish_time":1418253928023,"_cnpm_publish_time":1418253928023,"_hasShrinkwrap":false},"2.0.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["hooks","before","after","flow control"],"version":"2.0.0","main":"index.js","repository":{"type":"git","url":"git@github.com:bmeck/node-understudy"},"dependencies":{},"devDependencies":{},"optionalDependencies":{},"scripts":{"test":"for SCRIPT in test/*.js; do echo Testing $SCRIPT; node $SCRIPT; done"},"engines":{"node":"*"},"gitHead":"db4e59da2a33037b7ad7c0cb0667feab5e055916","bugs":{"url":"https://github.com/bmeck/node-understudy/issues"},"homepage":"https://github.com/bmeck/node-understudy","_id":"understudy@2.0.0","_shasum":"d588c78918139970c469cc587e95945320748ec5","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"jcrugzz","email":"jcrugzz@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"dist":{"shasum":"d588c78918139970c469cc587e95945320748ec5","size":5246,"noattachment":false,"key":"/understudy/-/understudy-2.0.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-2.0.0.tgz"},"directories":{},"publish_time":1418239505675,"_cnpm_publish_time":1418239505675,"_hasShrinkwrap":false},"1.1.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["buzzwords"],"version":"1.1.0","main":"lib/index.js","repository":{"type":"git","url":"git@github.com:bmeck/understudy"},"dependencies":{},"devDependencies":{},"optionalDependencies":{},"scripts":{"test":"for SCRIPT in test/*.js; do echo Testing $SCRIPT; node $SCRIPT; done"},"engines":{"node":"*"},"readmeFilename":"readme.md","_id":"understudy@1.1.0","dist":{"shasum":"051c5248add3cbb39c0e531b2bf1f130c560248e","size":4841,"noattachment":false,"key":"/understudy/-/understudy-1.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-1.1.0.tgz"},"_npmVersion":"1.1.70","_npmUser":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1357898836085,"_cnpm_publish_time":1357898836085,"_hasShrinkwrap":false},"1.0.1":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["buzzwords"],"version":"1.0.1","main":"lib/index.js","dependencies":{},"devDependencies":{},"optionalDependencies":{},"engines":{"node":"*"},"_id":"understudy@1.0.1","dist":{"shasum":"31606cf48d8567fbacfa3285e1cb0e18c4d9ad3e","size":4120,"noattachment":false,"key":"/understudy/-/understudy-1.0.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-1.0.1.tgz"},"_npmVersion":"1.1.59","_npmUser":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1350256448894,"_cnpm_publish_time":1350256448894,"_hasShrinkwrap":false},"1.0.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","tags":["buzzwords"],"version":"1.0.0","main":"lib/index.js","dependencies":{},"devDependencies":{},"optionalDependencies":{},"engines":{"node":"*"},"_id":"understudy@1.0.0","dist":{"shasum":"7059d9b828cd137009f11b68e5ed93b03231af40","size":4244,"noattachment":false,"key":"/understudy/-/understudy-1.0.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-1.0.0.tgz"},"_npmVersion":"1.1.59","_npmUser":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1350142391202,"_cnpm_publish_time":1350142391202,"_hasShrinkwrap":false},"0.1.1":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","version":"0.1.1","main":"lib/index.js","dependencies":{},"devDependencies":{},"optionalDependencies":{},"engines":{"node":"*"},"_id":"understudy@0.1.1","dist":{"shasum":"65619ef0c9efa916586cec86ac979c11be4de0d9","size":3970,"noattachment":false,"key":"/understudy/-/understudy-0.1.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-0.1.1.tgz"},"_npmVersion":"1.1.59","_npmUser":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1346422739628,"_cnpm_publish_time":1346422739628,"_hasShrinkwrap":false},"0.1.0":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","version":"0.1.0","main":"lib/index.js","dependencies":{},"devDependencies":{},"optionalDependencies":{},"engines":{"node":"*"},"_id":"understudy@0.1.0","dist":{"shasum":"993dbce4d0c5a28b0f60de76deaccdbdc95616de","size":3971,"noattachment":false,"key":"/understudy/-/understudy-0.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-0.1.0.tgz"},"_npmVersion":"1.1.59","_npmUser":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1345751885239,"_cnpm_publish_time":1345751885239,"_hasShrinkwrap":false},"0.0.2":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","version":"0.0.2","main":"lib/index.js","dependencies":{},"devDependencies":{},"optionalDependencies":{},"engines":{"node":"*"},"_id":"understudy@0.0.2","dist":{"shasum":"3609fb6ef418805ce3c317b0bef5df2cddec008b","size":4101,"noattachment":false,"key":"/understudy/-/understudy-0.0.2.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-0.0.2.tgz"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1344543985905,"_cnpm_publish_time":1344543985905,"_hasShrinkwrap":false},"0.0.1":{"author":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"name":"understudy","description":"action interceptor for dynamic extensible systems","version":"0.0.1","main":"lib/index.js","dependencies":{},"devDependencies":{},"optionalDependencies":{},"engines":{"node":"*"},"_id":"understudy@0.0.1","dist":{"shasum":"dc6c87d664cc7c900f6a8a3a43bba86d11eff64a","size":2578,"noattachment":false,"key":"/understudy/-/understudy-0.0.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/understudy/download/understudy-0.0.1.tgz"},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"},{"name":"jcrugzz","email":"jcrugzz@gmail.com"}],"directories":{},"publish_time":1344109131747,"_cnpm_publish_time":1344109131747,"_hasShrinkwrap":false}},"readme":"# Understudy\n\nA means to provide interceptors (i.e. hooks) when performing asynchronous actions.\n\n* **All hooks are asynchronous**\n* * All logic done by `perform`-based actions is asynchronous.\n* * Error first helpers.\n* * Fail fast.\n* **Consistency in arguments provided to hooks & actions**\n* * Hooks can only mutate arguments that they are passed, not the number of arguments.\n* * Use of callback arguments to pass information removes focus on return values to mitigate this potentially odd behavior.\n* **Opt-in behavior.**\n* * Only calls to `.perform(action ...` enable hooking.\n\n![](assets/flow.png)\n\nBy depending on `understudy` you are exposed to four methods: `perform`, `before`, `after` and `waterfall`\n\n#### `.perform(action, arg0, /* arg1, ... */, work, callback)`\n\nThis is the core API for invoking hooks provided by `Understudy`. Each call to `perform` for the same `action` should have a consistent argument signature because this is what will be expected by each of the before and after hooks for the `action`. The overall flow control is:\n\n1. Call all `before` hooks for `action`.\n2. Call `work` function for `action`.\n3. Call all `after hooks for `action`.\n4. Call `callback` with results from `work` function.\n\n#### `.before(action, arg0, /* arg1, ... */, next)`\n\nCalled before the `work` function is executed in perform with _exactly_ the arguments passed to `.perform`. Nothing passed to `next` have an impact on the flow control above **except any error is supplied short-circuits execution to the callback.**\n\n#### `.after(action, arg0, /* arg1, ... */, next)`\n\nCalled after the `work` function is executed in perform with _exactly_ the\narguments passed to `.perform`. Nothing passed to `next` have an impact on the\nflow control above **except any error is supplied short-circuits execution to\nthe callback.** \n\nWhile the above statement is true when using `.perform`, `after` hooks acquire a\n`waterfall` like behavior with `.waterfall` where the result of work function\ngets passed to the `after` hooks. Each after hook is then able to mutate the\narguments passed to the next one. Strongly discouraged to change number of\narguments for your user's sanity.\n\n#### `.waterfall(action, arg0, /* arg1, ... */, work, callback)`\n\nThis is a slightly different `perform` that is very useful for when you have to\nmodify state received from a function in a sequence of configurable hooks.\n\n1. Call all `before` hooks for `action`.\n2. Call `work` function for `action`.\n3. Call all `after hooks for `action` with the result returned from the `work`\n   function.\n4. Call `callback` with results from the `after` hooks execution (if any) and\n   otherwise the results from the `work` function.\n\n\n## Real-world Usage\n\nLet's consider a real-world application with two interceptable actions:\n\n- `start`: Application has started\n- `http:request`: Application has received an incoming HTTP request.\n\nWe could easily implement this `App` behavior in `Understudy`:\n\n``` js\nvar Understudy = require('understudy');\n\nvar App = module.exports = function App() {\n  Understudy.call(this);\n};\n\n//\n// Starts the application after running before and\n// after hooks.\n//\nApp.prototype.start = function (options, callback) {\n  this.perform('start', options, function (next) {\n    //\n    // Here, `options` may have been mutated from the execution\n    // of the before hooks.\n    // ...\n    // Do some other async thing\n    // ...\n    // These arguments are passed to the final callback unless\n    // short-circuited by an error here, or in an after hook.\n    //\n    next(null, options);\n  }, callback);\n};\n\nApp.prototype.handle = function (req, res) {\n  req.times = {\n    start: process.hrtime()\n  };\n\n  this.perform('http:request', req, res, function (next) {\n    req.times.middle = process.hrtime();\n    req.times.begin  = process.hrtime(req.times.start);\n    next();\n  }, function (err) {\n    if (err) {\n      //\n      // Do some error handling.\n      //\n    }\n\n    req.times.total = process.hrtime(req.times.start);\n    req.times.after = process.hrtime(req.times.middle);\n    console.log([\n      'Total time: %s',\n      '  Before hooks: %s',\n      '  After  hooks: %s'\n    ].join('\\n'), format(req.times.total), format(req.times.begin), format(req.times.after));\n\n    res.end();\n  });\n}\n\n//\n// Now we consume a new app with hooks.\n//\nvar http = require('http');\n\nvar app = new App();\napp.before('start', function (options, next) {\n  var server = options.server = http.createServer(function (req, res) {\n    app.handle(req, res);\n  });\n\n  server.listen(options.port, next);\n});\n\napp.after('start', function (options, next) {\n  console.log('App started on %s', options.port);\n});\n\napp.before('http:request', function (req, res, next) {\n  //\n  // Do something asynchronous.\n  //\n  next();\n});\n\napp.start({ port: 8080 }, function () {\n  console.log('ok');\n});\n\n//\n// Format process.hrtime()\n//\nfunction format(s) {\n  return (s[0] * 1e3 + s[1] / 1e6) / 1e3;\n}\n```\n\n## More Examples\n\nSee [the example directory](/example)\n\n### Error handling when **no callback is provided**\n\nEach `before` and `after` hook can provide an optional error to short-circuit evaluation of the flow that would normally follow it. This error will be provided to your `callback`, when supplied. In the event that you DO NOT provide a `callback` and a `before`, `after` or `work` function responds with an `Error` _IT WILL BE IGNORED AND FLOW WILL CONTINUE._ e.g.\n\n``` js\nvar Understudy = require('understudy');\nvar actor = new Understudy();\n\nactor.before('always', function (next) {\n  next(new Error('I always fail'));\n});\n\nactor.after('always', function (next) {\n  console.log('I always get called. NO MATTER WHAT');\n  console.log('BUT, only when no callback is supplied.');\n  next(new Error('Another swallowed error'));\n});\n\nactor.perform('always', function (done) {\n  done(new Error('Errors are ignored here too.'));\n});\n```\n\nIn other words (as in the above example): if you do not supply a callback to your `.perform` then `understudy` will consider all of your `before`, `after` and `work` functions as **\"fire and forget\".**\n\n##### LICENSE: MIT\n##### Author: [Bradley Meck](https://github.com/bmeck)\n##### Contributors: [Jarrett Cruger](https://github.com/jcrugzz), [Charlie Robbins](https://github.com/indexzero)\n","_attachments":{},"homepage":"https://github.com/bmeck/understudy#readme","bugs":{"url":"https://github.com/bmeck/understudy/issues"},"license":"MIT"}