1 var func = require("../base/functions"), 2 obj = require("../base/object"), 3 Promise = require("../promise").Promise, 4 define = require("../define").define; 5 6 7 var Middleware = define(null, { 8 instance : { 9 /** @lends comb.plugins.Middleware.prototype */ 10 11 12 __hooks : {pre : {}, post : {}}, 13 14 15 /** 16 * @class Plugin to enable middleware on a class 17 * 18 * @example 19 * 20 * var Mammal = define(comb.plugins.Middleware, { 21 * instance : { 22 * 23 * constructor: function(options) { 24 * options = options || {}; 25 * this.super(arguments); 26 * this._type = options.type || "mammal"; 27 * }, 28 * 29 * speak : function() { 30 * var ret = new comb.Promise(); 31 * this._hook("pre", "speak") 32 * .then(comb.hitch(this, "_hook", "post", "speak"), hitch(ret, "errback")) 33 * .then(comb.hitch(ret, "callback"), comb.hitch(ret, "errback")); 34 * return ret; 35 * } 36 * } 37 *}); 38 * 39 * Mammal.pre('speak', function(next){ 40 * //do something meaningful 41 * next(); 42 * }); 43 * var m = new Mammal({color : "gold"}); 44 * m.speak(); 45 * 46 * @constructs 47 */ 48 constructor : function() { 49 this.__hooks = obj.merge({}, this.__hooks); 50 this.super(arguments); 51 }, 52 53 /** 54 * <p>Protected!</p> 55 * 56 * <p>Call to initiate middleware for the topic</p> 57 * <p><b>NOTE:</b> this function takes a variable number of arguments 58 * whatever comes after the op param will be passed into 59 * the listening function, with the last argument to the listenting 60 * function being the next function</p> 61 * 62 * 63 * @public 64 * @param {"pre"|"post"} state the state in which the hook should be called 65 * @param {String} op the operation that is being acted upong 66 * @param args arguments to be passed into the listening functions. 67 * @returns {comb.Promise} a promise to use after middleware chain completes 68 * 69 */ 70 _hook : function(state, op, args) { 71 args = args || []; 72 var promise = new Promise(); 73 var funcs, length; 74 if (this.__hooks[state] && (funcs = this.__hooks[state][op]) != null && (length = funcs.length) > 0) { 75 var count = 0; 76 var next = func.hitch(this, function() { 77 //if Ive looped through all of them callback 78 if (count == length) { 79 promise.callback(); 80 } else { 81 //call next 82 var nextArgs = args.slice(0); 83 nextArgs.unshift(next); 84 funcs[count++].apply(this, nextArgs); 85 } 86 }); 87 next(); 88 } else { 89 promise.callback(); 90 } 91 return promise; 92 }, 93 94 /** 95 * Use to listen to before an event occurred i.e. pre save 96 * 97 * <b>NOTE:</b></br> 98 * <ul> 99 * <li>You must call next in order for the middleware chain to complete</li> 100 * <li>This connects to events on the instance of an object, not all instances!</li> 101 * <li>Hooks are called in the order they are received!</li> 102 * <li> When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li> 103 * </ul> 104 * 105 * @example 106 * instance.pre("save", function(args,...., next){ 107 * //do something... 108 * //you have to call next!!!!! 109 * next(); 110 * }); 111 * 112 * */ 113 pre : function(fun, callback) { 114 var hook = this.__hooks.pre[fun]; 115 if (!hook) { 116 hook = this.__hooks.pre[fun] = []; 117 } 118 hook.push(callback); 119 }, 120 121 /** 122 * <p>Use to listen to after an event has occurred i.e. post save</p> 123 * <b>NOTE:</b></br> 124 * <ul> 125 * <li>You must call next in order for the middleware chain to complete</li> 126 * <li>This connects to events on the instance of an object, NOT all instances!</li> 127 * <li>Hooks are called in the order they are received!</li> 128 * <li>When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li> 129 * </ul> 130 * @example 131 * 132 * instance.post("save", function(next){ 133 * //do something... 134 * //you have to call next!!!!! 135 * next(); 136 * }); 137 * */ 138 post : function(fun, callback) { 139 var hook = this.__hooks.post[fun]; 140 //if I havent initialized it create it; 141 if (hook == undefined) { 142 hook = this.__hooks.post[fun] = []; 143 } 144 hook.push(callback); 145 } 146 }, 147 148 static : { 149 /** @lends comb.plugins.Middleware */ 150 151 /** 152 *<p> Use to listen to after an event has occurred i.e. post save</p> 153 * 154 * <b>NOTE:</b></br> 155 * <ul> 156 * <li>You must call next in order for the middleware chain to complete</li> 157 * <li>This connects to events on ALL instances of an object</li> 158 * <li>Hooks are called in the order they are received!</li> 159 * <li>When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li> 160 * </ul> 161 * 162 * @example 163 * Class.pre("save", function(next){ 164 * ... 165 * //you must call next 166 * }); 167 * */ 168 pre : function(name, cb) { 169 var hooks = this.prototype.__hooks; 170 var hook = hooks.pre[name]; 171 if (!hook) { 172 hook = hooks.pre[name] = []; 173 } 174 hook.push(cb); 175 }, 176 177 /** 178 *<p>Use to listen to after an event has occurred i.e. post save</p> 179 * 180 *<b>NOTE:</b></br> 181 * <ul> 182 * <li>You must call next in order for the middleware chain to complete</li> 183 * <li>This connects to events on ALL instances of an object</li> 184 * <li>Hooks are called in the order they are received!</li> 185 * <li>When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li> 186 * </ul> 187 * 188 * @example 189 * Class.post("save", function(next){ 190 * ... 191 * //you must call next 192 * }); 193 * */ 194 post : function(name, cb) { 195 var hooks = this.prototype.__hooks; 196 var hook = hooks.post[name]; 197 if (!hook) { 198 hook = hooks.post[name] = []; 199 } 200 hook.push(cb); 201 } 202 } 203 204 }); 205 206 module.exports = exports = Middleware;