Repository: ambari
Updated Branches:
  refs/heads/trunk d5f2853df -> 5334953e1


AMBARI-13971. Implement Em.computed macros (2) (onechiporenko)


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

Branch: refs/heads/trunk
Commit: 5334953e1f36f9ab0d760c5c1319a82876b0b486
Parents: d5f2853
Author: Oleg Nechiporenko <onechipore...@apache.org>
Authored: Thu Nov 19 17:26:21 2015 +0200
Committer: Oleg Nechiporenko <onechipore...@apache.org>
Committed: Thu Nov 19 17:26:21 2015 +0200

----------------------------------------------------------------------
 ambari-web/app/utils/ember_computed.js          | 182 +++++++++++++-
 .../test/controllers/wizard/step2_test.js       |  10 +-
 .../test/controllers/wizard/step6_test.js       |   6 +-
 ambari-web/test/utils/ember_computed_test.js    | 247 ++++++++++++++++++-
 4 files changed, 426 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/5334953e/ambari-web/app/utils/ember_computed.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ember_computed.js 
b/ambari-web/app/utils/ember_computed.js
index d79c997..4ae8c5b 100644
--- a/ambari-web/app/utils/ember_computed.js
+++ b/ambari-web/app/utils/ember_computed.js
@@ -20,23 +20,65 @@ var computed = Em.computed;
 var get = Em.get;
 var makeArray = Em.makeArray;
 
+var slice = [].slice;
+
 var dataUtils = require('utils/data_manipulation');
 
 function getProperties(self, propertyNames) {
   var ret = {};
   for (var i = 0; i < propertyNames.length; i++) {
-    ret[propertyNames[i]] = get(self, propertyNames[i]);
+    var propertyName = propertyNames[i];
+    var value;
+    if (propertyName.startsWith('!')) {
+      propertyName = propertyName.substring(1);
+      value = !get(self, propertyName);
+    }
+    else {
+      value = get(self, propertyName);
+    }
+    ret[propertyName] = value;
   }
   return ret;
 }
 
+function getValues(self, propertyNames) {
+  return propertyNames.map(function (propertyName) {
+    return get(self, propertyName);
+  });
+}
+
+function generateComputedWithKey(macro) {
+  return function () {
+    var properties = slice.call(arguments, 1);
+    var key = arguments[0];
+    var computedFunc = computed(function () {
+      var values = getValues(this, properties);
+      return macro.call(this, key, values);
+    });
+
+    return computedFunc.property.apply(computedFunc, properties);
+  }
+}
+
 function generateComputedWithProperties(macro) {
   return function () {
-    var properties = [].slice.call(arguments);
+    var properties = slice.call(arguments);
     var computedFunc = computed(function () {
       return macro.apply(this, [getProperties(this, properties)]);
     });
 
+    var realProperties = properties.slice().invoke('replace', '!', '');
+    return computedFunc.property.apply(computedFunc, realProperties);
+  };
+}
+
+function generateComputedWithValues(macro) {
+  return function () {
+    var properties = slice.call(arguments);
+    var computedFunc = computed(function () {
+      return macro.apply(this, [getValues(this, properties)]);
+    });
+
     return computedFunc.property.apply(computedFunc, properties);
   };
 }
