function _renderTinyliquid(engine, str, options, cb) {
	  var context = engine.newContext();
	  var k;

	  /**
	   * Note that there's a bug in the library that doesn't allow us to pass
	   * the locals to newContext(), hence looping through the keys:
	   */

	  if (options.locals) {
	    for (k in options.locals) {
	      context.setLocals(k, options.locals[k]);
	    }
	    delete options.locals;
	  }

	  if (options.meta) {
	    context.setLocals('page', options.meta);
	    delete options.meta;
	  }

	  /**
	   * Add any defined filters:
	   */

	  if (options.filters) {
	    for (k in options.filters) {
	      context.setFilter(k, options.filters[k]);
	    }
	    delete options.filters;
	  }

	  /**
	   * Set up a callback for the include directory:
	   */

	  var includeDir = options.includeDir || process.cwd();

	  context.onInclude(function(name, callback) {
	    var extname = path.extname(name) ? '' : '.liquid';
	    var filename = path.resolve(includeDir, name + extname);

	    fs.readFile(filename, {encoding: 'utf8'}, function(err, data) {
	      if (err) return callback(err);
	      callback(null, engine.parse(data));
	    });
	  });
	  delete options.includeDir;

	  /**
	   * The custom tag functions need to have their results pushed back
	   * through the parser, so set up a shim before calling the provided
	   * callback:
	   */

	  var compileOptions = {
	    customTags: {}
	  };

	  if (options.customTags) {
	    var tagFunctions = options.customTags;

	    for (k in options.customTags) {
	      /*Tell jshint there's no problem with having this function in the loop */
	      /*jshint -W083 */
	      compileOptions.customTags[k] = function(context, name, body) {
	        var tpl = tagFunctions[name](body.trim());
	        context.astStack.push(engine.parse(tpl));
	      };
	      /*jshint +W083 */
	    }
	    delete options.customTags;
	  }

	  /**
	   * Now anything left in `options` becomes a local:
	   */

	  for (k in options) {
	    context.setLocals(k, options[k]);
	  }

	  /**
	   * Finally, execute the template:
	   */

	  var tmpl = cache(context) || cache(context, engine.compile(str, compileOptions));
	  tmpl(context, cb);
	}
Note that in order to get filters and custom tags we've had to push
all user-defined locals down into @locals. However, just to make things
backwards-compatible, any property of `options` that is left after
processing and removing `locals`, `meta`, `filters`, `customTags` and
`includeDir` will also become a local.