timshadel
2/26/2013 - 3:40 AM

the-crafty.js

/**
 * Original function
 */
function original(a, b) {
  console.log('I am original. I return a + b.');
  return a + b;
}

//==============

/** Middleware #1 */
function subber(inv, next) {
  return function sub(a, b) {
    console.log('Substituting return value with ret - 1.');
    return inv.invoke(next) - 1;
  };
}

/** Middleware #2 */
function logger(inv, next) {
  return function logit(a, b) {
    console.log('Logging values ' + a + ', ' + b);
    return inv.invoke(next);
  };
}

//========

var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
function createProxy(func) {
  // Retain the function length:
  var p;
  if (func.length) {
    eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) + ") { var inv = invocation(this, [].slice.call(arguments)); return inv.invoke(p.middleware(inv, func)); });");
  }
  else {
    p = function proxy() {
      var inv = invocation(this, [].slice.call(arguments));
      return inv.invoke(p.middleware(inv, func));
    };
  }
  p.stack = [];
  p.use = use;
  p.middleware = middleware;
  return p;
}

function use(item) {
  this.stack = this.stack || [];
  this.stack.push(item);
}

function middleware(inv, start) {
  var next = start;
  // assemble stack bottom-to-top
  for (var i = this.stack.length - 1; i >= 0; i--) {
    next = this.stack[i](inv, next);
  }
  return next;
}

//===============

function invocation(thisValue, args) {
  return {
    thisValue: thisValue,
    args: args,
    invoke: invoke
  };
}

function invoke(next) {
  return next.apply(this.thisValue, this.args);
}

//===============


var pox = createProxy(original);
pox.use(logger);
pox.use(subber);
console.log('Final', pox(3, 4));