@@ -182,12 +224,13 @@ computed.ifThenElse = function (dependentKey, trueValue, 
falseValue) {
  * Returns true if all of them are truly, false - otherwise
  *
  * @method and
+ * @param {...string} dependentKeys
  * @returns {Ember.ComputedProperty}
  */
 computed.and = generateComputedWithProperties(function (properties) {
   var value;
   for (var key in properties) {
-    value = properties[key];
+    value = !!properties[key];
     if (properties.hasOwnProperty(key) && !value) {
       return false;
     }
@@ -201,12 +244,13 @@ computed.and = generateComputedWithProperties(function 
(properties) {
  * Returns true if at least one of them is truly, false - otherwise
  *
  * @method or
+ * @param {...string} dependentKeys
  * @returns {Ember.ComputedProperty}
  */
 computed.or = generateComputedWithProperties(function (properties) {
   var value;
   for (var key in properties) {
-    value = properties[key];
+    value = !!properties[key];
     if (properties.hasOwnProperty(key) && value) {
       return value;
     }
@@ -219,6 +263,7 @@ computed.or = generateComputedWithProperties(function 
(properties) {
  * Takes any number of arguments
  *
  * @method sumProperties
+ * @param {...string} dependentKeys
  * @returns {Ember.ComputedProperty}
  */
 computed.sumProperties = generateComputedWithProperties(function (properties) {
@@ -443,7 +488,7 @@ computed.findBy = function (collectionKey, propertyName, 
neededValue) {
 computed.alias = function (dependentKey) {
   return computed(dependentKey, function () {
     return get(this, dependentKey);
-  })
+  });
 };
 
 /**
@@ -458,5 +503,128 @@ computed.existsIn = function (dependentKey, neededValues) 
{
   return computed(dependentKey, function () {
     var value = get(this, dependentKey);
     return makeArray(neededValues).contains(value);
-  })
-};
\ No newline at end of file
+  });
+};
+
+/**
+ * A computed property that returns true if dependent property doesn't exist 
in the needed values
+ *
+ * @method notExistsIn
+ * @param {string} dependentKey
+ * @param {array} neededValues
+ * @returns {Ember.ComputedProperty}
+ */
+computed.notExistsIn = function (dependentKey, neededValues) {
+  return computed(dependentKey, function () {
+    var value = get(this, dependentKey);
+    return !makeArray(neededValues).contains(value);
+  });
+};
+
+/**
+ * A computed property that returns result of calculation 
<code>(dependentProperty1/dependentProperty2 * 100)</code>
+ * If accuracy is 0 (by default), result is rounded to integer
+ * Otherwise - result is float with provided accuracy
+ *
+ * @method percents
+ * @param {string} dependentKey1
+ * @param {string} dependentKey2
+ * @param {number} [accuracy=0]
+ * @returns {Ember.ComputedProperty}
+ */
+computed.percents = function (dependentKey1, dependentKey2, accuracy) {
+  if (arguments.length < 3) {
+    accuracy = 0;
+  }
+  return computed(dependentKey1, dependentKey2, function () {
+    var v1 = get(this, dependentKey1);
+    var v2 = get(this, dependentKey2);
+    var result = v1 / v2 * 100;
+    if (0 === accuracy) {
+      return Math.round(result);
+    }
+    return parseFloat(result.toFixed(accuracy));
+  });
+};
+
+/**
+ * A computed property that returns result of <code>App.format.role</code> for 
dependent value
+ *
+ * @method formatRole
+ * @param {string} dependentKey
+ * @returns {Ember.ComputedProperty}
+ */
+computed.formatRole = function (dependentKey) {
+  return computed(dependentKey, function () {
+    var value = get(this, dependentKey);
+    return App.format.role(value);
+  });
+};
+
+/**
+ * A computed property that returns sum of the named property in the each 
collection's item
+ *
+ * @method sumBy
+ * @param {string} collectionKey
+ * @param {string} propertyName
+ * @returns {Ember.ComputedProperty}
+ */
+computed.sumBy = function (collectionKey, propertyName) {
+  return computed(collectionKey + '.@each.' + propertyName, function () {
+    var collection = get(this, collectionKey);
+    if (Em.isEmpty(collection)) {
+      return 0;
+    }
+    var sum = 0;
+    collection.forEach(function (item) {
+      sum += get(item, propertyName);
+    });
+    return sum;
+  });
+};
+
+/**
+ * A computed property that returns I18n-string formatted with dependent 
properties
+ * Takes at least one argument
+ *
+ * @param {string} key key in the I18n-messages
+ * @param {...string} dependentKeys
+ * @method i18nFormat
+ * @returns {Ember.ComputedProperty}
+ */
+computed.i18nFormat = generateComputedWithKey(function (key, dependentValues) {
+  var str = Em.I18n.t(key);
+  return str.format.apply(str, dependentValues);
+});
+
+/**
+ * A computed property that returns dependent values joined with separator
+ * Takes at least one argument
+ *
+ * @param {string} separator
+ * @param {...string} dependentKeys
+ * @method concat
+ * @return {Ember.ComputedProperty}
+ */
+computed.concat = generateComputedWithKey(function (separator, 
dependentValues) {
+  return dependentValues.join(separator);
+});
+
+/**
+ * A computed property that returns first not blank value from dependent values
+ * Based on <code>Ember.isBlank</code>
+ * Takes at least 1 argument
+ * Dependent values order affects the result
+ *
+ * @param {...string} dependentKeys
+ * @method {firstNotBlank}
+ * @return {Ember.ComputedProperty}
+ */
+computed.firstNotBlank = generateComputedWithValues(function (values) {
+  for (var i = 0; i < values.length; i++) {
+    if (!Em.isBlank(values[i])) {
+      return values[i];
+    }
+  }
+  return null;
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5334953e/ambari-web/test/controllers/wizard/step2_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step2_test.js 
b/ambari-web/test/controllers/wizard/step2_test.js
index 8f0fc66..b137669 100644
--- a/ambari-web/test/controllers/wizard/step2_test.js
+++ b/ambari-web/test/controllers/wizard/step2_test.js
@@ -531,31 +531,31 @@ describe('App.WizardStep2Controller', function () {
 
     it('should return value if hostsError is not empty', function () {
       controller.set('hostsError', 'error');
-      expect(controller.get('isSubmitDisabled').length).to.above(0);
+      expect(controller.get('isSubmitDisabled')).to.be.true;
     });
 
     it('should return value if sshKeyError is not empty', function () {
       controller.set('sshKeyError', 'error');
       controller.set('hostsError', '');
-      expect(controller.get('isSubmitDisabled').length).to.above(0);
+      expect(controller.get('isSubmitDisabled')).to.be.true;
     });
 
     it('should return value if sshUserError is not empty', function () {
       controller.set('sshUserError', 'error');
       controller.set('sshKeyError', '');
-      expect(controller.get('isSubmitDisabled').length).to.above(0);
+      expect(controller.get('isSubmitDisabled')).to.be.true;
     });
 
     it('should return value if agentUserError is not empty', function () {
       controller.set('agentUserError', 'error');
       controller.set('sshUserError', '');
-      expect(controller.get('isSubmitDisabled').length).to.above(0);
+      expect(controller.get('isSubmitDisabled')).to.be.true;
     });
 
     it('should return value if sshPortError is not empty', function () {
         controller.set('sshPortError', 'error');
         controller.set('agentUserError', '');
-        expect(controller.get('isSubmitDisabled').length).to.above(0);
+        expect(controller.get('isSubmitDisabled')).to.be.true;
     });
   });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5334953e/ambari-web/test/controllers/wizard/step6_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step6_test.js 
b/ambari-web/test/controllers/wizard/step6_test.js
index 303f986..2de57fd 100644
--- a/ambari-web/test/controllers/wizard/step6_test.js
+++ b/ambari-web/test/controllers/wizard/step6_test.js
@@ -314,15 +314,15 @@ describe('App.WizardStep6Controller', function () {
   describe('#anyGeneralIssues', function () {
     it('should return error message if errorMessage', function () {
       controller.set('errorMessage', "error 404");
-      expect(controller.get('anyGeneralIssues')).to.equal("error 404");
+      expect(controller.get('anyGeneralIssues')).to.be.true;
     });
     it('should return true if we have several errors', function () {
       controller.set('generalErrorMessages', ["error 404", "error"]);
-      expect(controller.get('anyGeneralIssues')).to.equal(true);
+      expect(controller.get('anyGeneralIssues')).to.be.true;
     });
     it('should return true if we have several warnings', function () {
       controller.set('generalWarningMessages', ["error 404", "error"]);
-      expect(controller.get('anyGeneralIssues')).to.equal(true);
+      expect(controller.get('anyGeneralIssues')).to.be.true;
     });
   });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5334953e/ambari-web/test/utils/ember_computed_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/ember_computed_test.js 
b/ambari-web/test/utils/ember_computed_test.js
index 0d8bb8e..659139c 100644
--- a/ambari-web/test/utils/ember_computed_test.js
+++ b/ambari-web/test/utils/ember_computed_test.js
@@ -109,19 +109,39 @@ describe('Ember.computed macros', function () {
         prop1: true,
         prop2: true,
         prop3: true,
-        prop4: Em.computed.and('prop1', 'prop2', 'prop3')
+        prop4: Em.computed.and('prop1', 'prop2', 'prop3'),
+        prop5: Em.computed.and('prop1', '!prop2', '!prop3')
       });
     });
 
-    it('`true` if all dependent properties are true', function () {
+    it('prop4 `true` if all dependent properties are true', function () {
       expect(this.obj.get('prop4')).to.be.true;
     });
 
-    it('`false` if at elast one dependent property is false', function () {
+    it('prop4 `false` if at elast one dependent property is false', function 
() {
       this.obj.set('prop2', false);
       expect(this.obj.get('prop4')).to.be.false;
     });
 
+    it('prop5 dependent keys are valid', function () {
+      expect(Em.meta(this.obj).descs.prop5._dependentKeys).to.eql(['prop1', 
'prop2', 'prop3']);
+    });
+
+    it('prop5 `false` if some inverted dependent properties is true', function 
() {
+      expect(this.obj.get('prop5')).to.be.false;
+    });
+
+    it('prop5 `false` if some inverted dependent properties is true (2)', 
function () {
+      this.obj.set('prop1', true);
+      expect(this.obj.get('prop5')).to.be.false;
+    });
+
+    it('prop5 `true` ', function () {
+      this.obj.set('prop2', false);
+      this.obj.set('prop3', false);
+      expect(this.obj.get('prop5')).to.be.true;
+    });
+
   });
 
   describe('#or', function () {
@@ -131,7 +151,8 @@ describe('Ember.computed macros', function () {
         prop1: false,
         prop2: false,
         prop3: false,
-        prop4: Em.computed.or('prop1', 'prop2', 'prop3')
+        prop4: Em.computed.or('prop1', 'prop2', 'prop3'),
+        prop5: Em.computed.or('!prop1', '!prop2', '!prop3')
       });
     });
 
@@ -144,6 +165,26 @@ describe('Ember.computed macros', function () {
       expect(this.obj.get('prop4')).to.be.true;
     });
 
+    it('prop5 dependent keys are valid', function () {
+      expect(Em.meta(this.obj).descs.prop5._dependentKeys).to.eql(['prop1', 
'prop2', 'prop3']);
+    });
+
+    it('prop5 `true` if some inverted dependent properties is true', function 
() {
+      expect(this.obj.get('prop5')).to.be.true;
+    });
+
+    it('prop5 `true` if some inverted dependent properties is true (2)', 
function () {
+      this.obj.set('prop1', true);
+      expect(this.obj.get('prop5')).to.be.true;
+    });
+
+    it('prop5 `false` ', function () {
+      this.obj.set('prop1', true);
+      this.obj.set('prop2', true);
+      this.obj.set('prop3', true);
+      expect(this.obj.get('prop5')).to.be.false;
+    });
+
   });
 
   describe('#sumProperties', function () {
@@ -548,4 +589,202 @@ describe('Ember.computed macros', function () {
 
   });
 
+  describe('#percents', function () {
+
+    beforeEach(function () {
+      this.obj = Em.Object.create({
+        prop1: 10,
+        prop2: 25,
+        prop3: Em.computed.percents('prop1', 'prop2'),
+        prop4: Em.computed.percents('prop1', 'prop2', 2)
+      });
+    });
+
+    it('should calculate percents', function () {
+      expect(this.obj.get('prop3')).to.equal(40);
+      expect(this.obj.get('prop4')).to.equal(40.00);
+    });
+
+    it('should calculate percents (2)', function () {
+      this.obj.set('prop2', 35);
+      expect(this.obj.get('prop3')).to.equal(29);
+      expect(this.obj.get('prop4')).to.equal(28.57);
+    });
+
+  });
+
+  describe('#formatRole', function () {
+
+    beforeEach(function () {
+      this.obj = Em.Object.create({
+        prop1: 'NAMENODE',
+        prop2: Em.computed.formatRole('prop1')
+      });
+      sinon.stub(App.StackServiceComponent, 'find', function () {
+        return [
+          Em.Object.create({id: 'NAMENODE', displayName: 'NameNode'}),
+          Em.Object.create({id: 'SECONDARY_NAMENODE', displayName: 'Secondary 
NameNode'})
+        ];
+      });
+      sinon.stub(App.StackService, 'find', function () {
+        return [
+          Em.Object.create({id: 'MAPREDUCE2', displayName: 'MapReduce2'}),
+          Em.Object.create({id: 'HIVE', displayName: 'Hive'})
+        ];
+      });
+    });
+
+    afterEach(function () {
+      App.StackService.find.restore();
+      App.StackServiceComponent.find.restore();
+    });
+
+    it('should format as role', function () {
+      expect(this.obj.get('prop2')).to.equal('NameNode');
+    });
+
+    it('should format as role (2)', function () {
+      this.obj.set('prop1', 'HIVE');
+      expect(this.obj.get('prop2')).to.equal('Hive');
+    });
+
+  });
+
+  describe('#sumBy', function () {
+
+    beforeEach(function () {
+      this.obj = Em.Object.create({
+        prop1: [
+          {a: 1}, {a: 2}, {a: 3}
+        ],
+        prop2: Em.computed.sumBy('prop1', 'a')
+      });
+    });
+
+    it('should calculate sum', function () {
+      expect(this.obj.get('prop2')).to.equal(6);
+    });
+
+    it('should calculate sum (2)', function () {
+      this.obj.get('prop1').pushObject({a: 4});
+      expect(this.obj.get('prop2')).to.equal(10);
+    });
+
+  });
+
+  describe('#i18nFormat', function () {
+
+    beforeEach(function () {
+      sinon.stub(Em.I18n, 't', function (key) {
+        var msgs = {
+          key1: '{0} {1} {2}'
+        };
+        return msgs[key];
+      });
+      this.obj = Em.Object.create({
+        prop1: 'abc',
+        prop2: 'cba',
+        prop3: 'aaa',
+        prop4: Em.computed.i18nFormat('key1', 'prop1', 'prop2', 'prop3')
+      });
+    });
+
+    afterEach(function () {
+      Em.I18n.t.restore();
+    });
+
+    it('`prop4` check dependent keys', function () {
+      expect(Em.meta(this.obj).descs.prop4._dependentKeys).to.eql(['prop1', 
'prop2', 'prop3']);
+    });
+
+    it('should format message', function () {
+      expect(this.obj.get('prop4')).to.equal('abc cba aaa');
+    });
+
+    it('should format message (2)', function () {
+      this.obj.set('prop1', 'aaa');
+      expect(this.obj.get('prop4')).to.equal('aaa cba aaa');
+    });
+
+  });
+
+  describe('#concat', function () {
+
+    beforeEach(function () {
+      this.obj = Em.Object.create({
+        prop1: 'abc',
+        prop2: 'cba',
+        prop3: 'aaa',
+        prop4: Em.computed.concat(' ', 'prop1', 'prop2', 'prop3')
+      });
+    });
+
+    it('should concat dependent values', function () {
+      expect(this.obj.get('prop4')).to.equal('abc cba aaa');
+    });
+
+    it('should concat dependent values (2)', function () {
+      this.obj.set('prop1', 'aaa');
+      expect(this.obj.get('prop4')).to.equal('aaa cba aaa');
+    });
+
+  });
+
+  describe('#notExistsIn', function () {
+
+    beforeEach(function () {
+      this.obj = Em.Object.create({
+        prop1: 'v1',
+        prop2: Em.computed.notExistsIn('prop1', ['v1', 'v2'])
+      });
+    });
+
+    it('`false` if dependent value is in the array', function () {
+      expect(this.obj.get('prop2')).to.be.false;
+    });
+
+    it('`false` if dependent value is in the array (2)', function () {
+      this.obj.set('prop1', 'v2');
+      expect(this.obj.get('prop2')).to.be.false;
+    });
+
+    it('`true` if dependent value is not in the array', function () {
+      this.obj.set('prop1', 'v3');
+      expect(this.obj.get('prop2')).to.be.true;
+    });
+
+  });
+
+  describe('#firstNotBlank', function () {
+
+    beforeEach(function () {
+      this.obj = Em.Object.create({
+        prop1: '',
+        prop2: null,
+        prop3: '1234',
+        prop4: Em.computed.firstNotBlank('prop1', 'prop2', 'prop3')
+      })
+    });
+
+    it('`prop4` check dependent keys', function () {
+      expect(Em.meta(this.obj).descs.prop4._dependentKeys).to.eql(['prop1', 
'prop2', 'prop3']);
+    });
+
+    it('should returns prop3', function () {
+      expect(this.obj.get('prop4')).to.equal('1234');
+    });
+
+    it('should returns prop2', function () {
+      this.obj.set('prop2', 'not empty string');
+      expect(this.obj.get('prop4')).to.equal('not empty string');
+    });
+
+    it('should returns prop1', function () {
+      this.obj.set('prop2', 'not empty string');
+      this.obj.set('prop1', 'prop1 is used');
+      expect(this.obj.get('prop4')).to.equal('prop1 is used');
+    });
+
+  });
+
 });
\ No newline at end of file

Reply via email to