AMBARI-20252. Add ability to use brace expansion in the dependent 
keys(onechiporenko)


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

Branch: refs/heads/branch-feature-AMBARI-12556
Commit: bcbc597b7bfa79ae884e2f82eab1f6f6fce022be
Parents: 890996f
Author: Oleg Nechiporenko <onechipore...@apache.org>
Authored: Wed Mar 1 12:13:56 2017 +0200
Committer: Oleg Nechiporenko <onechipore...@apache.org>
Committed: Wed Mar 1 12:13:56 2017 +0200

----------------------------------------------------------------------
 ambari-web/app/utils/ember_reopen.js       | 105 ++++++++++++++++++++++
 ambari-web/test/utils/ember_reopen_test.js | 112 ++++++++++++++++++++++++
 2 files changed, 217 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/bcbc597b/ambari-web/app/utils/ember_reopen.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ember_reopen.js 
b/ambari-web/app/utils/ember_reopen.js
index c7f93af..ff55b30 100644
--- a/ambari-web/app/utils/ember_reopen.js
+++ b/ambari-web/app/utils/ember_reopen.js
@@ -16,6 +16,11 @@
  * limitations under the License.
  */
 
+const {assert, warn, EXTEND_PROTOTYPES} = Ember;
+
+const END_WITH_EACH_REGEX = /\.@each$/;
+const DEEP_EACH_REGEX = /\.@each\.[^.]+\./;
+
 /**
  Merge the contents of two objects together into the first object.
 
@@ -350,3 +355,103 @@ Ember.Router.reopen({
     }
   }
 });
+
+function expandProperties(pattern, callback) {
+  assert('A computed property key must be a string', typeof pattern === 
'string');
+  assert(
+    'Brace expanded properties cannot contain spaces, e.g. "user.{firstName, 
lastName}" should be "user.{firstName,lastName}"',
+    pattern.indexOf(' ') === -1
+  );
+
+  let unbalancedNestedError = `Brace expanded properties have to be balanced 
and cannot be nested, pattern: ${pattern}`;
+  let properties = [pattern];
+
+  // Iterating backward over the pattern makes dealing with indices easier.
+  let bookmark;
+  let inside = false;
+  for (let i = pattern.length; i > 0; --i) {
+    let current = pattern[i - 1];
+
+    switch (current) {
+      // Closing curly brace will be the first character of the brace 
expansion we encounter.
+      // Bookmark its index so long as we're not already inside a brace 
expansion.
+      case '}':
+        if (!inside) {
+          bookmark = i - 1;
+          inside = true;
+        } else {
+          assert(unbalancedNestedError, false);
+        }
+        break;
+      // Opening curly brace will be the last character of the brace expansion 
we encounter.
+      // Apply the brace expansion so long as we've already seen a closing 
curly brace.
+      case '{':
+        if (inside) {
+          let expansion = pattern.slice(i, bookmark).split(',');
+          // Iterating backward allows us to push new properties w/out 
affecting our "cursor".
+          for (let j = properties.length; j > 0; --j) {
+            // Extract the unexpanded property from the array.
+            let property = properties.splice(j - 1, 1)[0];
+            // Iterate over the expansion, pushing the newly formed properties 
onto the array.
+            for (let k = 0; k < expansion.length; ++k) {
+              properties.push(property.slice(0, i - 1) +
+                expansion[k] +
+                property.slice(bookmark + 1));
+            }
+          }
+          inside = false;
+        } else {
+          assert(unbalancedNestedError, false);
+        }
+        break;
+    }
+  }
+  if (inside) {
+    assert(unbalancedNestedError, false);
+  }
+
+  for (let i = 0; i < properties.length; i++) {
+    callback(properties[i].replace(END_WITH_EACH_REGEX, '.[]'));
+  }
+}
+
+Ember.ComputedProperty.prototype.property = function () {
+  let args = [];
+
+  function addArg(property) {
+    warn(
+      `Dependent keys containing @each only work one level deep. ` +
+      `You used the key "${property}" which is invalid. ` +
+      `Please create an intermediary computed property.`,
+      DEEP_EACH_REGEX.test(property) === false
+    );
+    args.push(property);
+  }
+
+  for (let i = 0; i < arguments.length; i++) {
+    expandProperties(arguments[i], addArg);
+  }
+
+  this._dependentKeys = args.uniq();
+  return this;
+}
+
+Ember.observer = function(func, ...paths) {
+  let args = [];
+  for (let i = 0; i < paths.length; i++) {
+    expandProperties(paths[i], property => args.push(property));
+  }
+  func.__ember_observes__ = args.uniq();
+  return func;
+};
+
+if(EXTEND_PROTOTYPES) {
+  Function.prototype.observes = function () {
+    let args = [];
+    for (let i = 0; i < arguments.length; i++) {
+      expandProperties(arguments[i], property => args.push(property));
+    }
+    this.__ember_observes__ = args.uniq();
+    return this;
+  };
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/bcbc597b/ambari-web/test/utils/ember_reopen_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/ember_reopen_test.js 
b/ambari-web/test/utils/ember_reopen_test.js
index aa50a50..06e89be 100644
--- a/ambari-web/test/utils/ember_reopen_test.js
+++ b/ambari-web/test/utils/ember_reopen_test.js
@@ -135,4 +135,116 @@ describe('Ember functionality extension', function () {
       });
     });
   });
+
+  describe('#expand_properties', function () {
+
+    var body;
+    var tests = [
+      {
+        keys: ['a', 'b'],
+        expandedKeys: ['a', 'b']
+      },
+      {
+        keys: ['a.b', 'b'],
+        expandedKeys: ['a.b', 'b']
+      },
+      {
+        keys: ['a', 'a.b'],
+        expandedKeys: ['a', 'a.b']
+      },
+      {
+        keys: ['a.b', 'a.b'],
+        expandedKeys: ['a.b']
+      },
+      {
+        keys: ['a.b', 'b.c'],
+        expandedKeys: ['a.b', 'b.c']
+      },
+      {
+        keys: ['a.{b,c}'],
+        expandedKeys: ['a.b', 'a.c']
+      },
+      {
+        keys: ['{a,b}.c'],
+        expandedKeys: ['a.c', 'b.c']
+      },
+      {
+        keys: ['a.{b,c,d}'],
+        expandedKeys: ['a.b', 'a.c', 'a.d']
+      },
+      {
+        keys: ['a.@each.{b,c}'],
+        expandedKeys: ['a.@each.b', 'a.@each.c']
+      },
+      {
+        keys: ['a.{b.[],c}'],
+        expandedKeys: ['a.b.[]', 'a.c']
+      },
+      {
+        keys: ['a.{b,c.[]}'],
+        expandedKeys: ['a.b', 'a.c.[]']
+      },
+      {
+        keys: ['a.{b,c.[],b}'],
+        expandedKeys: ['a.b', 'a.c.[]']
+      },
+      {
+        keys: ['{a,b}.{c,d}'],
+        expandedKeys: ['a.d', 'b.d', 'a.c', 'b.c']
+      }
+    ];
+
+    beforeEach(function () {
+      body = function () {};
+    });
+
+    describe('#computed properties', function() {
+
+      describe('#Ember.computed', function () {
+        tests.forEach(function(test) {
+          it(JSON.stringify(test.keys), function () {
+            var args = test.keys.slice(0);
+            args.push(body);
+            var cp = Ember.computed.apply(null, args);
+            expect(cp._dependentKeys).to.be.eql(test.expandedKeys);
+          });
+        });
+      });
+
+      describe('#function(){}.property', function () {
+        tests.forEach(function(test) {
+          it(JSON.stringify(test.keys), function () {
+            var cp = body.property.apply(null, test.keys);
+            expect(cp._dependentKeys).to.be.eql(test.expandedKeys);
+          });
+        });
+      });
+
+    });
+
+    describe('#observers', function () {
+
+      describe('#Ember.observer', function () {
+        tests.forEach(function(test) {
+          it(JSON.stringify(test.keys), function () {
+            var args = [body].concat(test.keys);
+            var obs = Ember.observer.apply(null, args);
+            expect(obs.__ember_observes__).to.be.eql(test.expandedKeys);
+          });
+        });
+      });
+
+      describe('#function(){}.observes', function () {
+        tests.forEach(function(test) {
+          it(JSON.stringify(test.keys), function () {
+            var obs = body.observes.apply(body, test.keys);
+            expect(obs.__ember_observes__).to.be.eql(test.expandedKeys);
+          });
+        });
+      });
+
+    });
+
+  });
+
 });

Reply via email to