manyfest 1.0.30 → 1.0.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "manyfest",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "JSON Object Manifest for Data Description and Parsing",
5
5
  "main": "source/Manyfest.js",
6
6
  "scripts": {
@@ -1,7 +1,12 @@
1
1
  /**
2
2
  * @author <steven@velozo.com>
3
3
  */
4
- let libSimpleLog = require('./Manyfest-LogToConsole.js');
4
+ const libSimpleLog = require('./Manyfest-LogToConsole.js');
5
+ // This is for resolving functions mid-address
6
+ const libGetObjectValue = require('./Manyfest-ObjectAddress-GetValue.js');
7
+
8
+ // TODO: Just until this is a fable service.
9
+ let _MockFable = { DataFormat: require('./Manyfest-ObjectAddress-Parser.js') };
5
10
 
6
11
  /**
7
12
  * Object Address Resolver
@@ -25,6 +30,7 @@ class ManyfestObjectAddressResolverCheckAddressExists
25
30
  {
26
31
  constructor()
27
32
  {
33
+ this.getObjectValueClass = new libGetObjectValue(libSimpleLog, libSimpleLog);
28
34
  }
29
35
 
30
36
  // Check if an address exists.
@@ -33,7 +39,7 @@ class ManyfestObjectAddressResolverCheckAddressExists
33
39
  // whether the element/property is actually there or not (it returns
34
40
  // undefined whether the property exists or not). This function checks for
35
41
  // existance and returns true or false dependent.
36
- checkAddressExists (pObject, pAddress)
42
+ checkAddressExists (pObject, pAddress, pRootObject)
37
43
  {
38
44
  // TODO: Should these throw an error?
39
45
  // Make sure pObject is an object
@@ -41,15 +47,51 @@ class ManyfestObjectAddressResolverCheckAddressExists
41
47
  // Make sure pAddress is a string
42
48
  if (typeof(pAddress) != 'string') return false;
43
49
 
44
- // TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
45
- let tmpSeparatorIndex = pAddress.indexOf('.');
50
+ // Set the root object to the passed-in object if it isn't set yet. This is expected to be the root object.
51
+ // NOTE: This was added to support functions mid-stream
52
+ let tmpRootObject = (typeof(pRootObject) == 'undefined') ? pObject : pRootObject;
53
+
54
+ // DONE: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
55
+ let tmpAddressPartBeginning = _MockFable.DataFormat.stringGetFirstSegment(pAddress);
46
56
 
47
57
  // This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
48
- if (tmpSeparatorIndex == -1)
58
+ if (tmpAddressPartBeginning.length == pAddress.length)
49
59
  {
50
60
  // Check if the address refers to a boxed property
51
61
  let tmpBracketStartIndex = pAddress.indexOf('[');
52
62
  let tmpBracketStopIndex = pAddress.indexOf(']');
63
+
64
+ // Check if there is a function somewhere in the address... parenthesis start should only be in a function
65
+ let tmpFunctionStartIndex = pAddress.indexOf('(');
66
+
67
+ // NOTE THAT FUNCTIONS MUST RESOLVE FIRST
68
+ // Functions look like this
69
+ // MyFunction()
70
+ // MyFunction(Some.Address)
71
+ // MyFunction(Some.Address,Some.Other.Address)
72
+ // MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
73
+ //
74
+ // This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
75
+ // To heck with that. This is a simple function call.
76
+ //
77
+ // The requirements to detect a function are:
78
+ // 1) The start bracket is after character 0
79
+ if ((tmpFunctionStartIndex > 0)
80
+ // 2) The end bracket is after the start bracket
81
+ && (_MockFable.DataFormat.stringCountEnclosures(pAddress) > 0))
82
+ {
83
+ let tmpFunctionAddress = pAddress.substring(0, tmpFunctionStartIndex).trim();
84
+
85
+ if ((pObject.hasOwnProperty(tmpFunctionAddress)) && (typeof(pObject[tmpFunctionAddress]) == 'function'))
86
+ {
87
+ return true;
88
+ }
89
+ else
90
+ {
91
+ // The address suggests it is a function, but it is not.
92
+ return false;
93
+ }
94
+ }
53
95
  // Boxed elements look like this:
54
96
  // MyValues[10]
55
97
  // MyValues['Name']
@@ -59,7 +101,7 @@ class ManyfestObjectAddressResolverCheckAddressExists
59
101
  // When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
60
102
  // The requirements to detect a boxed element are:
61
103
  // 1) The start bracket is after character 0
62
- if ((tmpBracketStartIndex > 0)
104
+ else if ((tmpBracketStartIndex > 0)
63
105
  // 2) The end bracket has something between them
64
106
  && (tmpBracketStopIndex > tmpBracketStartIndex)
65
107
  // 3) There is data
@@ -117,13 +159,67 @@ class ManyfestObjectAddressResolverCheckAddressExists
117
159
  }
118
160
  else
119
161
  {
120
- let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
121
- let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
162
+ let tmpSubObjectName = tmpAddressPartBeginning;
163
+ let tmpNewAddress = pAddress.substring(tmpAddressPartBeginning.length+1);
122
164
 
123
165
  // Test if the tmpNewAddress is an array or object
124
166
  // Check if it's a boxed property
125
167
  let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
126
168
  let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
169
+
170
+ // Check if there is a function somewhere in the address... parenthesis start should only be in a function
171
+ let tmpFunctionStartIndex = tmpSubObjectName.indexOf('(');
172
+
173
+ // NOTE THAT FUNCTIONS MUST RESOLVE FIRST
174
+ // Functions look like this
175
+ // MyFunction()
176
+ // MyFunction(Some.Address)
177
+ // MyFunction(Some.Address,Some.Other.Address)
178
+ // MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
179
+ //
180
+ // This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
181
+ // To heck with that. This is a simple function call.
182
+ //
183
+ // The requirements to detect a function are:
184
+ // 1) The start bracket is after character 0
185
+ if ((tmpFunctionStartIndex > 0)
186
+ // 2) The end bracket is after the start bracket
187
+ && (_MockFable.DataFormat.stringCountEnclosures(tmpSubObjectName) > 0))
188
+ {
189
+ let tmpFunctionAddress = tmpSubObjectName.substring(0, tmpFunctionStartIndex).trim();
190
+ //tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
191
+
192
+ if (!typeof(pObject[tmpFunctionAddress]) == 'function')
193
+ {
194
+ // The address suggests it is a function, but it is not.
195
+ return false;
196
+ }
197
+
198
+ // Now see if the function has arguments.
199
+ // Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
200
+ let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(tmpSubObjectName.substring(tmpFunctionAddress.length), 0), ',');
201
+ if ((tmpFunctionArguments.length == 0) || (tmpFunctionArguments[0] == ''))
202
+ {
203
+ // No arguments... just call the function (bound to the scope of the object it is contained withing)
204
+ return this.checkAddressExists(pObject[tmpFunctionAddress].apply(pObject), tmpNewAddress, tmpRootObject);
205
+ }
206
+ else
207
+ {
208
+ let tmpArgumentValues = [];
209
+
210
+ let tmpRootObject = (typeof(pRootObject) == 'undefined') ? pObject : pRootObject;
211
+
212
+ // Now get the value for each argument
213
+ for (let i = 0; i < tmpFunctionArguments.length; i++)
214
+ {
215
+ // Resolve the values for each subsequent entry
216
+ // NOTE: This is where the resolves get really tricky. Recursion within recursion. Programming gom jabbar, yo.
217
+ tmpArgumentValues.push(this.getObjectValueClass.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
218
+ }
219
+
220
+ return this.checkAddressExists(pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues), tmpNewAddress, tmpRootObject);
221
+ }
222
+ }
127
223
  // Boxed elements look like this:
128
224
  // MyValues[42]
129
225
  // MyValues['Color']
@@ -133,7 +229,7 @@ class ManyfestObjectAddressResolverCheckAddressExists
133
229
  // When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
134
230
  // The requirements to detect a boxed element are:
135
231
  // 1) The start bracket is after character 0
136
- if ((tmpBracketStartIndex > 0)
232
+ else if ((tmpBracketStartIndex > 0)
137
233
  // 2) The end bracket has something between them
138
234
  && (tmpBracketStopIndex > tmpBracketStartIndex)
139
235
  // 3) There is data
@@ -177,12 +273,12 @@ class ManyfestObjectAddressResolverCheckAddressExists
177
273
  tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
178
274
 
179
275
  // Recurse directly into the subobject
180
- return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress);
276
+ return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, tmpRootObject);
181
277
  }
182
278
  else
183
279
  {
184
280
  // We parsed a valid number out of the boxed property name, so recurse into the array
185
- return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress);
281
+ return this.checkAddressExists(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, tmpRootObject);
186
282
  }
187
283
  }
188
284
 
@@ -195,13 +291,13 @@ class ManyfestObjectAddressResolverCheckAddressExists
195
291
  else if (pObject.hasOwnProperty(tmpSubObjectName))
196
292
  {
197
293
  // If there is already a subobject pass that to the recursive thingy
198
- return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress);
294
+ return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress, tmpRootObject);
199
295
  }
200
296
  else
201
297
  {
202
298
  // Create a subobject and then pass that
203
299
  pObject[tmpSubObjectName] = {};
204
- return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress);
300
+ return this.checkAddressExists(pObject[tmpSubObjectName], tmpNewAddress, tmpRootObject);
205
301
  }
206
302
  }
207
303
  }
@@ -235,8 +235,6 @@ class ManyfestObjectAddressResolverDeleteValue
235
235
  {
236
236
  return false;
237
237
  }
238
-
239
-
240
238
  //This is a bracketed value
241
239
  // 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
242
240
  // otherwise we will try to reat it as a dynamic object property.
@@ -3,7 +3,9 @@
3
3
  */
4
4
  let libSimpleLog = require('./Manyfest-LogToConsole.js');
5
5
  let fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
6
- let fParseConditionals = require(`../source/Manyfest-ParseConditionals.js`)
6
+ let fParseConditionals = require(`../source/Manyfest-ParseConditionals.js`);
7
+
8
+ let _MockFable = { DataFormat: require('./Manyfest-ObjectAddress-Parser.js') };
7
9
 
8
10
  /**
9
11
  * Object Address Resolver - GetValue
@@ -58,15 +60,15 @@ class ManyfestObjectAddressResolverGetValue
58
60
  // Set the root object to the passed-in object if it isn't set yet. This is expected to be the root object.
59
61
  let tmpRootObject = (typeof(pRootObject) == 'undefined') ? pObject : pRootObject;
60
62
 
61
- // TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
62
- let tmpSeparatorIndex = pAddress.indexOf('.');
63
+ // DONE: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
64
+ let tmpAddressPartBeginning = _MockFable.DataFormat.stringGetFirstSegment(pAddress);
63
65
 
64
66
  // Adding simple back-navigation in objects
65
- if (tmpSeparatorIndex == 0)
67
+ if (tmpAddressPartBeginning == '')
66
68
  {
67
69
  // Given an address of "Bundle.Contract.IDContract...Project.IDProject" the ... would be interpreted as two back-navigations from IDContract.
68
70
  // When the address is passed in, though, the first . is already eliminated. So we can count the dots.
69
- let tmpParentAddressParts = tmpParentAddress.split('.');
71
+ let tmpParentAddressParts = _MockFable.DataFormat.stringGetSegments(tmpParentAddress);
70
72
 
71
73
  let tmpBackNavigationCount = 0;
72
74
 
@@ -104,8 +106,10 @@ class ManyfestObjectAddressResolverGetValue
104
106
  }
105
107
 
106
108
  // This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
107
- if (tmpSeparatorIndex == -1)
109
+ if (tmpAddressPartBeginning.length == pAddress.length)
108
110
  {
111
+ // TODO: Optimize this by having these calls only happen when the previous fails.
112
+ // TODO: Alternatively look for all markers in one pass?
109
113
  // Check if the address refers to a boxed property
110
114
  let tmpBracketStartIndex = pAddress.indexOf('[');
111
115
  let tmpBracketStopIndex = pAddress.indexOf(']');
@@ -114,6 +118,72 @@ class ManyfestObjectAddressResolverGetValue
114
118
  // Note this will not work with a bracket in the same address box set
115
119
  let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
116
120
 
121
+
122
+ // Check if there is a function somewhere in the address... parenthesis start should only be in a function
123
+ let tmpFunctionStartIndex = pAddress.indexOf('(');
124
+
125
+ // NOTE THAT FUNCTIONS MUST RESOLVE FIRST
126
+ // Functions look like this
127
+ // MyFunction()
128
+ // MyFunction(Some.Address)
129
+ // MyFunction(Some.Address,Some.Other.Address)
130
+ // MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
131
+ //
132
+ // This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
133
+ // To heck with that. This is a simple function call.
134
+ //
135
+ // The requirements to detect a function are:
136
+ // 1) The start bracket is after character 0
137
+ if ((tmpFunctionStartIndex > 0)
138
+ // 2) The end bracket is after the start bracket
139
+ && (_MockFable.DataFormat.stringCountEnclosures(pAddress) > 0))
140
+ {
141
+ let tmpFunctionAddress = pAddress.substring(0, tmpFunctionStartIndex).trim();
142
+
143
+ if (!typeof(pObject[tmpFunctionAddress]) == 'function')
144
+ {
145
+ // The address suggests it is a function, but it is not.
146
+ return false;
147
+ }
148
+
149
+ // Now see if the function has arguments.
150
+ // Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
151
+ let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(pAddress.substring(tmpFunctionAddress.length), 0), ',');
152
+ if ((tmpFunctionArguments.length == 0) || (tmpFunctionArguments[0] == ''))
153
+ {
154
+ // No arguments... just call the function (bound to the scope of the object it is contained withing)
155
+ return pObject[tmpFunctionAddress].apply(pObject);
156
+ }
157
+ else
158
+ {
159
+ let tmpArgumentValues = [];
160
+
161
+ let tmpRootObject = (typeof(pRootObject) == 'undefined') ? pObject : pRootObject;
162
+
163
+ // Now get the value for each argument
164
+ for (let i = 0; i < tmpFunctionArguments.length; i++)
165
+ {
166
+ if(tmpFunctionArguments[i][0] == "'" && tmpFunctionArguments[i][tmpFunctionArguments[i].length - 1] == "'")
167
+ {
168
+ tmpArgumentValues.push(tmpFunctionArguments[i].substring(1, tmpFunctionArguments[i].length - 1));
169
+ }
170
+ else if(tmpFunctionArguments[i][0] == '"' && tmpFunctionArguments[i][tmpFunctionArguments[i].length - 1] == '"')
171
+ {
172
+ tmpArgumentValues.push(tmpFunctionArguments[i].substring(1, tmpFunctionArguments[i].length - 1));
173
+ }
174
+ else if(tmpFunctionArguments[i][0] == "`" && tmpFunctionArguments[i][tmpFunctionArguments[i].length - 1] == "`")
175
+ {
176
+ tmpArgumentValues.push(tmpFunctionArguments[i].substring(1, tmpFunctionArguments[i].length - 1));
177
+ }
178
+ else
179
+ {
180
+ tmpArgumentValues.push(this.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
181
+ }
182
+ }
183
+
184
+ return pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues);
185
+ }
186
+ }
117
187
  // Boxed elements look like this:
118
188
  // MyValues[10]
119
189
  // MyValues['Name']
@@ -123,7 +193,7 @@ class ManyfestObjectAddressResolverGetValue
123
193
  // When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
124
194
  // The requirements to detect a boxed element are:
125
195
  // 1) The start bracket is after character 0
126
- if ((tmpBracketStartIndex > 0)
196
+ else if ((tmpBracketStartIndex > 0)
127
197
  // 2) The end bracket has something between them
128
198
  && (tmpBracketStopIndex > tmpBracketStartIndex)
129
199
  // 3) There is data
@@ -197,6 +267,7 @@ class ManyfestObjectAddressResolverGetValue
197
267
  if (tmpKeepRecord)
198
268
  {
199
269
  tmpOutputArray.push(tmpInputArray[i]);
270
+
200
271
  }
201
272
  }
202
273
 
@@ -230,14 +301,69 @@ class ManyfestObjectAddressResolverGetValue
230
301
  }
231
302
  else
232
303
  {
233
- let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
234
- let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
304
+ //let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
305
+ //let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
306
+ let tmpSubObjectName = tmpAddressPartBeginning;
307
+ let tmpNewAddress = pAddress.substring(tmpAddressPartBeginning.length+1);
235
308
 
236
309
  // BOXED ELEMENTS
237
310
  // Test if the tmpNewAddress is an array or object
238
311
  // Check if it's a boxed property
239
312
  let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
240
313
  let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
314
+
315
+ // Check if there is a function somewhere in the address... parenthesis start should only be in a function
316
+ let tmpFunctionStartIndex = tmpSubObjectName.indexOf('(');
317
+
318
+ // NOTE THAT FUNCTIONS MUST RESOLVE FIRST
319
+ // Functions look like this
320
+ // MyFunction()
321
+ // MyFunction(Some.Address)
322
+ // MyFunction(Some.Address,Some.Other.Address)
323
+ // MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
324
+ //
325
+ // This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
326
+ // To heck with that. This is a simple function call.
327
+ //
328
+ // The requirements to detect a function are:
329
+ // 1) The start bracket is after character 0
330
+ if ((tmpFunctionStartIndex > 0)
331
+ // 2) The end bracket is after the start bracket
332
+ && (_MockFable.DataFormat.stringCountEnclosures(tmpSubObjectName) > 0))
333
+ {
334
+ let tmpFunctionAddress = tmpSubObjectName.substring(0, tmpFunctionStartIndex).trim();
335
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
336
+
337
+ if (!typeof(pObject[tmpFunctionAddress]) == 'function')
338
+ {
339
+ // The address suggests it is a function, but it is not.
340
+ return false;
341
+ }
342
+
343
+ // Now see if the function has arguments.
344
+ // Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
345
+ let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(tmpSubObjectName.substring(tmpFunctionAddress.length), 0), ',');
346
+ if ((tmpFunctionArguments.length == 0) || (tmpFunctionArguments[0] == ''))
347
+ {
348
+ // No arguments... just call the function (bound to the scope of the object it is contained withing)
349
+ return this.getValueAtAddress(pObject[tmpFunctionAddress].apply(pObject), tmpNewAddress, tmpParentAddress, tmpRootObject);
350
+ }
351
+ else
352
+ {
353
+ let tmpArgumentValues = [];
354
+
355
+ let tmpRootObject = (typeof(pRootObject) == 'undefined') ? pObject : pRootObject;
356
+
357
+ // Now get the value for each argument
358
+ for (let i = 0; i < tmpFunctionArguments.length; i++)
359
+ {
360
+ // Resolve the values for each subsequent entry
361
+ tmpArgumentValues.push(this.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
362
+ }
363
+
364
+ return this.getValueAtAddress(pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues), tmpNewAddress, tmpParentAddress, tmpRootObject);
365
+ }
366
+ }
241
367
  // Boxed elements look like this:
242
368
  // MyValues[42]
243
369
  // MyValues['Color']
@@ -247,7 +373,7 @@ class ManyfestObjectAddressResolverGetValue
247
373
  // When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
248
374
  // The requirements to detect a boxed element are:
249
375
  // 1) The start bracket is after character 0
250
- if ((tmpBracketStartIndex > 0)
376
+ else if ((tmpBracketStartIndex > 0)
251
377
  // 2) The end bracket has something between them
252
378
  && (tmpBracketStopIndex > tmpBracketStartIndex)
253
379
  // 3) There is data