/** * Module Dependencies */ var noop = function(){}; var co = require('co'); /** * Export `wrap-fn` */ module.exports = wrap; /** * Wrap a function to support * sync, async, and gen functions. * * @param {Function} fn * @param {Function} done * @return {Function} * @api public */ function wrap(fn, done) { done = once(done || noop); return function() { // prevents arguments leakage // see https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments var i = arguments.length; var args = new Array(i); while (i--) args[i] = arguments[i]; var ctx = this; // done if (!fn) { return done.apply(ctx, [null].concat(args)); } // async if (fn.length > args.length) { // NOTE: this only handles uncaught synchronous errors try { return fn.apply(ctx, args.concat(done)); } catch (e) { return done(e); } } // generator if (generator(fn)) { return co(fn).apply(ctx, args.concat(done)); } // sync return sync(fn, done).apply(ctx, args); } } /** * Wrap a synchronous function execution. * * @param {Function} fn * @param {Function} done * @return {Function} * @api private */ function sync(fn, done) { return function () { var ret; try { ret = fn.apply(this, arguments); } catch (err) { return done(err); } if (promise(ret)) { ret.then(function (value) { done(null, value); }, done); } else { ret instanceof Error ? done(ret) : done(null, ret); } } } /** * Is `value` a generator? * * @param {Mixed} value * @return {Boolean} * @api private */ function generator(value) { return value && value.constructor && 'GeneratorFunction' == value.constructor.name; } /** * Is `value` a promise? * * @param {Mixed} value * @return {Boolean} * @api private */ function promise(value) { return value && 'function' == typeof value.then; } /** * Once */ function once(fn) { return function() { var ret = fn.apply(this, arguments); fn = noop; return ret; }; }