docs: function makeBoundHelper(fn) {
        if (!SimpleHandlebarsView) { SimpleHandlebarsView = requireModule('ember-handlebars/views/handlebars_bound_view')['SimpleHandlebarsView']; } // ES6TODO: stupid circular dep
  
        var dependentKeys = slice.call(arguments, 1);
  
        function helper() {
          var properties = slice.call(arguments, 0, -1);
          var numProperties = properties.length;
          var options = arguments[arguments.length - 1];
          var normalizedProperties = [];
          var data = options.data;
          var types = data.isUnbound ? slice.call(options.types, 1) : options.types;
          var hash = options.hash;
          var hashTypes = options.hashTypes;
          var view = data.view;
          var contexts = options.contexts;
          var currentContext = (contexts && contexts.length) ? contexts[0] : this;
          var prefixPathForDependentKeys = '';
          var loc, len, hashOption;
          var boundOption, property;
          var normalizedValue = SimpleHandlebarsView.prototype.normalizedValue;
  
          Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn);
  
          // Detect bound options (e.g. countBinding="otherCount")
          var boundOptions = hash.boundOptions = {};
          for (hashOption in hash) {
            if (IS_BINDING.test(hashOption)) {
              // Lop off 'Binding' suffix.
              boundOptions[hashOption.slice(0, -7)] = hash[hashOption];
            } else if (hashTypes[hashOption] === 'ID') {
              boundOptions[hashOption] = hash[hashOption];
            }
          }
  
          // Expose property names on data.properties object.
          var watchedProperties = [];
          data.properties = [];
          for (loc = 0; loc < numProperties; ++loc) {
            data.properties.push(properties[loc]);
            if (types[loc] === 'ID') {
              var normalizedProp = normalizePath(currentContext, properties[loc], data);
              normalizedProperties.push(normalizedProp);
              watchedProperties.push(normalizedProp);
            } else {
              if (data.isUnbound) {
                normalizedProperties.push({path: properties[loc]});
              }else {
                normalizedProperties.push(null);
              }
            }
          }
  
          // Handle case when helper invocation is preceded by `unbound`, e.g.
          // {{unbound myHelper foo}}
          if (data.isUnbound) {
            return evaluateUnboundHelper(this, fn, normalizedProperties, options);
          }
  
          var bindView = new SimpleHandlebarsView(null, null, !options.hash.unescaped, options.data);
  
          // Override SimpleHandlebarsView's method for generating the view's content.
          bindView.normalizedValue = function() {
            var args = [];
            var boundOption;
  
            // Copy over bound hash options.
            for (boundOption in boundOptions) {
              if (!boundOptions.hasOwnProperty(boundOption)) { continue; }
              property = normalizePath(currentContext, boundOptions[boundOption], data);
              bindView.path = property.path;
              bindView.pathRoot = property.root;
              hash[boundOption] = normalizedValue.call(bindView);
            }
  
            for (loc = 0; loc < numProperties; ++loc) {
              property = normalizedProperties[loc];
              if (property) {
                bindView.path = property.path;
                bindView.pathRoot = property.root;
                args.push(normalizedValue.call(bindView));
              } else {
                args.push(properties[loc]);
              }
            }
            args.push(options);
  
            // Run the supplied helper function.
            return fn.apply(currentContext, args);
          };
  
          view.appendChild(bindView);
  
          // Assemble list of watched properties that'll re-render this helper.
          for (boundOption in boundOptions) {
            if (boundOptions.hasOwnProperty(boundOption)) {
              watchedProperties.push(normalizePath(currentContext, boundOptions[boundOption], data));
            }
          }
  
          // Observe each property.
          for (loc = 0, len = watchedProperties.length; loc < len; ++loc) {
            property = watchedProperties[loc];
            view.registerObserver(property.root, property.path, bindView, bindView.rerender);
          }
  
          if (types[0] !== 'ID' || normalizedProperties.length === 0) {
            return;
          }
  
          // Add dependent key observers to the first param
          var normalized = normalizedProperties[0];
          var pathRoot = normalized.root;
          var path = normalized.path;
  
          if(!isEmpty(path)) {
            prefixPathForDependentKeys = path + '.';
          }
          for (var i=0, l=dependentKeys.length; i<l; i++) {
            view.registerObserver(pathRoot, prefixPathForDependentKeys + dependentKeys[i], bindView, bindView.rerender);
          }
        }
  
        helper._rawFunction = fn;
        return helper;
      }