Skip to content

Commit c6378b1

Browse files
committed
exclude args from nestedProperty pruning
1 parent 1af058a commit c6378b1

File tree

2 files changed

+55
-16
lines changed

2 files changed

+55
-16
lines changed

src/lib/nested_property.js

+31-16
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,36 @@ function npGet(cont, parts) {
116116

117117
/*
118118
* Can this value be deleted? We can delete any empty object (null, undefined, [], {})
119-
* EXCEPT empty data arrays. Info arrays can be safely deleted, but not deleting them
120-
* has no ill effects other than leaving a trace or layout object with some cruft in it.
121-
* But deleting data arrays can change the meaning of the object, as `[]` means there is
119+
* EXCEPT empty data arrays, {} inside an array, or anything INSIDE an *args* array.
120+
*
121+
* Info arrays can be safely deleted, but not deleting them has no ill effects other
122+
* than leaving a trace or layout object with some cruft in it.
123+
*
124+
* Deleting data arrays can change the meaning of the object, as `[]` means there is
122125
* data for this attribute, it's just empty right now while `undefined` means the data
123-
* should be filled in with defaults to match other data arrays. So we do some simple
124-
* tests here to find known non-data arrays but don't worry too much about not deleting
125-
* some arrays that would actually be safe to delete.
126+
* should be filled in with defaults to match other data arrays.
127+
*
128+
* `{}` inside an array means "the default object" which is clearly different from
129+
* popping it off the end of the array, or setting it `undefined` inside the array.
130+
*
131+
* *args* arrays get passed directly to API methods and we should respect precisely
132+
* what the user has put there - although if the whole *args* array is empty it's fine
133+
* to delete that.
134+
*
135+
* So we do some simple tests here to find known non-data arrays but don't worry too
136+
* much about not deleting some arrays that would actually be safe to delete.
126137
*/
127-
var INFO_PATTERNS = /(^|.)((domain|range)(\.[xy])?|args|parallels)$/;
138+
var INFO_PATTERNS = /(^|\.)((domain|range)(\.[xy])?|args|parallels)$/;
139+
var ARGS_PATTERN = /(^|\.)args\[/;
128140
function isDeletable(val, propStr) {
129-
if(!emptyObj(val)) return false;
141+
if(!emptyObj(val) ||
142+
(isPlainObject(val) && propStr.charAt(propStr.length - 1) === ']') ||
143+
(propStr.match(ARGS_PATTERN) && val !== undefined)
144+
) {
145+
return false;
146+
}
130147
if(!isArray(val)) return true;
131148

132-
// domain and range are special - they show up in lots of places so hard code here.
133149
if(propStr.match(INFO_PATTERNS)) return true;
134150

135151
var match = containerArrayMatch(propStr);
@@ -186,8 +202,11 @@ function npSet(cont, parts, propStr) {
186202
}
187203

188204
function joinPropStr(propStr, newPart) {
189-
if(!propStr) return newPart;
190-
return propStr + isNumeric(newPart) ? ('[' + newPart + ']') : ('.' + newPart);
205+
var toAdd = newPart;
206+
if(isNumeric(newPart)) toAdd = '[' + newPart + ']';
207+
else if(propStr) toAdd = '.' + newPart;
208+
209+
return propStr + toAdd;
191210
}
192211

193212
// handle special -1 array index
@@ -244,11 +263,7 @@ function pruneContainers(containerLevels) {
244263
remainingKeys = false;
245264
if(isArray(curCont)) {
246265
for(j = curCont.length - 1; j >= 0; j--) {
247-
// If there's a plain object in an array, it's a container array
248-
// so we don't delete empty containers because they still have meaning.
249-
// `editContainerArray` handles the API for adding/removing objects
250-
// in this case.
251-
if(emptyObj(curCont[j]) && !isPlainObject(curCont[j])) {
266+
if(isDeletable(curCont[j], joinPropStr(propPart, j))) {
252267
if(remainingKeys) curCont[j] = undefined;
253268
else curCont.pop();
254269
}

test/jasmine/tests/lib_test.js

+24
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,30 @@ describe('Test lib.js:', function() {
375375
expect(obj).toEqual({a: [undefined, {}]});
376376
});
377377

378+
it('does not prune inside `args` arrays', function() {
379+
var obj = {},
380+
args = np(obj, 'args');
381+
382+
args.set([]);
383+
expect(obj.args).toBeUndefined();
384+
385+
args.set([null]);
386+
expect(obj.args).toEqual([null]);
387+
388+
np(obj, 'args[1]').set([]);
389+
expect(obj.args).toEqual([null, []]);
390+
391+
np(obj, 'args[2]').set({});
392+
expect(obj.args).toEqual([null, [], {}]);
393+
394+
np(obj, 'args[1]').set();
395+
expect(obj.args).toEqual([null, undefined, {}]);
396+
397+
// we still trim undefined off the end of arrays, but nothing else.
398+
np(obj, 'args[2]').set();
399+
expect(obj.args).toEqual([null]);
400+
});
401+
378402
it('should get empty, and fail on set, with a bad input object', function() {
379403
var badProps = [
380404
np(5, 'a'),

0 commit comments

Comments
 (0)