manyfest 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.config/configstore/update-notifier-npm.json +1 -1
- package/README.md +2 -0
- package/package.json +1 -1
- package/source/Manyfest-CleanWrapCharacters.js +28 -0
- package/source/Manyfest-ObjectAddress-CheckAddressExists.js +217 -0
- package/source/Manyfest-ObjectAddress-DeleteValue.js +452 -0
- package/source/{Manyfest-ObjectAddressResolver.js → Manyfest-ObjectAddress-GetValue.js} +27 -404
- package/source/Manyfest-ObjectAddress-SetValue.js +217 -0
- package/source/Manyfest-ObjectAddressGeneration.js +8 -8
- package/source/Manyfest-SchemaManipulation.js +3 -3
- package/source/Manyfest.js +42 -13
- package/test/Manyfest_Object_Delete_tests.js +96 -0
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
6
6
|
let libPrecedent = require('precedent');
|
|
7
|
+
let fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Object Address Resolver
|
|
10
|
-
*
|
|
10
|
+
* Object Address Resolver - GetValue
|
|
11
|
+
*
|
|
11
12
|
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
12
13
|
* be extremely clear what is going on in the recursion for
|
|
13
14
|
* each of the three address resolution functions.
|
|
14
|
-
*
|
|
15
|
+
*
|
|
15
16
|
* Although there is some opportunity to repeat ourselves a
|
|
16
17
|
* bit less in this codebase (e.g. with detection of arrays
|
|
17
18
|
* versus objects versus direct properties), it can make
|
|
@@ -19,13 +20,13 @@ let libPrecedent = require('precedent');
|
|
|
19
20
|
* optimizes out almost anything repeated in here. So please
|
|
20
21
|
* be kind and rewind... meaning please keep the codebase less
|
|
21
22
|
* terse and more verbose so humans can comprehend it.
|
|
22
|
-
*
|
|
23
|
-
* TODO: Once we validate this pattern is good to go, break these out into
|
|
23
|
+
*
|
|
24
|
+
* TODO: Once we validate this pattern is good to go, break these out into
|
|
24
25
|
* three separate modules.
|
|
25
26
|
*
|
|
26
|
-
* @class
|
|
27
|
+
* @class ManyfestObjectAddressResolverGetValue
|
|
27
28
|
*/
|
|
28
|
-
class
|
|
29
|
+
class ManyfestObjectAddressResolverGetValue
|
|
29
30
|
{
|
|
30
31
|
constructor(pInfoLog, pErrorLog)
|
|
31
32
|
{
|
|
@@ -35,211 +36,8 @@ class ManyfestObjectAddressResolver
|
|
|
35
36
|
|
|
36
37
|
this.elucidatorSolver = false;
|
|
37
38
|
this.elucidatorSolverState = {};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// When a boxed property is passed in, it should have quotes of some
|
|
41
|
-
// kind around it.
|
|
42
|
-
//
|
|
43
|
-
// For instance:
|
|
44
|
-
// MyValues['Name']
|
|
45
|
-
// MyValues["Age"]
|
|
46
|
-
// MyValues[`Cost`]
|
|
47
|
-
//
|
|
48
|
-
// This function removes the wrapping quotes.
|
|
49
|
-
//
|
|
50
|
-
// Please note it *DOES NOT PARSE* template literals, so backticks just
|
|
51
|
-
// end up doing the same thing as other quote types.
|
|
52
|
-
//
|
|
53
|
-
// TODO: Should template literals be processed? If so what state do they have access to?
|
|
54
|
-
cleanWrapCharacters (pCharacter, pString)
|
|
55
|
-
{
|
|
56
|
-
if (pString.startsWith(pCharacter) && pString.endsWith(pCharacter))
|
|
57
|
-
{
|
|
58
|
-
return pString.substring(1, pString.length - 1);
|
|
59
|
-
}
|
|
60
|
-
else
|
|
61
|
-
{
|
|
62
|
-
return pString;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check if an address exists.
|
|
67
|
-
//
|
|
68
|
-
// This is necessary because the getValueAtAddress function is ambiguous on
|
|
69
|
-
// whether the element/property is actually there or not (it returns
|
|
70
|
-
// undefined whether the property exists or not). This function checks for
|
|
71
|
-
// existance and returns true or false dependent.
|
|
72
|
-
checkAddressExists (pObject, pAddress)
|
|
73
|
-
{
|
|
74
|
-
// TODO: Should these throw an error?
|
|
75
|
-
// Make sure pObject is an object
|
|
76
|
-
if (typeof(pObject) != 'object') return false;
|
|
77
|
-
// Make sure pAddress is a string
|
|
78
|
-
if (typeof(pAddress) != 'string') return false;
|
|
79
|
-
|
|
80
|
-
// TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
|
|
81
|
-
let tmpSeparatorIndex = pAddress.indexOf('.');
|
|
82
|
-
|
|
83
|
-
// This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
|
|
84
|
-
if (tmpSeparatorIndex == -1)
|
|
85
|
-
{
|
|
86
|
-
// Check if the address refers to a boxed property
|
|
87
|
-
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
88
|
-
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
89
|
-
// Boxed elements look like this:
|
|
90
|
-
// MyValues[10]
|
|
91
|
-
// MyValues['Name']
|
|
92
|
-
// MyValues["Age"]
|
|
93
|
-
// MyValues[`Cost`]
|
|
94
|
-
//
|
|
95
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
96
|
-
// The requirements to detect a boxed element are:
|
|
97
|
-
// 1) The start bracket is after character 0
|
|
98
|
-
if ((tmpBracketStartIndex > 0)
|
|
99
|
-
// 2) The end bracket has something between them
|
|
100
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
101
|
-
// 3) There is data
|
|
102
|
-
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
103
|
-
{
|
|
104
|
-
// The "Name" of the Object contained too the left of the bracket
|
|
105
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
106
|
-
|
|
107
|
-
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
108
|
-
// This is a rare case where Arrays testing as Objects is useful
|
|
109
|
-
if (typeof(pObject[tmpBoxedPropertyName]) !== 'object')
|
|
110
|
-
{
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// The "Reference" to the property within it, either an array element or object property
|
|
115
|
-
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
|
|
116
|
-
// Attempt to parse the reference as a number, which will be used as an array element
|
|
117
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
118
|
-
|
|
119
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
120
|
-
// This seems confusing to me at first read, so explaination:
|
|
121
|
-
// Is the Boxed Object an Array? TRUE
|
|
122
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
123
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
124
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
|
|
125
|
-
{
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
130
|
-
// otherwise we will try to treat it as a dynamic object property.
|
|
131
|
-
if (isNaN(tmpBoxedPropertyNumber))
|
|
132
|
-
{
|
|
133
|
-
// This isn't a number ... let's treat it as a dynamic object property.
|
|
134
|
-
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
135
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
136
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
137
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
138
|
-
|
|
139
|
-
// Check if the property exists.
|
|
140
|
-
return pObject[tmpBoxedPropertyName].hasOwnProperty(tmpBoxedPropertyReference);
|
|
141
|
-
}
|
|
142
|
-
else
|
|
143
|
-
{
|
|
144
|
-
// Use the new in operator to see if the element is in the array
|
|
145
|
-
return (tmpBoxedPropertyNumber in pObject[tmpBoxedPropertyName]);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
else
|
|
149
|
-
{
|
|
150
|
-
// Check if the property exists
|
|
151
|
-
return pObject.hasOwnProperty(pAddress);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else
|
|
155
|
-
{
|
|
156
|
-
let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
157
|
-
let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
|
|
158
39
|
|
|
159
|
-
|
|
160
|
-
// Check if it's a boxed property
|
|
161
|
-
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
162
|
-
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
163
|
-
// Boxed elements look like this:
|
|
164
|
-
// MyValues[42]
|
|
165
|
-
// MyValues['Color']
|
|
166
|
-
// MyValues["Weight"]
|
|
167
|
-
// MyValues[`Diameter`]
|
|
168
|
-
//
|
|
169
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
170
|
-
// The requirements to detect a boxed element are:
|
|
171
|
-
// 1) The start bracket is after character 0
|
|
172
|
-
if ((tmpBracketStartIndex > 0)
|
|
173
|
-
// 2) The end bracket has something between them
|
|
174
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
175
|
-
// 3) There is data
|
|
176
|
-
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
177
|
-
{
|
|
178
|
-
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
179
|
-
|
|
180
|
-
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
|
|
181
|
-
|
|
182
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
183
|
-
|
|
184
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
185
|
-
// This seems confusing to me at first read, so explaination:
|
|
186
|
-
// Is the Boxed Object an Array? TRUE
|
|
187
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
188
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
189
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
190
|
-
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
191
|
-
// BUT
|
|
192
|
-
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
193
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
194
|
-
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
195
|
-
// BUT
|
|
196
|
-
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
197
|
-
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
198
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
|
|
199
|
-
{
|
|
200
|
-
// Because this is an impossible address, the property doesn't exist
|
|
201
|
-
// TODO: Should we throw an error in this condition?
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
//This is a bracketed value
|
|
206
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
207
|
-
// otherwise we will try to reat it as a dynamic object property.
|
|
208
|
-
if (isNaN(tmpBoxedPropertyNumber))
|
|
209
|
-
{
|
|
210
|
-
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
211
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
212
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
213
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
214
|
-
|
|
215
|
-
// Recurse directly into the subobject
|
|
216
|
-
return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress);
|
|
217
|
-
}
|
|
218
|
-
else
|
|
219
|
-
{
|
|
220
|
-
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
221
|
-
return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// If there is an object property already named for the sub object, but it isn't an object
|
|
226
|
-
// then the system can't set the value in there. Error and abort!
|
|
227
|
-
if (pObject.hasOwnProperty(tmpSubObjectName) && typeof(pObject[tmpSubObjectName]) !== 'object')
|
|
228
|
-
{
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
else if (pObject.hasOwnProperty(tmpSubObjectName))
|
|
232
|
-
{
|
|
233
|
-
// If there is already a subobject pass that to the recursive thingy
|
|
234
|
-
return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress);
|
|
235
|
-
}
|
|
236
|
-
else
|
|
237
|
-
{
|
|
238
|
-
// Create a subobject and then pass that
|
|
239
|
-
pObject[tmpSubObjectName] = {};
|
|
240
|
-
return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
40
|
+
this.cleanWrapCharacters = fCleanWrapCharacters;
|
|
243
41
|
}
|
|
244
42
|
|
|
245
43
|
checkFilters(pAddress, pRecord)
|
|
@@ -264,14 +62,14 @@ class ManyfestObjectAddressResolver
|
|
|
264
62
|
let tmpFilterState = (
|
|
265
63
|
{
|
|
266
64
|
Record: pRecord,
|
|
267
|
-
keepRecord: true
|
|
65
|
+
keepRecord: true
|
|
268
66
|
});
|
|
269
67
|
|
|
270
68
|
// This is about as complex as it gets.
|
|
271
69
|
// TODO: Optimize this so it is only initialized once.
|
|
272
70
|
// TODO: That means figuring out a healthy pattern for passing in state to this
|
|
273
71
|
tmpPrecedent.addPattern('<<~~', '~~>>',
|
|
274
|
-
(pInstructionHash) =>
|
|
72
|
+
(pInstructionHash) =>
|
|
275
73
|
{
|
|
276
74
|
// This is for internal config on the solution steps. Right now config is not shared across steps.
|
|
277
75
|
if (this.elucidatorSolverState.hasOwnProperty(pInstructionHash))
|
|
@@ -281,7 +79,7 @@ class ManyfestObjectAddressResolver
|
|
|
281
79
|
this.elucidatorSolver.solveInternalOperation('Custom', pInstructionHash, tmpFilterState);
|
|
282
80
|
});
|
|
283
81
|
tmpPrecedent.addPattern('<<~?', '?~>>',
|
|
284
|
-
(pMagicSearchExpression) =>
|
|
82
|
+
(pMagicSearchExpression) =>
|
|
285
83
|
{
|
|
286
84
|
if (typeof(pMagicSearchExpression) !== 'string')
|
|
287
85
|
{
|
|
@@ -314,8 +112,8 @@ class ManyfestObjectAddressResolver
|
|
|
314
112
|
{
|
|
315
113
|
"Namespace": "Logic",
|
|
316
114
|
"Instruction": "if",
|
|
317
|
-
|
|
318
|
-
"InputHashAddressMap":
|
|
115
|
+
|
|
116
|
+
"InputHashAddressMap":
|
|
319
117
|
{
|
|
320
118
|
// This is ... dynamically assigning the address in the instruction
|
|
321
119
|
// The complexity is astounding.
|
|
@@ -376,10 +174,10 @@ class ManyfestObjectAddressResolver
|
|
|
376
174
|
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
377
175
|
// The requirements to detect a boxed element are:
|
|
378
176
|
// 1) The start bracket is after character 0
|
|
379
|
-
if ((tmpBracketStartIndex > 0)
|
|
177
|
+
if ((tmpBracketStartIndex > 0)
|
|
380
178
|
// 2) The end bracket has something between them
|
|
381
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
382
|
-
// 3) There is data
|
|
179
|
+
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
180
|
+
// 3) There is data
|
|
383
181
|
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
384
182
|
{
|
|
385
183
|
// The "Name" of the Object contained too the left of the bracket
|
|
@@ -427,9 +225,9 @@ class ManyfestObjectAddressResolver
|
|
|
427
225
|
}
|
|
428
226
|
// The requirements to detect a boxed set element are:
|
|
429
227
|
// 1) The start bracket is after character 0
|
|
430
|
-
else if ((tmpBracketStartIndex > 0)
|
|
228
|
+
else if ((tmpBracketStartIndex > 0)
|
|
431
229
|
// 2) The end bracket is after the start bracket
|
|
432
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
230
|
+
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
433
231
|
// 3) There is nothing in the brackets
|
|
434
232
|
&& (tmpBracketStopIndex - tmpBracketStartIndex == 1))
|
|
435
233
|
{
|
|
@@ -493,10 +291,10 @@ class ManyfestObjectAddressResolver
|
|
|
493
291
|
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
494
292
|
// The requirements to detect a boxed element are:
|
|
495
293
|
// 1) The start bracket is after character 0
|
|
496
|
-
if ((tmpBracketStartIndex > 0)
|
|
294
|
+
if ((tmpBracketStartIndex > 0)
|
|
497
295
|
// 2) The end bracket has something between them
|
|
498
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
499
|
-
// 3) There is data
|
|
296
|
+
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
297
|
+
// 3) There is data
|
|
500
298
|
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
501
299
|
{
|
|
502
300
|
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
@@ -555,9 +353,9 @@ class ManyfestObjectAddressResolver
|
|
|
555
353
|
}
|
|
556
354
|
// The requirements to detect a boxed set element are:
|
|
557
355
|
// 1) The start bracket is after character 0
|
|
558
|
-
else if ((tmpBracketStartIndex > 0)
|
|
356
|
+
else if ((tmpBracketStartIndex > 0)
|
|
559
357
|
// 2) The end bracket is after the start bracket
|
|
560
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
358
|
+
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
561
359
|
// 3) There is nothing in the brackets
|
|
562
360
|
&& (tmpBracketStopIndex - tmpBracketStartIndex == 1))
|
|
563
361
|
{
|
|
@@ -579,7 +377,7 @@ class ManyfestObjectAddressResolver
|
|
|
579
377
|
{
|
|
580
378
|
let tmpPropertyParentAddress = `${tmpParentAddress}[${i}]`;
|
|
581
379
|
let tmpValue = this.getValueAtAddress(pObject[tmpBoxedPropertyName][i], tmpNewAddress, tmpPropertyParentAddress);
|
|
582
|
-
|
|
380
|
+
|
|
583
381
|
tmpContainerObject[`${tmpPropertyParentAddress}.${tmpNewAddress}`] = tmpValue;
|
|
584
382
|
}
|
|
585
383
|
|
|
@@ -610,7 +408,7 @@ class ManyfestObjectAddressResolver
|
|
|
610
408
|
{
|
|
611
409
|
let tmpPropertyParentAddress = `${tmpParentAddress}.${tmpObjectPropertyKeys[i]}`;
|
|
612
410
|
let tmpValue = this.getValueAtAddress(pObject[tmpObjectPropertyName][tmpObjectPropertyKeys[i]], tmpNewAddress, tmpPropertyParentAddress);
|
|
613
|
-
|
|
411
|
+
|
|
614
412
|
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
615
413
|
let tmpKeepRecord = this.checkFilters(pAddress, tmpValue);
|
|
616
414
|
if (tmpKeepRecord)
|
|
@@ -645,181 +443,6 @@ class ManyfestObjectAddressResolver
|
|
|
645
443
|
}
|
|
646
444
|
}
|
|
647
445
|
}
|
|
648
|
-
|
|
649
|
-
// Set the value of an element at an address
|
|
650
|
-
setValueAtAddress (pObject, pAddress, pValue)
|
|
651
|
-
{
|
|
652
|
-
// Make sure pObject is an object
|
|
653
|
-
if (typeof(pObject) != 'object') return false;
|
|
654
|
-
// Make sure pAddress is a string
|
|
655
|
-
if (typeof(pAddress) != 'string') return false;
|
|
656
|
-
|
|
657
|
-
let tmpSeparatorIndex = pAddress.indexOf('.');
|
|
658
|
-
|
|
659
|
-
if (tmpSeparatorIndex == -1)
|
|
660
|
-
{
|
|
661
|
-
// Check if it's a boxed property
|
|
662
|
-
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
663
|
-
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
664
|
-
// Boxed elements look like this:
|
|
665
|
-
// MyValues[10]
|
|
666
|
-
// MyValues['Name']
|
|
667
|
-
// MyValues["Age"]
|
|
668
|
-
// MyValues[`Cost`]
|
|
669
|
-
//
|
|
670
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
671
|
-
// The requirements to detect a boxed element are:
|
|
672
|
-
// 1) The start bracket is after character 0
|
|
673
|
-
if ((tmpBracketStartIndex > 0)
|
|
674
|
-
// 2) The end bracket has something between them
|
|
675
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
676
|
-
// 3) There is data
|
|
677
|
-
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
678
|
-
{
|
|
679
|
-
// The "Name" of the Object contained too the left of the bracket
|
|
680
|
-
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
681
|
-
|
|
682
|
-
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
683
|
-
// This is a rare case where Arrays testing as Objects is useful
|
|
684
|
-
if (typeof(pObject[tmpBoxedPropertyName]) !== 'object')
|
|
685
|
-
{
|
|
686
|
-
return false;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// The "Reference" to the property within it, either an array element or object property
|
|
690
|
-
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
|
|
691
|
-
// Attempt to parse the reference as a number, which will be used as an array element
|
|
692
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
693
|
-
|
|
694
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
695
|
-
// This seems confusing to me at first read, so explaination:
|
|
696
|
-
// Is the Boxed Object an Array? TRUE
|
|
697
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
698
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
699
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
|
|
700
|
-
{
|
|
701
|
-
return false;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
705
|
-
// otherwise we will try to treat it as a dynamic object property.
|
|
706
|
-
if (isNaN(tmpBoxedPropertyNumber))
|
|
707
|
-
{
|
|
708
|
-
// This isn't a number ... let's treat it as a dynamic object property.
|
|
709
|
-
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
710
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
711
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
712
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
713
|
-
|
|
714
|
-
// Return the value in the property
|
|
715
|
-
pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference] = pValue;
|
|
716
|
-
return true;
|
|
717
|
-
}
|
|
718
|
-
else
|
|
719
|
-
{
|
|
720
|
-
pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber] = pValue;
|
|
721
|
-
return true;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
else
|
|
725
|
-
{
|
|
726
|
-
// Now is the time in recursion to set the value in the object
|
|
727
|
-
pObject[pAddress] = pValue;
|
|
728
|
-
return true;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
else
|
|
732
|
-
{
|
|
733
|
-
let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
734
|
-
let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
|
|
735
|
-
|
|
736
|
-
// Test if the tmpNewAddress is an array or object
|
|
737
|
-
// Check if it's a boxed property
|
|
738
|
-
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
739
|
-
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
740
|
-
// Boxed elements look like this:
|
|
741
|
-
// MyValues[42]
|
|
742
|
-
// MyValues['Color']
|
|
743
|
-
// MyValues["Weight"]
|
|
744
|
-
// MyValues[`Diameter`]
|
|
745
|
-
//
|
|
746
|
-
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
747
|
-
// The requirements to detect a boxed element are:
|
|
748
|
-
// 1) The start bracket is after character 0
|
|
749
|
-
if ((tmpBracketStartIndex > 0)
|
|
750
|
-
// 2) The end bracket has something between them
|
|
751
|
-
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
752
|
-
// 3) There is data
|
|
753
|
-
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
754
|
-
{
|
|
755
|
-
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
756
|
-
|
|
757
|
-
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
|
|
758
|
-
|
|
759
|
-
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
760
|
-
|
|
761
|
-
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
762
|
-
// This seems confusing to me at first read, so explaination:
|
|
763
|
-
// Is the Boxed Object an Array? TRUE
|
|
764
|
-
// And is the Reference inside the boxed Object not a number? TRUE
|
|
765
|
-
// --> So when these are in agreement, it's an impossible access state
|
|
766
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
767
|
-
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
768
|
-
// BUT
|
|
769
|
-
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
770
|
-
// This could be a failure in the recursion chain because they passed something like this in:
|
|
771
|
-
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
772
|
-
// BUT
|
|
773
|
-
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
774
|
-
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
775
|
-
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
|
|
776
|
-
{
|
|
777
|
-
return false;
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
//This is a bracketed value
|
|
781
|
-
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
782
|
-
// otherwise we will try to reat it as a dynamic object property.
|
|
783
|
-
if (isNaN(tmpBoxedPropertyNumber))
|
|
784
|
-
{
|
|
785
|
-
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
786
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
787
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
788
|
-
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
789
|
-
|
|
790
|
-
// Recurse directly into the subobject
|
|
791
|
-
return this.setValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, pValue);
|
|
792
|
-
}
|
|
793
|
-
else
|
|
794
|
-
{
|
|
795
|
-
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
796
|
-
return this.setValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, pValue);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// If there is an object property already named for the sub object, but it isn't an object
|
|
801
|
-
// then the system can't set the value in there. Error and abort!
|
|
802
|
-
if (pObject.hasOwnProperty(tmpSubObjectName) && typeof(pObject[tmpSubObjectName]) !== 'object')
|
|
803
|
-
{
|
|
804
|
-
if (!pObject.hasOwnProperty('__ERROR'))
|
|
805
|
-
pObject['__ERROR'] = {};
|
|
806
|
-
// Put it in an error object so data isn't lost
|
|
807
|
-
pObject['__ERROR'][pAddress] = pValue;
|
|
808
|
-
return false;
|
|
809
|
-
}
|
|
810
|
-
else if (pObject.hasOwnProperty(tmpSubObjectName))
|
|
811
|
-
{
|
|
812
|
-
// If there is already a subobject pass that to the recursive thingy
|
|
813
|
-
return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
|
|
814
|
-
}
|
|
815
|
-
else
|
|
816
|
-
{
|
|
817
|
-
// Create a subobject and then pass that
|
|
818
|
-
pObject[tmpSubObjectName] = {};
|
|
819
|
-
return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
446
|
};
|
|
824
447
|
|
|
825
|
-
module.exports =
|
|
448
|
+
module.exports = ManyfestObjectAddressResolverGetValue;
|