text: var path = require('path');
  
  function Gently() {
    this.expectations = [];
    this.hijacked = {};
  
    var self = this;
    process.addListener('exit', function() {
      self.verify('process exit');
    });
  };
  module.exports = Gently;
  
  Gently.prototype.stub = function(location, exportsName) {
    function Stub() {
      return Stub['new'].apply(this, arguments);
    };
  
    Stub['new'] = function () {};
  
    var stubName = 'require('+JSON.stringify(location)+')';
    if (exportsName) {
      stubName += '.'+exportsName;
    }
  
    Stub.prototype.toString = Stub.toString = function() {
      return stubName;
    };
  
    var exports = this.hijacked[location] || {};
    if (exportsName) {
      exports[exportsName] = Stub;
    } else {
      exports = Stub;
    }
  
    this.hijacked[location] = exports;
    return Stub;
  };
  
  Gently.prototype.hijack = function(realRequire) {
    var self = this;
    return function(location) {
      return self.hijacked[location] = (self.hijacked[location])
        ? self.hijacked[location]
        : realRequire(location);
    };
  };
  
  Gently.prototype.expect = function(obj, method, count, stubFn) {
    if (typeof obj != 'function' && typeof obj != 'object' && typeof obj != 'number') {
      throw new Error
        ( 'Bad 1st argument for gently.expect(), '
        + 'object, function, or number expected, got: '+(typeof obj)
        );
    } else if (typeof obj == 'function' && (typeof method != 'string')) {
      // expect(stubFn) interface
      stubFn = obj;
      obj = null;
      method = null;
      count = 1;
    } else if (typeof method == 'function') {
      // expect(count, stubFn) interface
      count = obj;
      stubFn = method;
      obj = null;
      method = null;
    } else if (typeof count == 'function') {
      // expect(obj, method, stubFn) interface
      stubFn = count;
      count = 1;
    } else if (count === undefined) {
      // expect(obj, method) interface
      count = 1;
    }
  
    var name = this._name(obj, method, stubFn);
    this.expectations.push({obj: obj, method: method, stubFn: stubFn, name: name, count: count});
  
    var self = this;
    function delegate() {
      return self._stubFn(this, obj, method, name, Array.prototype.slice.call(arguments));
    }
  
    if (!obj) {
      return delegate;
    }
  
    var original = (obj[method])
      ? obj[method]._original || obj[method]
      : undefined;
  
    obj[method] = delegate;
    return obj[method]._original = original;
  };
  
  Gently.prototype.restore = function(obj, method) {
    if (!obj[method] || !obj[method]._original) {
      throw new Error(this._name(obj, method)+' is not gently stubbed');
    }
    obj[method] = obj[method]._original;
  };
  
  Gently.prototype.verify = function(msg) {
    if (!this.expectations.length) {
      return;
    }
  
    var validExpectations = [];
    for (var i = 0, l = this.expectations.length; i < l; i++) {
      var expectation = this.expectations[i];
  
      if (expectation.count > 0) {
        validExpectations.push(expectation);
      }
    }
  
    this.expectations = []; // reset so that no duplicate verification attempts are made
  
    if (!validExpectations.length) {
      return;
    }
  
    var expectation = validExpectations[0];
  
    throw new Error
      ( 'Expected call to '+expectation.name+' did not happen'
      + ( (msg)
          ? ' ('+msg+')'
          : ''
        )
      );
  };
  
  Gently.prototype._stubFn = function(self, obj, method, name, args) {
    var expectation = this.expectations[0], obj, method;
  
    if (!expectation) {
      throw new Error('Unexpected call to '+name+', no call was expected');
    }
  
    if (expectation.obj !== obj || expectation.method !== method) {
      throw new Error('Unexpected call to '+name+', expected call to '+ expectation.name);
    }
  
    expectation.count -= 1;
    if (expectation.count === 0) {
      this.expectations.shift();
  
      // autorestore original if its not a closure
      // and no more expectations on that object
      var has_more_expectations = this.expectations.reduce(function (memo, expectation) {
        return memo || (expectation.obj === obj && expectation.method === method);
      }, false);
      if (obj !== null && method !== null && !has_more_expectations) {
        if (typeof obj[method]._original !== 'undefined') {
          obj[method] = obj[method]._original;
          delete obj[method]._original;
        } else {
          delete obj[method];
        }
      }
    }
  
    if (expectation.stubFn) {
      return expectation.stubFn.apply(self, args);
    }
  };
  
  Gently.prototype._name = function(obj, method, stubFn) {
    if (obj) {
      var objectName = obj.toString();
      if (objectName == '[object Object]' && obj.constructor.name) {
        objectName = '['+obj.constructor.name+']';
      }
      return (objectName)+'.'+method+'()';
    }
  
    if (stubFn.name) {
      return stubFn.name+'()';
    }
  
    return '>> '+stubFn.toString()+' <<';
  };
  