You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2015/10/23 16:47:55 UTC

[41/50] [abbrv] ambari git commit: AMBARI-13520. Implement mapping-methods for collections (onechiporenko)

AMBARI-13520. Implement mapping-methods for collections (onechiporenko)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/4b66a824
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/4b66a824
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/4b66a824

Branch: refs/heads/branch-dev-patch-upgrade
Commit: 4b66a8243eb23531188127349ec50163dd88c9b7
Parents: b4468ce
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Thu Oct 22 13:40:47 2015 +0300
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Fri Oct 23 11:27:37 2015 +0300

----------------------------------------------------------------------
 .../alerts/manage_alert_groups_controller.js    |  2 +
 ambari-web/app/controllers/wizard.js            |  5 +-
 .../app/controllers/wizard/step6_controller.js  | 20 +----
 .../mappers/alert_definition_summary_mapper.js  | 10 +--
 .../app/mappers/alert_definitions_mapper.js     | 16 +---
 .../mixins/common/configs/configs_comparator.js |  5 +-
 .../mixins/common/configs/enhanced_configs.js   | 43 ++++++----
 ambari-web/app/models/configs/config_group.js   |  5 +-
 ambari-web/app/utils/blueprint.js               |  8 +-
 ambari-web/app/utils/helper.js                  | 89 ++++++++++++++++++++
 ambari-web/test/utils/helper_test.js            | 88 +++++++++++++++++++
 11 files changed, 221 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js b/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
