From 46afe8ef8e5d6d59ae27fb19550638b95d6ba6d7 Mon Sep 17 00:00:00 2001 From: Gordon Kristan Date: Sun, 13 Jul 2014 14:18:09 -0400 Subject: [PATCH] [FEATURE property-brace-expansion-improvement] --- FEATURES.md | 6 ++ features.json | 3 +- packages/ember-metal/lib/expand_properties.js | 49 ++++++++++- .../tests/expand_properties_test.js | 88 +++++++++++++++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 packages/ember-metal/tests/expand_properties_test.js diff --git a/FEATURES.md b/FEATURES.md index dbb0ab6ccfd..aa05fa61fd1 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -83,3 +83,9 @@ for a detailed explanation. Added in [#5136](https://github.com/emberjs/ember.js/pull/5136) +* `property-brace-expansion-improvement` + + Property brace expansion now allows multiple sets of braces to be used, + as well as not restricting their location in the string. + + Added in [#4617](https://github.com/emberjs/ember.js/pull/4617) \ No newline at end of file diff --git a/features.json b/features.json index 63b68708aa8..bd1848ca010 100644 --- a/features.json +++ b/features.json @@ -9,7 +9,8 @@ "ember-routing-will-change-hooks": null, "ember-routing-consistent-resources": true, "event-dispatcher-can-disable-event-manager": null, - "ember-metal-is-present": null + "ember-metal-is-present": null, + "property-brace-expansion-improvement": null }, "debugStatements": [ "Ember.warn", diff --git a/packages/ember-metal/lib/expand_properties.js b/packages/ember-metal/lib/expand_properties.js index 23b79d2e81c..b45f8420d44 100644 --- a/packages/ember-metal/lib/expand_properties.js +++ b/packages/ember-metal/lib/expand_properties.js @@ -1,3 +1,4 @@ +import Ember from "ember-metal/core"; import EmberError from 'ember-metal/error'; import { forEach } from 'ember-metal/enumerable_utils'; @@ -5,7 +6,8 @@ import { forEach } from 'ember-metal/enumerable_utils'; @module ember-metal */ -var BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; +var BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/, + SPLIT_REGEX = /\{|\}/; /** Expands `pattern`, invoking `callback` for each expansion. @@ -31,13 +33,21 @@ var BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; expansion, and is passed the expansion. */ export default function expandProperties(pattern, callback) { - var match, prefix, list; - if (pattern.indexOf(' ') > -1) { throw new EmberError('Brace expanded properties cannot contain spaces, ' + 'e.g. `user.{firstName, lastName}` should be `user.{firstName,lastName}`'); } + if (Ember.FEATURES.isEnabled('property-brace-expansion-improvement')) { + return newExpandProperties(pattern, callback); + } else { + return oldExpandProperties(pattern, callback); + } +} + +function oldExpandProperties(pattern, callback) { + var match, prefix, list; + if (match = BRACE_EXPANSION.exec(pattern)) { prefix = match[1]; list = match[2]; @@ -49,3 +59,36 @@ export default function expandProperties(pattern, callback) { callback(pattern); } } + +function newExpandProperties(pattern, callback) { + if ('string' === Ember.typeOf(pattern)) { + var parts = pattern.split(SPLIT_REGEX), + properties = [parts]; + + forEach(parts, function(part, index) { + if (part.indexOf(',') >= 0) { + properties = duplicateAndReplace(properties, part.split(','), index); + } + }); + + forEach(properties, function(property) { + callback(property.join('')); + }); + } else { + callback(pattern); + } +} + +function duplicateAndReplace(properties, currentParts, index) { + var all = []; + + forEach(properties, function(property) { + forEach(currentParts, function(part) { + var current = property.slice(0); + current[index] = part; + all.push(current); + }); + }); + + return all; +} \ No newline at end of file diff --git a/packages/ember-metal/tests/expand_properties_test.js b/packages/ember-metal/tests/expand_properties_test.js new file mode 100644 index 00000000000..b114ab696b9 --- /dev/null +++ b/packages/ember-metal/tests/expand_properties_test.js @@ -0,0 +1,88 @@ +import Ember from "ember-metal/core"; +import expandProperties from "ember-metal/expand_properties"; + +var foundProperties = []; + +function addProperty(property) { + foundProperties.push(property); +} + +QUnit.module('Property Brace Expansion Test', { + setup: function() { + foundProperties = []; + } +}); + +test('Properties without expansions are unaffected', function() { + expect(1); + + expandProperties('a', addProperty); + expandProperties('a.b', addProperty); + expandProperties('a.b.@each', addProperty); + + deepEqual(['a', 'a.b', 'a.b.@each'].sort(), foundProperties.sort()); +}); + +test('A single expansion at the end expands properly', function() { + expect(1); + + expandProperties('a.b.{c,d}', addProperty); + + deepEqual(['a.b.c', 'a.b.d'].sort(), foundProperties.sort()); +}); + +test('A property with only a brace expansion expands correctly', function() { + expect(1); + + expandProperties('{a,b,c}', addProperty); + + var expected = ['a', 'b', 'c']; + deepEqual(expected.sort(), foundProperties.sort()); +}); + +if (!Ember.FEATURES.isEnabled('property-brace-expansion-improvement')) { + test('A brace expansion at the beginning doesn\'t expand' , function() { + expect(1); + + expandProperties('{a,b,c}.d', addProperty); + + deepEqual(['{a,b,c}.d'], foundProperties); + }); +} + +if (Ember.FEATURES.isEnabled('property-brace-expansion-improvement')) { + test('Expansions with single properties only expand once', function() { + expect(1); + + expandProperties('a.b.{c}.d.{e}', addProperty); + + deepEqual(['a.b.c.d.e'], foundProperties); + }); + + test('A single brace expansion expands correctly', function() { + expect(1); + + expandProperties('a.{b,c,d}.e', addProperty); + + var expected = ['a.b.e', 'a.c.e', 'a.d.e']; + deepEqual(expected.sort(), foundProperties.sort()); + }); + + test('Multiple brace expansions work correctly', function() { + expect(1); + + expandProperties('{a,b,c}.d.{e,f}.g', addProperty); + + var expected = ['a.d.e.g', 'a.d.f.g', 'b.d.e.g', 'b.d.f.g', 'c.d.e.g', 'c.d.f.g']; + deepEqual(expected.sort(), foundProperties.sort()); + }); + + test('A property with only brace expansions expands correctly', function() { + expect(1); + + expandProperties('{a,b,c}.{d}.{e,f}', addProperty); + + var expected = ['a.d.e', 'a.d.f', 'b.d.e', 'b.d.f', 'c.d.e', 'c.d.f']; + deepEqual(expected.sort(), foundProperties.sort()); + }); +} \ No newline at end of file