From 3f77b82ed00dc1d71976de76bd4f8dfb6e885f2a Mon Sep 17 00:00:00 2001
From: machty <machty@gmail.com>
Date: Thu, 10 Dec 2015 12:25:20 -0500
Subject: [PATCH] Remove stringParams and trackIds mode

Closes #1145
---
 lib/handlebars/compiler/compiler.js           |  46 +---
 .../compiler/javascript-compiler.js           | 106 +-------
 .../helpers/block-helper-missing.js           |  12 +-
 lib/handlebars/helpers/each.js                |  15 +-
 lib/handlebars/helpers/with.js                |   8 +-
 lib/handlebars/runtime.js                     |   6 -
 lib/handlebars/utils.js                       |   8 -
 print-script                                  |   1 -
 spec/string-params.js                         | 176 -------------
 spec/subexpressions.js                        |  44 ----
 spec/track-ids.js                             | 237 ------------------
 11 files changed, 8 insertions(+), 651 deletions(-)
 delete mode 100644 spec/string-params.js
 delete mode 100644 spec/track-ids.js

diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 987d0d45..9fa9dd88 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -49,8 +49,6 @@ Compiler.prototype = {
     this.opcodes = [];
     this.children = [];
     this.options = options;
-    this.stringParams = options.stringParams;
-    this.trackIds = options.trackIds;
 
     options.blockParams = options.blockParams || [];
 
@@ -393,49 +391,7 @@ Compiler.prototype = {
   },
 
   pushParam: function(val) {
-    let value = val.value != null ? val.value : val.original || '';
-
-    if (this.stringParams) {
-      if (value.replace) {
-        value = value
-            .replace(/^(\.?\.\/)*/g, '')
-            .replace(/\//g, '.');
-      }
-
-      if (val.depth) {
-        this.addDepth(val.depth);
-      }
-      this.opcode('getContext', val.depth || 0);
-      this.opcode('pushStringParam', value, val.type);
-
-      if (val.type === 'SubExpression') {
-        // SubExpressions get evaluated and passed in
-        // in string params mode.
-        this.accept(val);
-      }
-    } else {
-      if (this.trackIds) {
-        let blockParamIndex;
-        if (val.parts && !AST.helpers.scopedId(val) && !val.depth) {
-           blockParamIndex = this.blockParamIndex(val.parts[0]);
-        }
-        if (blockParamIndex) {
-          let blockParamChild = val.parts.slice(1).join('.');
-          this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild);
-        } else {
-          value = val.original || value;
-          if (value.replace) {
-            value = value
-                .replace(/^this(?:\.|$)/, '')
-                .replace(/^\.\//, '')
-                .replace(/^\.$/, '');
-          }
-
-          this.opcode('pushId', val.type, value);
-        }
-      }
-      this.accept(val);
-    }
+    this.accept(val);
   },
 
   setupFullMustacheParams: function(sexpr, program, inverse, omitEmpty) {
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js
index 97939df0..1708032c 100644
--- a/lib/handlebars/compiler/javascript-compiler.js
+++ b/lib/handlebars/compiler/javascript-compiler.js
@@ -57,8 +57,6 @@ JavaScriptCompiler.prototype = {
   compile: function(environment, options, context, asObject) {
     this.environment = environment;
     this.options = options;
-    this.stringParams = this.options.stringParams;
-    this.trackIds = this.options.trackIds;
     this.precompile = !asObject;
 
     this.name = this.environment.name;
@@ -498,37 +496,7 @@ JavaScriptCompiler.prototype = {
     this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']);
   },
 
-  // [pushStringParam]
-  //
-  // On stack, before: ...
-  // On stack, after: string, currentContext, ...
-  //
-  // This opcode is designed for use in string mode, which
-  // provides the string value of a parameter along with its
-  // depth rather than resolving it immediately.
-  pushStringParam: function(string, type) {
-    this.pushContext();
-    this.pushString(type);
-
-    // If it's a subexpression, the string result
-    // will be pushed after this opcode.
-    if (type !== 'SubExpression') {
-      if (typeof string === 'string') {
-        this.pushString(string);
-      } else {
-        this.pushStackLiteral(string);
-      }
-    }
-  },
-
   emptyHash: function(omitEmpty) {
-    if (this.trackIds) {
-      this.push('{}'); // hashIds
-    }
-    if (this.stringParams) {
-      this.push('{}'); // hashContexts
-      this.push('{}'); // hashTypes
-    }
     this.pushStackLiteral(omitEmpty ? 'undefined' : '{}');
   },
   pushHash: function() {
@@ -541,14 +509,6 @@ JavaScriptCompiler.prototype = {
     let hash = this.hash;
     this.hash = this.hashes.pop();
 
-    if (this.trackIds) {
-      this.push(this.objectLiteral(hash.ids));
-    }
-    if (this.stringParams) {
-      this.push(this.objectLiteral(hash.contexts));
-      this.push(this.objectLiteral(hash.types));
-    }
-
     this.push(this.objectLiteral(hash.values));
   },
 
@@ -727,44 +687,7 @@ JavaScriptCompiler.prototype = {
   //
   // Pops a value off the stack and assigns it to the current hash
   assignToHash: function(key) {
-    let value = this.popStack(),
-        context,
-        type,
-        id;
-
-    if (this.trackIds) {
-      id = this.popStack();
-    }
-    if (this.stringParams) {
-      type = this.popStack();
-      context = this.popStack();
-    }
-
-    let hash = this.hash;
-    if (context) {
-      hash.contexts[key] = context;
-    }
-    if (type) {
-      hash.types[key] = type;
-    }
-    if (id) {
-      hash.ids[key] = id;
-    }
-    hash.values[key] = value;
-  },
-
-  pushId: function(type, name, child) {
-    if (type === 'BlockParam') {
-      this.pushStackLiteral(
-          'blockParams[' + name[0] + '].path[' + name[1] + ']'
-          + (child ? ' + ' + JSON.stringify('.' + child) : ''));
-    } else if (type === 'PathExpression') {
-      this.pushString(name);
-    } else if (type === 'SubExpression') {
-      this.pushStackLiteral('true');
-    } else {
-      this.pushStackLiteral('null');
-    }
+    this.hash.values[key] = this.popStack();
   },
 
   // HELPERS
@@ -997,9 +920,6 @@ JavaScriptCompiler.prototype = {
 
   setupParams: function(helper, paramSize, params) {
     let options = {},
-        contexts = [],
-        types = [],
-        ids = [],
         objectArgs = !params,
         param;
 
@@ -1010,14 +930,6 @@ JavaScriptCompiler.prototype = {
     options.name = this.quotedString(helper);
     options.hash = this.popStack();
 
-    if (this.trackIds) {
-      options.hashIds = this.popStack();
-    }
-    if (this.stringParams) {
-      options.hashTypes = this.popStack();
-      options.hashContexts = this.popStack();
-    }
-
     let inverse = this.popStack(),
         program = this.popStack();
 
@@ -1034,28 +946,12 @@ JavaScriptCompiler.prototype = {
     while (i--) {
       param = this.popStack();
       params[i] = param;
-
-      if (this.trackIds) {
-        ids[i] = this.popStack();
-      }
-      if (this.stringParams) {
-        types[i] = this.popStack();
-        contexts[i] = this.popStack();
-      }
     }
 
     if (objectArgs) {
       options.args = this.source.generateArray(params);
     }
 
-    if (this.trackIds) {
-      options.ids = this.source.generateArray(ids);
-    }
-    if (this.stringParams) {
-      options.types = this.source.generateArray(types);
-      options.contexts = this.source.generateArray(contexts);
-    }
-
     if (this.options.data) {
       options.data = 'data';
     }
diff --git a/lib/handlebars/helpers/block-helper-missing.js b/lib/handlebars/helpers/block-helper-missing.js
index 6639ddb9..e6d162f7 100644
--- a/lib/handlebars/helpers/block-helper-missing.js
+++ b/lib/handlebars/helpers/block-helper-missing.js
@@ -1,4 +1,4 @@
-import {appendContextPath, createFrame, isArray} from '../utils';
+import {isArray} from '../utils';
 
 export default function(instance) {
   instance.registerHelper('blockHelperMissing', function(context, options) {
@@ -11,21 +11,11 @@ export default function(instance) {
       return inverse(this);
     } else if (isArray(context)) {
       if (context.length > 0) {
-        if (options.ids) {
-          options.ids = [options.name];
-        }
-
         return instance.helpers.each(context, options);
       } else {
         return inverse(this);
       }
     } else {
-      if (options.data && options.ids) {
-        let data = createFrame(options.data);
-        data.contextPath = appendContextPath(options.data.contextPath, options.name);
-        options = {data: data};
-      }
-
       return fn(context, options);
     }
   });
diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js
index fb11903c..914928d6 100644
--- a/lib/handlebars/helpers/each.js
+++ b/lib/handlebars/helpers/each.js
@@ -1,4 +1,4 @@
-import {appendContextPath, blockParams, createFrame, isArray, isFunction} from '../utils';
+import {createFrame, isArray, isFunction} from '../utils';
 import Exception from '../exception';
 
 export default function(instance) {
@@ -11,12 +11,7 @@ export default function(instance) {
         inverse = options.inverse,
         i = 0,
         ret = '',
-        data,
-        contextPath;
-
-    if (options.data && options.ids) {
-      contextPath = appendContextPath(options.data.contextPath, options.ids[0]) + '.';
-    }
+        data;
 
     if (isFunction(context)) { context = context.call(this); }
 
@@ -30,15 +25,11 @@ export default function(instance) {
         data.index = index;
         data.first = index === 0;
         data.last = !!last;
-
-        if (contextPath) {
-          data.contextPath = contextPath + field;
-        }
       }
 
       ret = ret + fn(context[field], {
         data: data,
-        blockParams: blockParams([context[field], field], [contextPath + field, null])
+        blockParams: [context[field], field]
       });
     }
 
diff --git a/lib/handlebars/helpers/with.js b/lib/handlebars/helpers/with.js
index 7418cd06..bb352c54 100644
--- a/lib/handlebars/helpers/with.js
+++ b/lib/handlebars/helpers/with.js
@@ -1,4 +1,4 @@
-import {appendContextPath, blockParams, createFrame, isEmpty, isFunction} from '../utils';
+import {isEmpty, isFunction} from '../utils';
 
 export default function(instance) {
   instance.registerHelper('with', function(context, options) {
@@ -8,14 +8,10 @@ export default function(instance) {
 
     if (!isEmpty(context)) {
       let data = options.data;
-      if (options.data && options.ids) {
-        data = createFrame(options.data);
-        data.contextPath = appendContextPath(options.data.contextPath, options.ids[0]);
-      }
 
       return fn(context, {
         data: data,
-        blockParams: blockParams([context], [data && data.contextPath])
+        blockParams: [context]
       });
     } else {
       return options.inverse(this);
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js
index b47d9615..a7a91033 100644
--- a/lib/handlebars/runtime.js
+++ b/lib/handlebars/runtime.js
@@ -38,9 +38,6 @@ export function template(templateSpec, env) {
   function invokePartialWrapper(partial, context, options) {
     if (options.hash) {
       context = Utils.extend({}, context, options.hash);
-      if (options.ids) {
-        options.ids[0] = true;
-      }
     }
 
     partial = env.VM.resolvePartial.call(this, partial, context, options);
@@ -224,9 +221,6 @@ export function resolvePartial(partial, context, options) {
 
 export function invokePartial(partial, context, options) {
   options.partial = true;
-  if (options.ids) {
-    options.data.contextPath = options.ids[0] || options.data.contextPath;
-  }
 
   let partialBlock;
   if (options.fn && options.fn !== noop) {
diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js
index 2584601e..9d083947 100644
--- a/lib/handlebars/utils.js
+++ b/lib/handlebars/utils.js
@@ -98,11 +98,3 @@ export function createFrame(object) {
   return frame;
 }
 
-export function blockParams(params, ids) {
-  params.path = ids;
-  return params;
-}
-
-export function appendContextPath(contextPath, id) {
-  return (contextPath ? contextPath + '.' : '') + id;
-}
diff --git a/print-script b/print-script
index 046b99c1..84646987 100755
--- a/print-script
+++ b/print-script
@@ -16,7 +16,6 @@ var template = Handlebars.precompile(script, {
       assumeObjects: true,
       compat: false,
       strict: true,
-      trackIds: true,
       knownHelpersOnly: false
     });
 
diff --git a/spec/string-params.js b/spec/string-params.js
deleted file mode 100644
index b76f291f..00000000
--- a/spec/string-params.js
+++ /dev/null
@@ -1,176 +0,0 @@
-describe('string params mode', function() {
-  it('arguments to helpers can be retrieved from options hash in string form', function() {
-    var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {stringParams: true});
-
-    var helpers = {
-      wycats: function(passiveVoice, noun) {
-        return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun;
-      }
-    };
-
-    var result = template({}, {helpers: helpers});
-
-    equals(result, 'HELP ME MY BOSS is.a slave.driver', 'String parameters output');
-  });
-
-  it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() {
-    var template = CompilerContext.compile('{{#wycats is.a slave.driver}}help :({{/wycats}}', {stringParams: true});
-
-    var helpers = {
-      wycats: function(passiveVoice, noun, options) {
-        return 'HELP ME MY BOSS ' + passiveVoice + ' ' +
-                noun + ': ' + options.fn(this);
-      }
-    };
-
-    var result = template({}, {helpers: helpers});
-
-    equals(result, 'HELP ME MY BOSS is.a slave.driver: help :(', 'String parameters output');
-  });
-
-  it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() {
-    var template = CompilerContext.compile('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}', {stringParams: true});
-
-    var helpers = {
-      tomdale: function(desire, noun, options) {
-        return 'STOP ME FROM READING HACKER NEWS I ' +
-                options.contexts[0][desire] + ' ' + noun;
-      },
-
-      'with': function(context, options) {
-        return options.fn(options.contexts[0][context]);
-      }
-    };
-
-    var result = template({
-      dale: {},
-
-      need: 'need-a'
-    }, {helpers: helpers});
-
-    equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke', 'Proper context variable output');
-  });
-
-  it('information about the types is passed along', function() {
-    var template = CompilerContext.compile("{{tomdale 'need' dad.joke true false}}", { stringParams: true });
-
-    var helpers = {
-      tomdale: function(desire, noun, trueBool, falseBool, options) {
-        equal(options.types[0], 'StringLiteral', 'the string type is passed');
-        equal(options.types[1], 'PathExpression', 'the expression type is passed');
-        equal(options.types[2], 'BooleanLiteral', 'the expression type is passed');
-        equal(desire, 'need', 'the string form is passed for strings');
-        equal(noun, 'dad.joke', 'the string form is passed for expressions');
-        equal(trueBool, true, 'raw booleans are passed through');
-        equal(falseBool, false, 'raw booleans are passed through');
-        return 'Helper called';
-      }
-    };
-
-    var result = template({}, { helpers: helpers });
-    equal(result, 'Helper called');
-  });
-
-  it('hash parameters get type information', function() {
-    var template = CompilerContext.compile("{{tomdale he.says desire='need' noun=dad.joke bool=true}}", { stringParams: true });
-
-    var helpers = {
-      tomdale: function(exclamation, options) {
-        equal(exclamation, 'he.says');
-        equal(options.types[0], 'PathExpression');
-
-        equal(options.hashTypes.desire, 'StringLiteral');
-        equal(options.hashTypes.noun, 'PathExpression');
-        equal(options.hashTypes.bool, 'BooleanLiteral');
-        equal(options.hash.desire, 'need');
-        equal(options.hash.noun, 'dad.joke');
-        equal(options.hash.bool, true);
-        return 'Helper called';
-      }
-    };
-
-    var result = template({}, { helpers: helpers });
-    equal(result, 'Helper called');
-  });
-
-  it('hash parameters get context information', function() {
-    var template = CompilerContext.compile("{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}", { stringParams: true });
-
-    var context = {dale: {}};
-
-    var helpers = {
-      tomdale: function(exclamation, options) {
-        equal(exclamation, 'he.says');
-        equal(options.types[0], 'PathExpression');
-
-        equal(options.contexts.length, 1);
-        equal(options.hashContexts.noun, context);
-        equal(options.hash.desire, 'need');
-        equal(options.hash.noun, 'dad.joke');
-        equal(options.hash.bool, true);
-        return 'Helper called';
-      },
-      'with': function(withContext, options) {
-        return options.fn(options.contexts[0][withContext]);
-      }
-    };
-
-    var result = template(context, { helpers: helpers });
-    equal(result, 'Helper called');
-  });
-
-  it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() {
-    var template = CompilerContext.compile('{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}', {stringParams: true});
-
-    var helpers = {
-      tomdale: function(desire, noun, options) {
-        return 'STOP ME FROM READING HACKER NEWS I ' +
-                options.contexts[0][desire] + ' ' + noun + ' ' +
-                options.fn(this);
-      },
-
-      'with': function(context, options) {
-        return options.fn(options.contexts[0][context]);
-      }
-    };
-
-    var result = template({
-      dale: {},
-
-      need: 'need-a'
-    }, {helpers: helpers});
-
-    equals(result, 'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot', 'Proper context variable output');
-  });
-
-  it('with nested block ambiguous', function() {
-    var template = CompilerContext.compile('{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}', {stringParams: true});
-
-    var helpers = {
-      'with': function() {
-        return 'WITH';
-      },
-      view: function() {
-        return 'VIEW';
-      }
-    };
-
-    var result = template({}, {helpers: helpers});
-    equals(result, 'WITH');
-  });
-
-  it('should handle DATA', function() {
-    var template = CompilerContext.compile('{{foo @bar}}', { stringParams: true });
-
-    var helpers = {
-      foo: function(bar, options) {
-        equal(bar, '@bar');
-        equal(options.types[0], 'PathExpression');
-        return 'Foo!';
-      }
-    };
-
-    var result = template({}, { helpers: helpers });
-    equal(result, 'Foo!');
-  });
-});
diff --git a/spec/subexpressions.js b/spec/subexpressions.js
index dad741e4..3810eb85 100644
--- a/spec/subexpressions.js
+++ b/spec/subexpressions.js
@@ -162,50 +162,6 @@ describe('subexpressions', function() {
     shouldCompileTo(string, [context, helpers], '<input aria-label="Name" placeholder="Example User" />');
   });
 
-  it('in string params mode,', function() {
-    var template = CompilerContext.compile('{{snog (blorg foo x=y) yeah a=b}}', {stringParams: true});
-
-    var helpers = {
-      snog: function(a, b, options) {
-        equals(a, 'foo');
-        equals(options.types.length, 2, 'string params for outer helper processed correctly');
-        equals(options.types[0], 'SubExpression', 'string params for outer helper processed correctly');
-        equals(options.types[1], 'PathExpression', 'string params for outer helper processed correctly');
-        return a + b;
-      },
-
-      blorg: function(a, options) {
-        equals(options.types.length, 1, 'string params for inner helper processed correctly');
-        equals(options.types[0], 'PathExpression', 'string params for inner helper processed correctly');
-        return a;
-      }
-    };
-
-    var result = template({
-      foo: {},
-      yeah: {}
-    }, {helpers: helpers});
-
-    equals(result, 'fooyeah');
-  });
-
-  it('as hashes in string params mode', function() {
-    var template = CompilerContext.compile('{{blog fun=(bork)}}', {stringParams: true});
-
-    var helpers = {
-      blog: function(options) {
-        equals(options.hashTypes.fun, 'SubExpression');
-        return 'val is ' + options.hash.fun;
-      },
-      bork: function() {
-        return 'BORK';
-      }
-    };
-
-    var result = template({}, {helpers: helpers});
-    equals(result, 'val is BORK');
-  });
-
   it('subexpression functions on the context', function() {
     var string = '{{foo (bar)}}!';
     var context = {
diff --git a/spec/track-ids.js b/spec/track-ids.js
deleted file mode 100644
index 30a46617..00000000
--- a/spec/track-ids.js
+++ /dev/null
@@ -1,237 +0,0 @@
-describe('track ids', function() {
-  var context;
-  beforeEach(function() {
-    context = {is: {a: 'foo'}, slave: {driver: 'bar'}};
-  });
-
-  it('should not include anything without the flag', function() {
-    var template = CompilerContext.compile('{{wycats is.a slave.driver}}');
-
-    var helpers = {
-      wycats: function(passiveVoice, noun, options) {
-        equal(options.ids, undefined);
-        equal(options.hashIds, undefined);
-
-        return 'success';
-      }
-    };
-
-    equals(template({}, {helpers: helpers}), 'success');
-  });
-  it('should include argument ids', function() {
-    var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {trackIds: true});
-
-    var helpers = {
-      wycats: function(passiveVoice, noun, options) {
-        equal(options.ids[0], 'is.a');
-        equal(options.ids[1], 'slave.driver');
-
-        return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
-      }
-    };
-
-    equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo slave.driver:bar');
-  });
-  it('should include hash ids', function() {
-    var template = CompilerContext.compile('{{wycats bat=is.a baz=slave.driver}}', {trackIds: true});
-
-    var helpers = {
-      wycats: function(options) {
-        equal(options.hashIds.bat, 'is.a');
-        equal(options.hashIds.baz, 'slave.driver');
-
-        return 'HELP ME MY BOSS ' + options.hashIds.bat + ':' + options.hash.bat + ' ' + options.hashIds.baz + ':' + options.hash.baz;
-      }
-    };
-
-    equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo slave.driver:bar');
-  });
-  it('should note ../ and ./ references', function() {
-    var template = CompilerContext.compile('{{wycats ./is.a ../slave.driver this.is.a this}}', {trackIds: true});
-
-    var helpers = {
-      wycats: function(passiveVoice, noun, thiz, thiz2, options) {
-        equal(options.ids[0], 'is.a');
-        equal(options.ids[1], '../slave.driver');
-        equal(options.ids[2], 'is.a');
-        equal(options.ids[3], '');
-
-        return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
-      }
-    };
-
-    equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS is.a:foo ../slave.driver:undefined');
-  });
-  it('should note @data references', function() {
-    var template = CompilerContext.compile('{{wycats @is.a @slave.driver}}', {trackIds: true});
-
-    var helpers = {
-      wycats: function(passiveVoice, noun, options) {
-        equal(options.ids[0], '@is.a');
-        equal(options.ids[1], '@slave.driver');
-
-        return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
-      }
-    };
-
-    equals(template({}, {helpers: helpers, data: context}), 'HELP ME MY BOSS @is.a:foo @slave.driver:bar');
-  });
-
-  it('should return null for constants', function() {
-    var template = CompilerContext.compile('{{wycats 1 "foo" key=false}}', {trackIds: true});
-
-    var helpers = {
-      wycats: function(passiveVoice, noun, options) {
-        equal(options.ids[0], null);
-        equal(options.ids[1], null);
-        equal(options.hashIds.key, null);
-
-        return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun + ' ' + options.hash.key;
-      }
-    };
-
-    equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS 1 foo false');
-  });
-  it('should return true for subexpressions', function() {
-    var template = CompilerContext.compile('{{wycats (sub)}}', {trackIds: true});
-
-    var helpers = {
-      sub: function() { return 1; },
-      wycats: function(passiveVoice, options) {
-        equal(options.ids[0], true);
-
-        return 'HELP ME MY BOSS ' + passiveVoice;
-      }
-    };
-
-    equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS 1');
-  });
-
-  it('should use block param paths', function() {
-    var template = CompilerContext.compile('{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}', {trackIds: true});
-
-    var helpers = {
-      doIt: function(options) {
-        var blockParams = [this.is];
-        blockParams.path = ['zomg'];
-        return options.fn(this, {blockParams: blockParams});
-      },
-      wycats: function(passiveVoice, noun, blah, options) {
-        equal(options.ids[0], 'zomg.a');
-        equal(options.ids[1], 'slave.driver');
-        equal(options.ids[2], 'zomg');
-
-        return 'HELP ME MY BOSS ' + options.ids[0] + ':' + passiveVoice + ' ' + options.ids[1] + ':' + noun;
-      }
-    };
-
-    equals(template(context, {helpers: helpers}), 'HELP ME MY BOSS zomg.a:foo slave.driver:bar');
-  });
-
-  describe('builtin helpers', function() {
-    var helpers = {
-      blockParams: function(name, options) {
-        return name + ':' + options.ids[0] + '\n';
-      },
-      wycats: function(name, options) {
-        return name + ':' + options.data.contextPath + '\n';
-      }
-    };
-
-    describe('#each', function() {
-      it('should track contextPath for arrays', function() {
-        var template = CompilerContext.compile('{{#each array}}{{wycats name}}{{/each}}', {trackIds: true});
-
-        equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:array.0\nbar:array.1\n');
-      });
-      it('should track contextPath for keys', function() {
-        var template = CompilerContext.compile('{{#each object}}{{wycats name}}{{/each}}', {trackIds: true});
-
-        equals(template({object: {foo: {name: 'foo'}, bar: {name: 'bar'}}}, {helpers: helpers}), 'foo:object.foo\nbar:object.bar\n');
-      });
-      it('should handle nesting', function() {
-        var template = CompilerContext.compile('{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}', {trackIds: true});
-
-        equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:.array..0\nbar:.array..1\n');
-      });
-      it('should handle block params', function() {
-        var template = CompilerContext.compile('{{#each array as |value|}}{{blockParams value.name}}{{/each}}', {trackIds: true});
-
-        equals(template({array: [{name: 'foo'}, {name: 'bar'}]}, {helpers: helpers}), 'foo:array.0.name\nbar:array.1.name\n');
-      });
-    });
-    describe('#with', function() {
-      it('should track contextPath', function() {
-        var template = CompilerContext.compile('{{#with field}}{{wycats name}}{{/with}}', {trackIds: true});
-
-        equals(template({field: {name: 'foo'}}, {helpers: helpers}), 'foo:field\n');
-      });
-      it('should handle nesting', function() {
-        var template = CompilerContext.compile('{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}', {trackIds: true});
-
-        equals(template({bat: {field: {name: 'foo'}}}, {helpers: helpers}), 'foo:bat.field\n');
-      });
-    });
-    describe('#blockHelperMissing', function() {
-      it('should track contextPath for arrays', function() {
-        var template = CompilerContext.compile('{{#field}}{{wycats name}}{{/field}}', {trackIds: true});
-
-        equals(template({field: [{name: 'foo'}]}, {helpers: helpers}), 'foo:field.0\n');
-      });
-      it('should track contextPath for keys', function() {
-        var template = CompilerContext.compile('{{#field}}{{wycats name}}{{/field}}', {trackIds: true});
-
-        equals(template({field: {name: 'foo'}}, {helpers: helpers}), 'foo:field\n');
-      });
-      it('should handle nesting', function() {
-        var template = CompilerContext.compile('{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}', {trackIds: true});
-
-        equals(template({bat: {field: {name: 'foo'}}}, {helpers: helpers}), 'foo:bat.field\n');
-      });
-    });
-  });
-
-  describe('partials', function() {
-    var helpers = {
-      blockParams: function(name, options) {
-        return name + ':' + options.ids[0] + '\n';
-      },
-      wycats: function(name, options) {
-        return name + ':' + options.data.contextPath + '\n';
-      }
-    };
-
-    it('should pass track id for basic partial', function() {
-      var template = CompilerContext.compile('Dudes: {{#dudes}}{{> dude}}{{/dudes}}', {trackIds: true}),
-          hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
-
-      var partials = {
-        dude: CompilerContext.compile('{{wycats name}}', {trackIds: true})
-      };
-
-      equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:dudes.0\nAlan:dudes.1\n');
-    });
-
-    it('should pass track id for context partial', function() {
-      var template = CompilerContext.compile('Dudes: {{> dude dudes}}', {trackIds: true}),
-          hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
-
-      var partials = {
-        dude: CompilerContext.compile('{{#each this}}{{wycats name}}{{/each}}', {trackIds: true})
-      };
-
-      equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:dudes..0\nAlan:dudes..1\n');
-    });
-
-    it('should invalidate context for partials with parameters', function() {
-      var template = CompilerContext.compile('Dudes: {{#dudes}}{{> dude . bar="foo"}}{{/dudes}}', {trackIds: true}),
-          hash = {dudes: [{name: 'Yehuda', url: 'http://yehuda'}, {name: 'Alan', url: 'http://alan'}]};
-
-      var partials = {
-        dude: CompilerContext.compile('{{wycats name}}', {trackIds: true})
-      };
-
-      equals(template(hash, {helpers: helpers, partials: partials}), 'Dudes: Yehuda:true\nAlan:true\n');
-    });
-  });
-});
