function partial(view, options){

  var collection;
  var object;
  var locals;
  var name;

  // parse options
  if( options ){
    // collection
    if( options.collection ){
      collection = options.collection;
      delete options.collection;
    } else if( 'length' in options ){
      collection = options;
      options = {};
    }

    // locals
    if( options.locals ){
      locals = options.locals;
      delete options.locals;
    }

    // object
    if( 'Object' !== options.constructor.name ){
      object = options;
      options = {};
    } else if( options.object !== undefined ){
      object = options.object;
      delete options.object;
    }
  } else {
    options = {};
  }

  // merge locals into options
  if( locals ) {
    options.__proto__ = locals;
  }

  // merge app locals into options
  for(var k in this) {
    options[k] = options[k] || this[k];
  }

  // extract object name from view
  name = options.as || resolveObjectName(view);

  // find view, relative to this filename
  // NOTE -- the original `dirname(options.filename)` stopped working
  // after ejs-locals was inlined, perhaps due to changed in EJS.
  // The `options.absPathToView` value is set above in the
  // main `renderFile` function. -SMG 10/27/2016
  var root = dirname(options.filename);
  var file = lookup(root, view, options);
  var key = file + ':string';
  if( !file ) {
    throw new Error('Could not find partial ' + view);
  }

  // read view
  var source = options.cache
    ? cache[key] || (cache[key] = fs.readFileSync(file, 'utf8'))
    : fs.readFileSync(file, 'utf8');

  // Update the options.filename to point to the current partial,
  // so that relative paths can work in calling nested partials.
  // -SMG 10/27/2016
  options.filename = file;

  // re-bind partial for relative partial paths
  options.partial = partial.bind(options);

  // render partial
  function render(){
    if (object) {
      if ('string' === typeof name) {
        options[name] = object;
      } else if (name === global) {
        // wtf?
        // merge(options, object);
      }
    }
    // TODO Support other templates (but it's sync now...)
    var html = ejs.render(source, options, options);
    return html;
  }

  // Collection support
  if (collection) {
    var len = collection.length;
    var buf = '';
    var keys;
    var prop;
    var val;
    var i;

    if ('number' === typeof len || Array.isArray(collection)) {
      options.collectionLength = len;
      for (i = 0; i < len; ++i) {
        val = collection[i];
        options.firstInCollection = i === 0;
        options.indexInCollection = i;
        options.lastInCollection = i === len - 1;
        object = val;
        buf += render();
      }
    } else {
      keys = Object.keys(collection);
      len = keys.length;
      options.collectionLength = len;
      options.collectionKeys = keys;
      for (i = 0; i < len; ++i) {
        prop = keys[i];
        val = collection[prop];
        options.keyInCollection = prop;
        options.firstInCollection = i === 0;
        options.indexInCollection = i;
        options.lastInCollection = i === len - 1;
        object = val;
        buf += render();
      }
    }

    return buf;
  } else {
    return render();
  }
}
Render `view` partial with the given `options`. Optionally a
callback `fn(err, str)` may be passed instead of writing to
the socket.

Options:

  - `object` Single object with name derived from the view (unless `as` is present)

  - `as` Variable name for each `collection` value, defaults to the view name.
    * as: 'something' will add the `something` local variable
    * as: this will use the collection value as the template context
    * as: global will merge the collection value's properties with `locals`

  - `collection` Array of objects, the name is derived from the view name itself.
    For example _video.html_ will have a object _video_ available to it.

@param  {String} view
@param  {Object|Array} options, collection or object
@return {String}
@api private