index e956a9e..2ee9213 100644
--- a/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
+++ b/ambari-web/app/controllers/main/alerts/manage_alert_groups_controller.js
@@ -340,6 +340,8 @@ App.ManageAlertGroupsController = Em.Controller.extend({
     var availableDefinitions = [];
     var sharedDefinitions = App.AlertDefinition.find();
 
+    usedDefinitionsMap = selectedAlertGroup.get('definitions').toWickMapByProperty('name');
+
     selectedAlertGroup.get('definitions').forEach(function (def) {
       usedDefinitionsMap[def.name] = true;
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/controllers/wizard.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index 517122f..2acac59 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -905,10 +905,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     var serviceConfigProperties = [];
     var fileNamesToUpdate = this.getDBProperty('fileNamesToUpdate') || [];
     var installedServiceNames = stepController.get('installedServiceNames') || [];
-    var installedServiceNamesMap = {};
-    installedServiceNames.forEach(function(name) {
-      installedServiceNamesMap[name] = true;
-    });
+    var installedServiceNamesMap = installedServiceNames.toWickMap();
     stepController.get('stepConfigs').forEach(function (_content) {
 
       if (_content.serviceName === 'YARN') {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/controllers/wizard/step6_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js
index 3427aa1..a7ac939 100644
--- a/ambari-web/app/controllers/wizard/step6_controller.js
+++ b/ambari-web/app/controllers/wizard/step6_controller.js
@@ -180,11 +180,8 @@ App.WizardStep6Controller = Em.Controller.extend(App.BlueprintMixin, {
     var err = false;
     var hosts = this.get('hosts');
     var headers = this.get('headers');
-    var headersMap = {};
+    var headersMap = headers.toWickMapByProperty('name');
 
-    headers.forEach(function (header) {
-      headersMap[header.name] = true;
-    });
     hosts.forEach(function (host) {
       host.checkboxes.forEach(function (checkbox) {
         if (headersMap[checkbox.component]) {
@@ -379,10 +376,7 @@ App.WizardStep6Controller = Em.Controller.extend(App.BlueprintMixin, {
       masterHosts = [],
       headers = this.get('headers'),
       masterHostNames = this.get('content.masterComponentHosts').mapProperty('hostName').uniq(),
-      masterHostNamesMap = {};
-    masterHostNames.forEach(function(hostName) {
-      masterHostNamesMap[hostName] = true;
-    });
+      masterHostNamesMap = masterHostNames.toWickMap();
 
     this.getHostNames().forEach(function (_hostName) {
       var hasMaster = masterHostNamesMap[_hostName];
@@ -459,14 +453,8 @@ App.WizardStep6Controller = Em.Controller.extend(App.BlueprintMixin, {
       });
     } else {
 
-      var slaveComponentsMap = {};
-      slaveComponents.forEach(function(slave) {
-        slaveComponentsMap[Em.get(slave, 'componentName')] = slave;
-      });
-      var hostsObjMap = {};
-      hostsObj.forEach(function(host) {
-        hostsObjMap[Em.get(host, 'hostName')] = host;
-      });
+      var slaveComponentsMap = slaveComponents.toMapByProperty('componentName');
+      var hostsObjMap =  hostsObj.toMapByProperty('hostName');
 
       this.get('headers').forEach(function (header) {
         var nodes = slaveComponentsMap[header.get('name')];

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/mappers/alert_definition_summary_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/alert_definition_summary_mapper.js b/ambari-web/app/mappers/alert_definition_summary_mapper.js
index b795638..b6aa135 100644
--- a/ambari-web/app/mappers/alert_definition_summary_mapper.js
+++ b/ambari-web/app/mappers/alert_definition_summary_mapper.js
@@ -27,10 +27,7 @@ App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({
 
     if (!data.alerts_summary_grouped) return;
     var alertDefinitions = App.AlertDefinition.find();
-    var alertDefinitionsMap = {};
-    alertDefinitions.forEach(function (definition) {
-      alertDefinitionsMap[definition.get('id')] = definition;
-    });
+    var alertDefinitionsMap = alertDefinitions.toArray().toMapByProperty('id');
     var summaryMap = {};
     data.alerts_summary_grouped.forEach(function(alertDefinitionSummary) {
       var alertDefinition = alertDefinitionsMap[alertDefinitionSummary.definition_id];
@@ -67,10 +64,7 @@ App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({
     // set alertsCount and hasCriticalAlerts for each service
     var groupedByServiceName = dataManipulation.groupPropertyValues(alertDefinitions, 'service.serviceName');
     var services = App.Service.find();
-    var servicesMap = {};
-    services.forEach(function (service) {
-      servicesMap[service.get('id')] = service;
-    });
+    var servicesMap = services.toArray().toMapByProperty('id');
     Object.keys(groupedByServiceName).forEach(function(serviceName) {
       var service = servicesMap[serviceName];
       if (service) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/mappers/alert_definitions_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/alert_definitions_mapper.js b/ambari-web/app/mappers/alert_definitions_mapper.js
index 80d175e..7dad450 100644
--- a/ambari-web/app/mappers/alert_definitions_mapper.js
+++ b/ambari-web/app/mappers/alert_definitions_mapper.js
@@ -79,14 +79,10 @@ App.alertDefinitionsMapper = App.QuickDataMapper.create({
           alertMetricsUriDefinitions = [],
           alertGroupsMap = App.cache['previousAlertGroupsMap'],
           existingAlertDefinitions = App.AlertDefinition.find(),
-          existingAlertDefinitionsMap = {},
+          existingAlertDefinitionsMap = existingAlertDefinitions.toArray().toMapByProperty('id'),
           alertDefinitionsToDelete = existingAlertDefinitions.mapProperty('id'),
           rawSourceData = {};
 
-      existingAlertDefinitions.forEach(function (d) {
-        existingAlertDefinitionsMap[d.get('id')] = d;
-      });
-
       json.items.forEach(function (item) {
         var convertedReportDefinitions = [];
         var reporting = item.AlertDefinition.source.reporting;
@@ -206,10 +202,7 @@ App.alertDefinitionsMapper = App.QuickDataMapper.create({
    * @param data
    */
   setMetricsSourcePropertyLists: function (model, data) {
-    var modelsMap = {};
-    model.find().forEach(function (m) {
-      modelsMap[m.get('id')] = m;
-    });
+    var modelsMap = model.find().toArray().toMapByProperty('id');
     data.forEach(function (record) {
       var m = modelsMap[record.id];
       if (m) {
@@ -224,10 +217,7 @@ App.alertDefinitionsMapper = App.QuickDataMapper.create({
    */
   setAlertDefinitionsRawSourceData: function (rawSourceData) {
     var allDefinitions = App.AlertDefinition.find();
-    var allDefinitionsMap = {};
-    allDefinitions.forEach(function(d) {
-      allDefinitionsMap[d.get('id')] = d;
-    });
+    var allDefinitionsMap = allDefinitions.toArray().toMapByProperty('id');
     for (var alertDefinitionId in rawSourceData) {
       if (rawSourceData.hasOwnProperty(alertDefinitionId)) {
         var m = allDefinitionsMap[+alertDefinitionId];

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/mixins/common/configs/configs_comparator.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/configs/configs_comparator.js b/ambari-web/app/mixins/common/configs/configs_comparator.js
index 0b30cb6..bb7bc88 100644
--- a/ambari-web/app/mixins/common/configs/configs_comparator.js
+++ b/ambari-web/app/mixins/common/configs/configs_comparator.js
@@ -75,7 +75,7 @@ App.ConfigsComparator = Em.Mixin.create({
    */
   initCompareConfig: function(allConfigs, json) {
     var serviceVersionMap = {};
-    var configNamesMap = {};
+    var configNamesMap = allConfigs.toWickMapByProperty('name');
     var serviceName = this.get('content.serviceName');
     var compareVersionNumber = this.get('compareServiceVersion').get('version');
     //indicate whether compared versions are from non-default group
@@ -85,9 +85,6 @@ App.ConfigsComparator = Em.Mixin.create({
     if (compareNonDefaultVersions) {
       serviceVersionMap[this.get('selectedVersion')] = {};
     }
-    allConfigs.mapProperty('name').forEach(function(name) {
-      configNamesMap[name] = true;
-    });
 
     json.items.forEach(function (item) {
       item.configurations.forEach(function (configuration) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/mixins/common/configs/enhanced_configs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js b/ambari-web/app/mixins/common/configs/enhanced_configs.js
index 009a775..d984e7d 100644
--- a/ambari-web/app/mixins/common/configs/enhanced_configs.js
+++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js
@@ -183,9 +183,8 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
    * @param {App.ServiceConfig[]} stepConfigs
    */
   clearDependenciesForInstalledServices: function(installedServices, stepConfigs) {
-    var allConfigs = stepConfigs.mapProperty('configs').filter(function(item) {
-      return item.length;
-    }).reduce(function(p, c) {
+    var stackConfigsMap = App.StackConfigProperty.find().toArray().toMapByProperty('name');
+    var allConfigs = stepConfigs.mapProperty('configs').filterProperty('length').reduce(function(p, c) {
       if (p) {
         return p.concat(c);
       }
@@ -193,7 +192,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
     var cleanDependencies = this.get('_dependentConfigValues').reject(function(item) {
       if ('hadoop.proxyuser'.contains(Em.get(item, 'name'))) return false;
       if (installedServices.contains(Em.get(item, 'serviceName'))) {
-        var stackProperty = App.StackConfigProperty.find().findProperty("name", item.propertyName);
+        var stackProperty = stackConfigsMap[item.propertyName];
         var parentConfigs = stackProperty && stackProperty.get('propertyDependsOn');
         if (!parentConfigs || !parentConfigs.length) {
           return true;
@@ -440,7 +439,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
     for (var key in configObject) {
 
       /**  defines main info for file name (service name, config group, config that belongs to filename) **/
-      var service = App.config.getServiceByConfigType(key);
+      var service = App.config.get('serviceByConfigTypeMap')[key];
       var serviceName = service.get('serviceName');
       var stepConfig = this.get('stepConfigs').findProperty('serviceName', serviceName);
       if (stepConfig) {
@@ -451,7 +450,6 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
 
         for (var propertyName in configObject[key].properties) {
 
-          var dependentProperty = this.get('_dependentConfigValues').filterProperty('propertyName', propertyName).filterProperty('fileName', key).findProperty('configGroup', group && Em.get(group,'name'));
           var cp = configProperties.findProperty('name', propertyName);
           var override = (notDefaultGroup && group && cp && cp.get('overrides')) ? cp.get('overrides').findProperty('group.name', group.get('name')) : null;
 
@@ -471,6 +469,10 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
           recommendedValue = validator.isValidFloat(recommendedValue) ? parseFloat(recommendedValue).toString() : recommendedValue;
 
           if (!updateOnlyBoundaries && !parentPropertiesNames.contains(App.config.configId(propertyName, key)) && initialValue != recommendedValue) { //on first initial request we don't need to change values
+            var groupName = group && Em.get(group, 'name');
+            var dependentProperty = this.get('_dependentConfigValues').find(function (dcv) {
+              return dcv.propertyName === propertyName && dcv.fileName === key && dcv.configGroup === groupName;
+            });
             if (dependentProperty) {
               Em.set(dependentProperty, 'value', initialValue);
               Em.set(dependentProperty, 'recommendedValue', recommendedValue);
@@ -545,6 +547,16 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
     }
   },
 
+  installedServices: function () {
+    return App.StackService.find().toArray().toMapByCallback('serviceName', function (item) {
+      return Em.get(item, 'isInstalled');
+    });
+  }.property(),
+
+  stackConfigsMap: function () {
+    return App.StackConfigProperty.find().toArray().toMapByProperty('id');
+  }.property(),
+
   /**
    * Save property attributes received from recommendations. These attributes are minimum, maximum,
    * increment_step. Attributes are stored in <code>App.StackConfigProperty</code> model.
@@ -556,14 +568,13 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
    */
   _saveRecommendedAttributes: function(configs, parentPropertiesNames, updateOnlyBoundaries, selectedConfigGroup) {
     var self = this;
-    var wizardController = self.get('wizardController');
+    var installedServices = this.get('installedServices');
+    var wizardController = this.get('wizardController');
     var fileNamesToUpdate = wizardController ? this.get('_fileNamesToUpdate') : [];
-    var stackConfigsMap = {};
-    App.StackConfigProperty.find().forEach(function (c) {
-      stackConfigsMap[c.get('id')] = c;
-    });
+    var stackConfigsMap = this.get('stackConfigsMap');
     Em.keys(configs).forEach(function (siteName) {
-      var service = App.config.getServiceByConfigType(siteName);
+      var fileName = App.config.getOriginalFileName(siteName);
+      var service = App.config.get('serviceByConfigTypeMap')[siteName];
       var serviceName = service.get('serviceName');
       var group = self.getGroupForService(serviceName);
       var stepConfig = self.get('stepConfigs').findProperty('serviceName', serviceName);
@@ -576,9 +587,11 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
         Em.keys(attributes).forEach(function (attributeName) {
           if (attributeName == 'delete' && cp) {
             if (!updateOnlyBoundaries) {
-              var fileName = App.config.getOriginalFileName(siteName);
               var modifiedFileNames = self.get('modifiedFileNames');
-              var dependentProperty = self.get('_dependentConfigValues').filterProperty('propertyName', propertyName).filterProperty('fileName', siteName).findProperty('configGroup', group && Em.get(group,'name'));
+              var groupName = group && Em.get(group,'name');
+              var dependentProperty = self.get('_dependentConfigValues').find(function (dcv) {
+                return dcv.propertyName === propertyName && dcv.fileName === siteName && dcv.configGroup === groupName;
+              });
               if (dependentProperty) {
                 Em.set(dependentProperty, 'toDelete', true);
                 Em.set(dependentProperty, 'toAdd', false);
@@ -603,7 +616,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create({
               }
               if (modifiedFileNames && !modifiedFileNames.contains(fileName)) {
                modifiedFileNames.push(fileName);
-              } else if (wizardController && App.StackService.find(service.get('serviceName')).get('isInstalled')) {
+              } else if (wizardController && installedServices[service.get('serviceName')]) {
                 if (!fileNamesToUpdate.contains(fileName)) {
                   fileNamesToUpdate.push(fileName);
                 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/models/configs/config_group.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/config_group.js b/ambari-web/app/models/configs/config_group.js
index 467f53e..c204774 100644
--- a/ambari-web/app/models/configs/config_group.js
+++ b/ambari-web/app/models/configs/config_group.js
@@ -122,13 +122,10 @@ App.ServiceConfigGroup = DS.Model.extend({
    */
   availableHosts: function () {
     if (this.get('isDefault')) return [];
-    var unusedHostsMap = {};
+    var unusedHostsMap = this.get('parentConfigGroup.hosts').toWickMap();
     var availableHosts = [];
     var sharedHosts = this.get('clusterHosts');
     // parentConfigGroup.hosts(hosts from default group) - are available hosts, which don't belong to any group
-    this.get('parentConfigGroup.hosts').forEach(function (hostName) {
-      unusedHostsMap[hostName] = true;
-    });
     sharedHosts.forEach(function (host) {
       if (unusedHostsMap[host.get('id')]) {
         availableHosts.pushObject(Ember.Object.create({

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/utils/blueprint.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/blueprint.js b/ambari-web/app/utils/blueprint.js
index 687c7fd..d3138e5 100644
--- a/ambari-web/app/utils/blueprint.js
+++ b/ambari-web/app/utils/blueprint.js
@@ -137,15 +137,11 @@ module.exports = {
    * @returns {object}
    */
   blueprintToObject: function(blueprint, field) {
-    var ret = {};
     var valueToMap = Em.get(blueprint, field);
     if (!Array.isArray(valueToMap)) {
-      return ret;
+      return {};
     }
-    valueToMap.forEach(function(n) {
-      ret[Em.get(n, 'name')] = n;
-    });
-    return ret;
+    return valueToMap.toMapByProperty('name');
   },
 
   matchGroups: function(masterBlueprint, slaveBlueprint) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/app/utils/helper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/helper.js b/ambari-web/app/utils/helper.js
index 9489eee..0f4a5c3 100644
--- a/ambari-web/app/utils/helper.js
+++ b/ambari-web/app/utils/helper.js
@@ -233,6 +233,95 @@ Array.prototype.sortPropertyLight = function (path) {
   });
   return this;
 };
+
+/**
+ * Create map from array with executing provided callback for each array's item
+ * Example:
+ * <pre>
+ *   var array = [{a: 1, b: 3}, {a: 2, b: 2}, {a: 3, b: 1}];
+ *   var map = array.toMapByCallback('a', function (item) {
+ *    return Em.get(item, 'b');
+ *   });
+ *   console.log(map); // {1: 3, 2: 2, 3: 1}
+ * </pre>
+ * <code>map[1]</code> is much more faster than <code>array.findProperty('a', 1).get('b')</code>
+ *
+ * @param {string} property
+ * @param {Function} callback
+ * @returns {object}
+ * @method toMapByCallback
+ */
+Array.prototype.toMapByCallback = function (property, callback) {
+  var ret = {};
+  Em.assert('`property` can\'t be empty string', property.length);
+  Em.assert('`callback` should be a function', 'function' === Em.typeOf(callback));
+  this.forEach(function (item) {
+    var key = Em.get(item, property);
+    ret[key] = callback(item, property);
+  });
+  return ret;
+};
+
+/**
+ * Create map from array
+ * Example:
+ * <pre>
+ *   var array = [{a: 1}, {a: 2}, {a: 3}];
+ *   var map = array.toMapByProperty('a'); // {1: {a: 1}, 2: {a: 2}, 3: {a: 3}}
+ * </pre>
+ * <code>map[1]</code> is much more faster than <code>array.findProperty('a', 1)</code>
+ *
+ * @param {string} property
+ * @return {object}
+ * @method toMapByProperty
+ * @see toMapByCallback
+ */
+Array.prototype.toMapByProperty = function (property) {
+  return this.toMapByCallback(property, function (item) {
+    return item;
+  });
+};
+
+/**
+ * Create wick map from array
+ * Example:
+ * <pre>
+ *   var array = [{a: 1}, {a: 2}, {a: 3}];
+ *   var map = array.toWickMapByProperty('a'); // {1: true, 2: true, 3: true}
+ * </pre>
+ * <code>map[1]</code> works faster than <code>array.someProperty('a', 1)</code>
+ *
+ * @param {string} property
+ * @return {object}
+ * @method toWickMapByProperty
+ * @see toMapByCallback
+ */
+Array.prototype.toWickMapByProperty = function (property) {
+  return this.toMapByCallback(property, function () {
+    return true;
+  });
+};
+
+/**
+ * Create wick map from array of primitives
+ * Example:
+ * <pre>
+ *   var array = [1, 2, 3];
+ *   var map = array.toWickMap(); // {1: true, 2: true, 3: true}
+ * </pre>
+ * <code>map[1]</code> works faster than <code>array.contains(1)</code>
+ *
+ * @returns {object}
+ * @method toWickMap
+ */
+Array.prototype.toWickMap = function () {
+  var ret = {};
+  this.forEach(function (item) {
+    ret[item] = true;
+  });
+  return ret;
+};
+
 /** @namespace Em **/
 Em.CoreObject.reopen({
   t:function (key, attrs) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4b66a824/ambari-web/test/utils/helper_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/helper_test.js b/ambari-web/test/utils/helper_test.js
index 63fd59d..62acf58 100644
--- a/ambari-web/test/utils/helper_test.js
+++ b/ambari-web/test/utils/helper_test.js
@@ -17,8 +17,10 @@
  */
 var App = require('app');
 require('utils/helper');
+var O = Em.Object;
 
 describe('utils/helper', function() {
+
   describe('String helpers', function() {
     describe('#trim()', function(){
       it('should replace first space', function() {
@@ -94,6 +96,7 @@ describe('utils/helper', function() {
       });
     });
   });
+
   describe('Number helpers', function(){
     describe('#toDaysHoursMinutes()', function(){
       var time = 1000000000;
@@ -113,7 +116,56 @@ describe('utils/helper', function() {
       });
     });
   });
+
   describe('Array helpers', function(){
+
+    var tests = Em.A([
+      {
+        m: 'plain objects, no nesting',
+        array: [{a: 1}, {a: 2}, {a: 3}],
+        property: 'a',
+        callback3: function (item) {
+          return Em.get(item, 'a');
+        },
+        e1: {1: {a: 1}, 2: {a: 2}, 3: {a: 3}},
+        e2: {1: true, 2: true, 3: true},
+        e3: {1: 1, 2: 2, 3: 3}
+      },
+      {
+        m: 'plain objects, nesting',
+        array: [{a: {a: 1}}, {a: {a: 2}}, {a:{a: 3}}],
+        property: 'a.a',
+        callback3: function (item) {
+          return Em.get(item, 'a.a');
+        },
+        e1: {1: {a: {a: 1}}, 2: {a: {a: 2}}, 3: {a: {a: 3}}},
+        e2: {1: true, 2: true, 3: true},
+        e3: {1: 1, 2: 2, 3: 3}
+      },
+      {
+        m: 'Ember objects, no nesting',
+        array: [O.create({a: 1}), O.create({a: 2}), O.create({a: 3})],
+        property: 'a',
+        callback3: function (item) {
+          return Em.get(item, 'a');
+        },
+        e1: {1: O.create({a: 1}), 2: O.create({a: 2}), 3: O.create({a: 3})},
+        e2: {1: true, 2: true, 3: true},
+        e3: {1: 1, 2: 2, 3: 3}
+      },
+      {
+        m: 'Ember objects, nesting',
+        array: [O.create({a: {a: 1}}), O.create({a: {a: 2}}), O.create({a: {a: 3}})],
+        property: 'a.a',
+        callback3: function (item) {
+          return Em.get(item, 'a.a');
+        },
+        e1: {1: O.create({a: {a: 1}}), 2: O.create({a: {a: 2}}), 3: O.create({a: {a: 3}})},
+        e2: {1: true, 2: true, 3: true},
+        e3: {1: 1, 2: 2, 3: 3}
+      }
+    ]);
+
     describe('#sortPropertyLight()', function(){
       var testable = [
         { a: 2 },
@@ -137,7 +189,41 @@ describe('utils/helper', function() {
         expect(testable.sortPropertyLight(['a'])).to.ok;
       });
     });
+
+    describe('#toMapByProperty', function () {
+      tests.forEach(function (test) {
+        it(test.m, function () {
+          expect(test.array.toMapByProperty(test.property)).to.eql(test.e1);
+        });
+      });
+    });
+
+    describe('#toWickMapByProperty', function () {
+      tests.forEach(function (test) {
+        it(test.m, function () {
+          expect(test.array.toWickMapByProperty(test.property)).to.eql(test.e2);
+        });
+      });
+    });
+
+    describe('#toMapByCallback', function () {
+      tests.forEach(function (test) {
+        it(test.m, function () {
+          expect(test.array.toMapByCallback(test.property, test.callback3)).to.eql(test.e3);
+        });
+      });
+    });
+
+    describe('#toWickMap', function () {
+
+      it('should convert to wick map', function () {
+        expect([1,2,3].toWickMap()).to.eql({1: true, 2: true, 3: true});
+      });
+
+    });
+
   });
+
   describe('App helpers', function(){
     var appendDiv = function() {
       $('body').append('<div id="tooltip-test"></div>');
@@ -347,6 +433,7 @@ describe('utils/helper', function() {
       });
     });
   });
+
   describe('#App.permit()', function() {
     var obj = {
       a1: 'v1',
@@ -484,4 +571,5 @@ describe('utils/helper', function() {
       });
     });
   });
+
 });