{"_id":"filterchain","_rev":"5-b2cb5494ba2afc97a6caf69e47ec5a6c","name":"filterchain","description":"perform work before and after an operation","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"author":{"name":"Elijah Insua","email":"tmpvar@gmail.com","url":"http://tmpvar.com"},"name":"filterchain","description":"perform work before and after an operation","version":"0.1.0","homepage":"http://tmpvar.com/project/filterchain","repository":{"type":"git","url":"git://github.com/tmpvar/filterchain.git"},"engines":{"node":"*"},"dependencies":{},"devDependencies":{},"main":"index","scripts":{"test":"node test.js"},"_npmUser":{"name":"tmpvar","email":"tmpvar@gmail.com"},"_id":"filterchain@0.1.0","_engineSupported":true,"_npmVersion":"1.0.104","_nodeVersion":"v0.6.1-pre","_defaultsLoaded":true,"dist":{"shasum":"ccd812afa8c91668382804842ebbbbdfa63dbd7d","tarball":"https://registry.npmjs.org/filterchain/-/filterchain-0.1.0.tgz","integrity":"sha512-Q3NpbHZRkQlivWbSfEklRPfnHD6zbJG7v4hFCFAWBqfEq6+2qzJyHroMqM9WyEN5CfN1Byt+AVQkzrml5oA9Dg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMi2ss4DEUkMtj7zlOei1ih8Tj+nPkknEhoYtjF22n3AIhAOEW7MGrnsPgaQOn91tYsIvzqKXudBPGyJtcJV1qXuw2"}]},"maintainers":[{"name":"tmpvar","email":"tmpvar@gmail.com"}]}},"readme":"# filterchain\n\nPerform work before and/or after an operation.\n\n## Disclaimer\n\nThis is not meant to be used as async flow control, but is merely a way to dynamically upgrade the functionality of getters and setters.\n\nThis also means that errors will not stop the chain of events here, you must specifically cancel the current layer in order to avoid performing the `core` action.\n\n## Learn by example\n\nThe basic operation of filterchain goes something like this:\n\n```javascript\nvar chain = require('filterchain').createChain([\n  function(data, next, cancel) {\n    // forward to the next layer\n    next('override');\n  } // you can add more functions here\n]);\n\nchain('initial data', function(data) {\n  console.log(data); // outputs 'override'\n});\n\n```\n\nand here is the outer api\n\n\n```javascript\n// Create a chain with no layers and no core function\nvar chain = filterchain.createChain(/* layers */, /* core */);\n```\n\nWhere `chain` is a function that accepts an optional `data` and a optional `callback` with the arguments `errors` and `data`.  The `callback` is called after the filter chain has been executed.\n\n`data` can be any javascript value\n\n```javascript\n// Excerpt to show what the chain callback looks like.\n// `errors` is either null (no errors) or an array\nchain('some optional data', function(errors, data) {})\n```\n\nAnd `layers` is an array of functions\n\n```javascript\nvar chain = require('filterchain').createChain([\n  function(data, next, cancel) {\n\n    // just forward the data, this is basically a no-op\n    next(data);\n  }\n], core);\n```\n`next` is a function with the signature `next(data[, bubbleFunction])`\n`cancel` is a function with the signature `cancel([error])`\n\nAnd `core` is the function that will be run after the capture phase but before the bubble phase. The `core` method should accept `data` and `fn`.\n\n```javascript\nvar chain = require('filterchain').createChain([], function(data, fn) {\n\n  // send the incoming back the same way it came\n  if (data === true) {\n    fn(null, 'tricked ya!')\n  } else {\n    fn('error!')\n  }\n\n});\n\nchain(true, function(errors, data) {\n  console.log(data); // outputs: 'tricked ya!'\n});\n\nchain(false, function(errors, data) {\n  console.log(errors[0]); // outputs: 'error!'\n  console.log(data); // outputs: undefined\n});\n\n```\n\n## What (else) does it do?\n\nIn a sense, filter chains are similar to onions. Passing data into the outer husk causes it to flow down through each layer toward the core. Each function (aka: layer) along the path as a chance to either manipulate or validate the data before forwarding it onto the next layer or canceling it.\n\n### Manipulate and forward data\n\n```javascript\nvar chain = require('filterchain').createChain([\n  function(data, next, cancel) {\n\n    // ignore the incoming data and forward something more\n    // to our liking\n    next('pass this along');\n  }\n]);\n\nchain(function(errors, data) {\n  console.log(data);  // outputs 'pass this along'\n});\n```\n\n### Cancel + bubble\n\nCancelling causes the flow of the chain to be reversed immediately.\n\n```javascript\nvar chain = require('filterchain').createChain([\n  function(data, next, cancel) {\n\n    // the first argument to cancel is an optional error.  The error\n    // will be collected and sent to the final callback for processing\n    cancel('fat chance');\n  }\n], function(data, fn) {\n\n  // this is never called\n  fn('some other thing');\n});\n\nchain(function(errors, data) {\n  console.log(errors[0]); // ouputs 'fat chance'\n});\n\n```\n\n### Post process data\n\nPassing a function as the second argument to `next` will cause the filter chain to call that method during the bubble phase\n\n```javascript\nvar fc = require('filterchain');\nvar chain = fc.createChain([\n  function(data, next, cancel) {\n    next(data, function(data, done) {\n      // You can return a value here or perform\n      // an async operation and send the result through done\n      done(null, data + ' + post processing')\n    });\n  }\n]);\n\nchain('initial value', function(errors, data) {\n  console.log(data); // outputs 'initial value + post processing'\n});\n\n```\n\nThe first argument to `done` is an error and the second is the data that will be bubbled back out to the outer husk of the filter chain.\n\n### Compose filter chains\n\n```javascript\n\nvar inner = require('filterchain').createChain([\n  function(data, next) {\n    next(data + ' (inner capture ->', function(outgoingData, done) {\n      done(null, outgoingData + ' inner bubble)');\n    });\n  }\n], function(data, fn) {\n  fn(null, data + '[inner core] ->')\n})\n\nvar outer = require('filterchain').createChain([\n  function(data, next) {\n    next(data + 'outer capture ->', function(outgoingData, done) {\n      done(null, outgoingData + ' outer bubble');\n    });\n  },\n  inner, // add a filterchain into the filterchain.. oh my!\n\n], function(data, fn) {\n  fn(null, data + ' [outer core] ->')\n});\n\nouter('run: ', function(errors, data) {\n  // outputs: 'run: outer capture -> (inner capture ->[inner core] -> inner bubble) [outer core] -> outer bubble'\n  console.log(data);\n})\n```\n\n## Contrived Use Cases\n\n### User creation example\n\n```javascript\nvar createUser = require('filterchain').createChain([\n  function unique(username, next, cancel) {\n    db.exists({ username : username }, function(err, result) {\n      if (err) {\n        cancel(err);\n      } else if (result === true) {\n        cancel('sorry, that username already exists')\n      } else {\n        next(username);\n      }\n    });\n  }\n], function(data, fn) {\n  db.save({ username : username }, fn);\n});\n\ncreateUser('tmpvar', function(errors, data) {\n  console.log(errors[0]); // outputs 'sorry, that username already exists'\n});\n\ncreateUser('tmpvar-not-taken', function(errors, data) {\n  console.log(errors); // outputs null\n  console.log(data); // outputs '{ _id : 2, username: \"tmpvar-not-taken\" }'\n});\n```\n\n### Calculated attriutes in backbone\n\n__note__: this is purely conceptual\n\n```javascript\n\nvar Rectangle = Backbone.Model.extend({\n  initialize : function() {\n    this.calculatedAttributes = {\n      area : filterchain.createChain(function(data, fn) {\n        // Not only is the return value calculated, but you\n        // can add filters and post processing to your values.\n        //\n        // Simply add filters to the chain during creation or\n        // chain.layers.push(function(data, next, cancel) {});\n\n        fn(data.width * data.height);\n      });\n    }\n  },\n  get : function(key) {\n    var that = this;\n\n    if (this.calculatedAttributes[key]) {\n      this.calculatedAttributes[key](that.toJSON(), function(result) {\n        value = result;\n      });\n\n      if (typeof value !== 'undefined') {\n        return value;\n      }\n    }\n\n    // fall back to the default backbone behavior\n    return Backbone.Model.prototype.get.call(this, key);\n  }\n});\n\nvar a = new Rectangle({ x: 10, y : 4 });\nconsole.log(a.get('area')); // outputs '40'\n\n```\n\n## Install\n\n### Node.js\n\n    npm install filterchain\n\n### Browser\n\nworks with a plain ol' script tag and access it via `window.createChain`\n","maintainers":[{"name":"tmpvar","email":"tmpvar@gmail.com"}],"time":{"modified":"2022-06-18T00:49:50.913Z","created":"2011-11-18T03:55:30.385Z","0.1.0":"2011-11-18T03:55:32.526Z"},"author":{"name":"Elijah Insua","email":"tmpvar@gmail.com","url":"http://tmpvar.com"},"repository":{"type":"git","url":"git://github.com/tmpvar/filterchain.git"}}