      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;