Skip to content

Commit fc5be0b

Browse files
authored
Merge pull request #13231 from nickiaconis/cp-commas
Correctly handle commas in CP property keys
2 parents f38b4b8 + 14edb5c commit fc5be0b

File tree

2 files changed

+54
-45
lines changed

2 files changed

+54
-45
lines changed

packages/ember-metal/lib/expand_properties.js

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { assert } from './debug';
55
@submodule ember-metal
66
*/
77

8-
const SPLIT_REGEX = /\{|\}/;
9-
const END_WITH_EACH_REGEX = /\.@each$/;
8+
var END_WITH_EACH_REGEX = /\.@each$/;
109

1110
/**
1211
Expands `pattern`, invoking `callback` for each expansion.
@@ -41,53 +40,55 @@ export default function expandProperties(pattern, callback) {
4140
'Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"',
4241
pattern.indexOf(' ') === -1
4342
);
44-
assert(
45-
`Brace expanded properties have to be balanced and cannot be nested, pattern: ${pattern}`,
46-
((str) => {
47-
let inBrace = 0;
48-
let char;
49-
for (let i = 0; i < str.length; i++) {
50-
char = str.charAt(i);
5143

52-
if (char === '{') {
53-
inBrace++;
54-
} else if (char === '}') {
55-
inBrace--;
44+
let unbalancedNestedError = `Brace expanded properties have to be balanced and cannot be nested, pattern: ${pattern}`;
45+
let properties = [pattern];
46+
47+
// Iterating backward over the pattern makes dealing with indices easier.
48+
let bookmark;
49+
let inside = false;
50+
for (let i = pattern.length; i > 0; --i) {
51+
let current = pattern[i - 1];
52+
53+
switch (current) {
54+
// Closing curly brace will be the first character of the brace expansion we encounter.
55+
// Bookmark its index so long as we're not already inside a brace expansion.
56+
case '}':
57+
if (!inside) {
58+
bookmark = i - 1;
59+
inside = true;
60+
} else {
61+
assert(unbalancedNestedError, false);
5662
}
57-
58-
if (inBrace > 1 || inBrace < 0) {
59-
return false;
63+
break;
64+
// Opening curly brace will be the last character of the brace expansion we encounter.
65+
// Apply the brace expansion so long as we've already seen a closing curly brace.
66+
case '{':
67+
if (inside) {
68+
let expansion = pattern.slice(i, bookmark).split(',');
69+
// Iterating backward allows us to push new properties w/out affecting our "cursor".
70+
for (let j = properties.length; j > 0; --j) {
71+
// Extract the unexpanded property from the array.
72+
let property = properties.splice(j - 1, 1)[0];
73+
// Iterate over the expansion, pushing the newly formed properties onto the array.
74+
for (let k = 0; k < expansion.length; ++k) {
75+
properties.push(property.slice(0, i - 1) +
76+
expansion[k] +
77+
property.slice(bookmark + 1));
78+
}
79+
}
80+
inside = false;
81+
} else {
82+
assert(unbalancedNestedError, false);
6083
}
61-
}
62-
63-
return true;
64-
})(pattern));
65-
66-
let parts = pattern.split(SPLIT_REGEX);
67-
let properties = [parts];
68-
69-
for (let i = 0; i < parts.length; i++) {
70-
let part = parts[i];
71-
if (part.indexOf(',') >= 0) {
72-
properties = duplicateAndReplace(properties, part.split(','), i);
84+
break;
7385
}
7486
}
87+
if (inside) {
88+
assert(unbalancedNestedError, false);
89+
}
7590

7691
for (let i = 0; i < properties.length; i++) {
77-
callback(properties[i].join('').replace(END_WITH_EACH_REGEX, '.[]'));
92+
callback(properties[i].replace(END_WITH_EACH_REGEX, '.[]'));
7893
}
7994
}
80-
81-
function duplicateAndReplace(properties, currentParts, index) {
82-
let all = [];
83-
84-
properties.forEach(property => {
85-
currentParts.forEach(part => {
86-
let current = property.slice(0);
87-
current[index] = part;
88-
all.push(current);
89-
});
90-
});
91-
92-
return all;
93-
}

packages/ember-metal/tests/expand_properties_test.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ QUnit.test('Nested brace expansions are not allowed', function() {
8989
}, /Brace expanded properties have to be balanced and cannot be nested/);
9090
});
9191

92+
QUnit.test('A property with no braces does not expand', function() {
93+
expect(1);
94+
95+
expandProperties('a,b,c.d.e,f', addProperty);
96+
97+
deepEqual(foundProperties, ['a,b,c.d.e,f']);
98+
});
99+
92100
QUnit.test('A pattern must be a string', function() {
93101
expect(1);
94102

@@ -100,7 +108,7 @@ QUnit.test('A pattern must be a string', function() {
100108
QUnit.test('A pattern must not contain a space', function() {
101109
expect(1);
102110

103-
expectAssertion(() => {
104-
expandProperties('a, b', addProperty);
111+
expectAssertion(function() {
112+
expandProperties('{a, b}', addProperty);
105113
}, /Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"/);
106114
});

0 commit comments

Comments
 (0)