manyfest 1.0.30 → 1.0.31
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 +1 -1
- package/source/Manyfest-ObjectAddress-CheckAddressExists.js +109 -13
- package/source/Manyfest-ObjectAddress-DeleteValue.js +0 -2
- package/source/Manyfest-ObjectAddress-GetValue.js +122 -10
- package/source/Manyfest-ObjectAddress-Parser.js +298 -0
- package/source/Manyfest-ParseConditionals.js +10 -12
- package/test/Manyfest_Embedded_Solvers_tests.js +18 -16
- package/test/Manyfest_ObjectAddress_Parser_tests.js +160 -0
- package/test/Manyfest_Object_CheckExistence_tests.js +54 -0
- package/test/Manyfest_Object_Read_tests.js +81 -0
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @author <steven@velozo.com>
|
|
3
3
|
*/
|
|
4
|
-
|
|
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
|
-
//
|
|
45
|
-
|
|
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 (
|
|
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 =
|
|
121
|
-
let tmpNewAddress = pAddress.substring(
|
|
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
|
-
//
|
|
62
|
-
let
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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,58 @@ 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
|
+
// Resolve the values for each subsequent entry
|
|
167
|
+
tmpArgumentValues.push(this.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
117
173
|
// Boxed elements look like this:
|
|
118
174
|
// MyValues[10]
|
|
119
175
|
// MyValues['Name']
|
|
@@ -123,7 +179,7 @@ class ManyfestObjectAddressResolverGetValue
|
|
|
123
179
|
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
124
180
|
// The requirements to detect a boxed element are:
|
|
125
181
|
// 1) The start bracket is after character 0
|
|
126
|
-
if ((tmpBracketStartIndex > 0)
|
|
182
|
+
else if ((tmpBracketStartIndex > 0)
|
|
127
183
|
// 2) The end bracket has something between them
|
|
128
184
|
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
129
185
|
// 3) There is data
|
|
@@ -197,6 +253,7 @@ class ManyfestObjectAddressResolverGetValue
|
|
|
197
253
|
if (tmpKeepRecord)
|
|
198
254
|
{
|
|
199
255
|
tmpOutputArray.push(tmpInputArray[i]);
|
|
256
|
+
|
|
200
257
|
}
|
|
201
258
|
}
|
|
202
259
|
|
|
@@ -230,14 +287,69 @@ class ManyfestObjectAddressResolverGetValue
|
|
|
230
287
|
}
|
|
231
288
|
else
|
|
232
289
|
{
|
|
233
|
-
let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
234
|
-
let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
|
|
290
|
+
//let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
291
|
+
//let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
|
|
292
|
+
let tmpSubObjectName = tmpAddressPartBeginning;
|
|
293
|
+
let tmpNewAddress = pAddress.substring(tmpAddressPartBeginning.length+1);
|
|
235
294
|
|
|
236
295
|
// BOXED ELEMENTS
|
|
237
296
|
// Test if the tmpNewAddress is an array or object
|
|
238
297
|
// Check if it's a boxed property
|
|
239
298
|
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
240
299
|
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
300
|
+
|
|
301
|
+
// Check if there is a function somewhere in the address... parenthesis start should only be in a function
|
|
302
|
+
let tmpFunctionStartIndex = tmpSubObjectName.indexOf('(');
|
|
303
|
+
|
|
304
|
+
// NOTE THAT FUNCTIONS MUST RESOLVE FIRST
|
|
305
|
+
// Functions look like this
|
|
306
|
+
// MyFunction()
|
|
307
|
+
// MyFunction(Some.Address)
|
|
308
|
+
// MyFunction(Some.Address,Some.Other.Address)
|
|
309
|
+
// MyFunction(Some.Address,Some.Other.Address,Some.Third.Address)
|
|
310
|
+
//
|
|
311
|
+
// This could be enhanced to allow purely numeric and string values to be passed to the function. For now,
|
|
312
|
+
// To heck with that. This is a simple function call.
|
|
313
|
+
//
|
|
314
|
+
// The requirements to detect a function are:
|
|
315
|
+
// 1) The start bracket is after character 0
|
|
316
|
+
if ((tmpFunctionStartIndex > 0)
|
|
317
|
+
// 2) The end bracket is after the start bracket
|
|
318
|
+
&& (_MockFable.DataFormat.stringCountEnclosures(tmpSubObjectName) > 0))
|
|
319
|
+
{
|
|
320
|
+
let tmpFunctionAddress = tmpSubObjectName.substring(0, tmpFunctionStartIndex).trim();
|
|
321
|
+
tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
|
|
322
|
+
|
|
323
|
+
if (!typeof(pObject[tmpFunctionAddress]) == 'function')
|
|
324
|
+
{
|
|
325
|
+
// The address suggests it is a function, but it is not.
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Now see if the function has arguments.
|
|
330
|
+
// Implementation notes: * ARGUMENTS MUST SHARE THE SAME ROOT OBJECT CONTEXT *
|
|
331
|
+
let tmpFunctionArguments = _MockFable.DataFormat.stringGetSegments(_MockFable.DataFormat.stringGetEnclosureValueByIndex(tmpSubObjectName.substring(tmpFunctionAddress.length), 0), ',');
|
|
332
|
+
if ((tmpFunctionArguments.length == 0) || (tmpFunctionArguments[0] == ''))
|
|
333
|
+
{
|
|
334
|
+
// No arguments... just call the function (bound to the scope of the object it is contained withing)
|
|
335
|
+
return this.getValueAtAddress(pObject[tmpFunctionAddress].apply(pObject), tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
336
|
+
}
|
|
337
|
+
else
|
|
338
|
+
{
|
|
339
|
+
let tmpArgumentValues = [];
|
|
340
|
+
|
|
341
|
+
let tmpRootObject = (typeof(pRootObject) == 'undefined') ? pObject : pRootObject;
|
|
342
|
+
|
|
343
|
+
// Now get the value for each argument
|
|
344
|
+
for (let i = 0; i < tmpFunctionArguments.length; i++)
|
|
345
|
+
{
|
|
346
|
+
// Resolve the values for each subsequent entry
|
|
347
|
+
tmpArgumentValues.push(this.getValueAtAddress(tmpRootObject, tmpFunctionArguments[i]));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return this.getValueAtAddress(pObject[tmpFunctionAddress].apply(pObject, tmpArgumentValues), tmpNewAddress, tmpParentAddress, tmpRootObject);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
241
353
|
// Boxed elements look like this:
|
|
242
354
|
// MyValues[42]
|
|
243
355
|
// MyValues['Color']
|
|
@@ -247,7 +359,7 @@ class ManyfestObjectAddressResolverGetValue
|
|
|
247
359
|
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
248
360
|
// The requirements to detect a boxed element are:
|
|
249
361
|
// 1) The start bracket is after character 0
|
|
250
|
-
if ((tmpBracketStartIndex > 0)
|
|
362
|
+
else if ((tmpBracketStartIndex > 0)
|
|
251
363
|
// 2) The end bracket has something between them
|
|
252
364
|
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
253
365
|
// 3) There is data
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// TODO: This is an inelegant solution to delay the rewrite of Manyfest.
|
|
2
|
+
|
|
3
|
+
// Fable 3.0 has a service for data formatting that deals well with nested enclosures.
|
|
4
|
+
|
|
5
|
+
// The Manyfest library predates fable 3.0 and the services structure of it, so the functions
|
|
6
|
+
// are more or less pure javascript and as functional as they can be made to be.
|
|
7
|
+
|
|
8
|
+
// Until we shift Manyfest to be a fable service, these three functions were pulled out of
|
|
9
|
+
// fable to aid in parsing functions with nested enclosures.
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
/**
|
|
13
|
+
* Count the number of segments in a string, respecting enclosures
|
|
14
|
+
*
|
|
15
|
+
* @param {string} pString
|
|
16
|
+
* @param {string} pSeparator
|
|
17
|
+
* @param {object} pEnclosureStartSymbolMap
|
|
18
|
+
* @param {object} pEnclosureEndSymbolMap
|
|
19
|
+
* @returns the count of segments in the string as a number
|
|
20
|
+
*/
|
|
21
|
+
stringCountSegments: (pString, pSeparator, pEnclosureStartSymbolMap, pEnclosureEndSymbolMap) =>
|
|
22
|
+
{
|
|
23
|
+
let tmpString = (typeof(pString) == 'string') ? pString : '';
|
|
24
|
+
|
|
25
|
+
let tmpSeparator = (typeof(pSeparator) == 'string') ? pSeparator : '.';
|
|
26
|
+
|
|
27
|
+
let tmpEnclosureStartSymbolMap = (typeof(pEnclosureStartSymbolMap) == 'object') ? pEnclosureStart : { '{': 0, '[': 1, '(': 2 };
|
|
28
|
+
let tmpEnclosureEndSymbolMap = (typeof(pEnclosureEndSymbolMap) == 'object') ? pEnclosureEnd : { '}': 0, ']': 1, ')': 2 };
|
|
29
|
+
|
|
30
|
+
if (pString.length < 1)
|
|
31
|
+
{
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let tmpSegmentCount = 1;
|
|
36
|
+
let tmpEnclosureStack = [];
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < tmpString.length; i++)
|
|
39
|
+
{
|
|
40
|
+
// IF This is the start of a segment
|
|
41
|
+
if ((tmpString[i] == tmpSeparator)
|
|
42
|
+
// AND we are not in a nested portion of the string
|
|
43
|
+
&& (tmpEnclosureStack.length == 0))
|
|
44
|
+
{
|
|
45
|
+
// Increment the segment count
|
|
46
|
+
tmpSegmentCount++;
|
|
47
|
+
}
|
|
48
|
+
// IF This is the start of an enclosure
|
|
49
|
+
else if (tmpEnclosureStartSymbolMap.hasOwnProperty(tmpString[i]))
|
|
50
|
+
{
|
|
51
|
+
// Add it to the stack!
|
|
52
|
+
tmpEnclosureStack.push(tmpEnclosureStartSymbolMap[tmpString[i]]);
|
|
53
|
+
}
|
|
54
|
+
// IF This is the end of an enclosure
|
|
55
|
+
else if (tmpEnclosureEndSymbolMap.hasOwnProperty(tmpString[i])
|
|
56
|
+
// AND it matches the current nest level symbol
|
|
57
|
+
&& tmpEnclosureEndSymbolMap[tmpString[i]] == tmpEnclosureStack[tmpEnclosureStack.length - 1])
|
|
58
|
+
{
|
|
59
|
+
// Pop it off the stack!
|
|
60
|
+
tmpEnclosureStack.pop();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return tmpSegmentCount;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the first segment in a string, respecting enclosures
|
|
69
|
+
*
|
|
70
|
+
* @param {string} pString
|
|
71
|
+
* @param {string} pSeparator
|
|
72
|
+
* @param {object} pEnclosureStartSymbolMap
|
|
73
|
+
* @param {object} pEnclosureEndSymbolMap
|
|
74
|
+
* @returns the first segment in the string as a string
|
|
75
|
+
*/
|
|
76
|
+
stringGetFirstSegment: (pString, pSeparator, pEnclosureStartSymbolMap, pEnclosureEndSymbolMap) =>
|
|
77
|
+
{
|
|
78
|
+
let tmpString = (typeof(pString) == 'string') ? pString : '';
|
|
79
|
+
|
|
80
|
+
let tmpSeparator = (typeof(pSeparator) == 'string') ? pSeparator : '.';
|
|
81
|
+
|
|
82
|
+
let tmpEnclosureStartSymbolMap = (typeof(pEnclosureStartSymbolMap) == 'object') ? pEnclosureStart : { '{': 0, '[': 1, '(': 2 };
|
|
83
|
+
let tmpEnclosureEndSymbolMap = (typeof(pEnclosureEndSymbolMap) == 'object') ? pEnclosureEnd : { '}': 0, ']': 1, ')': 2 };
|
|
84
|
+
|
|
85
|
+
if (pString.length < 1)
|
|
86
|
+
{
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let tmpEnclosureStack = [];
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < tmpString.length; i++)
|
|
93
|
+
{
|
|
94
|
+
// IF This is the start of a segment
|
|
95
|
+
if ((tmpString[i] == tmpSeparator)
|
|
96
|
+
// AND we are not in a nested portion of the string
|
|
97
|
+
&& (tmpEnclosureStack.length == 0))
|
|
98
|
+
{
|
|
99
|
+
// Return the segment
|
|
100
|
+
return tmpString.substring(0, i);
|
|
101
|
+
}
|
|
102
|
+
// IF This is the start of an enclosure
|
|
103
|
+
else if (tmpEnclosureStartSymbolMap.hasOwnProperty(tmpString[i]))
|
|
104
|
+
{
|
|
105
|
+
// Add it to the stack!
|
|
106
|
+
tmpEnclosureStack.push(tmpEnclosureStartSymbolMap[tmpString[i]]);
|
|
107
|
+
}
|
|
108
|
+
// IF This is the end of an enclosure
|
|
109
|
+
else if (tmpEnclosureEndSymbolMap.hasOwnProperty(tmpString[i])
|
|
110
|
+
// AND it matches the current nest level symbol
|
|
111
|
+
&& tmpEnclosureEndSymbolMap[tmpString[i]] == tmpEnclosureStack[tmpEnclosureStack.length - 1])
|
|
112
|
+
{
|
|
113
|
+
// Pop it off the stack!
|
|
114
|
+
tmpEnclosureStack.pop();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return tmpString;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get all segments in a string, respecting enclosures
|
|
123
|
+
*
|
|
124
|
+
* @param {string} pString
|
|
125
|
+
* @param {string} pSeparator
|
|
126
|
+
* @param {object} pEnclosureStartSymbolMap
|
|
127
|
+
* @param {object} pEnclosureEndSymbolMap
|
|
128
|
+
* @returns the first segment in the string as a string
|
|
129
|
+
*/
|
|
130
|
+
stringGetSegments: (pString, pSeparator, pEnclosureStartSymbolMap, pEnclosureEndSymbolMap)=>
|
|
131
|
+
{
|
|
132
|
+
let tmpString = (typeof(pString) == 'string') ? pString : '';
|
|
133
|
+
|
|
134
|
+
let tmpSeparator = (typeof(pSeparator) == 'string') ? pSeparator : '.';
|
|
135
|
+
|
|
136
|
+
let tmpEnclosureStartSymbolMap = (typeof(pEnclosureStartSymbolMap) == 'object') ? pEnclosureStart : { '{': 0, '[': 1, '(': 2 };
|
|
137
|
+
let tmpEnclosureEndSymbolMap = (typeof(pEnclosureEndSymbolMap) == 'object') ? pEnclosureEnd : { '}': 0, ']': 1, ')': 2 };
|
|
138
|
+
|
|
139
|
+
let tmpCurrentSegmentStart = 0;
|
|
140
|
+
let tmpSegmentList = [];
|
|
141
|
+
|
|
142
|
+
if (pString.length < 1)
|
|
143
|
+
{
|
|
144
|
+
return tmpSegmentList;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let tmpEnclosureStack = [];
|
|
148
|
+
|
|
149
|
+
for (let i = 0; i < tmpString.length; i++)
|
|
150
|
+
{
|
|
151
|
+
// IF This is the start of a segment
|
|
152
|
+
if ((tmpString[i] == tmpSeparator)
|
|
153
|
+
// AND we are not in a nested portion of the string
|
|
154
|
+
&& (tmpEnclosureStack.length == 0))
|
|
155
|
+
{
|
|
156
|
+
// Return the segment
|
|
157
|
+
tmpSegmentList.push(tmpString.substring(tmpCurrentSegmentStart, i));
|
|
158
|
+
tmpCurrentSegmentStart = i+1;
|
|
159
|
+
}
|
|
160
|
+
// IF This is the start of an enclosure
|
|
161
|
+
else if (tmpEnclosureStartSymbolMap.hasOwnProperty(tmpString[i]))
|
|
162
|
+
{
|
|
163
|
+
// Add it to the stack!
|
|
164
|
+
tmpEnclosureStack.push(tmpEnclosureStartSymbolMap[tmpString[i]]);
|
|
165
|
+
}
|
|
166
|
+
// IF This is the end of an enclosure
|
|
167
|
+
else if (tmpEnclosureEndSymbolMap.hasOwnProperty(tmpString[i])
|
|
168
|
+
// AND it matches the current nest level symbol
|
|
169
|
+
&& tmpEnclosureEndSymbolMap[tmpString[i]] == tmpEnclosureStack[tmpEnclosureStack.length - 1])
|
|
170
|
+
{
|
|
171
|
+
// Pop it off the stack!
|
|
172
|
+
tmpEnclosureStack.pop();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (tmpCurrentSegmentStart < tmpString.length)
|
|
177
|
+
{
|
|
178
|
+
tmpSegmentList.push(tmpString.substring(tmpCurrentSegmentStart));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return tmpSegmentList;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Count the number of enclosures in a string based on the start and end characters.
|
|
186
|
+
*
|
|
187
|
+
* If no start or end characters are specified, it will default to parentheses. If the string is not a string, it will return 0.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} pString
|
|
190
|
+
* @param {string} pEnclosureStart
|
|
191
|
+
* @param {string} pEnclosureEnd
|
|
192
|
+
* @returns the count of full in the string
|
|
193
|
+
*/
|
|
194
|
+
stringCountEnclosures: (pString, pEnclosureStart, pEnclosureEnd) =>
|
|
195
|
+
{
|
|
196
|
+
let tmpString = (typeof(pString) == 'string') ? pString : '';
|
|
197
|
+
let tmpEnclosureStart = (typeof(pEnclosureStart) == 'string') ? pEnclosureStart : '(';
|
|
198
|
+
let tmpEnclosureEnd = (typeof(pEnclosureEnd) == 'string') ? pEnclosureEnd : ')';
|
|
199
|
+
|
|
200
|
+
let tmpEnclosureCount = 0;
|
|
201
|
+
let tmpEnclosureDepth = 0;
|
|
202
|
+
for (let i = 0; i < tmpString.length; i++)
|
|
203
|
+
{
|
|
204
|
+
// This is the start of an enclosure
|
|
205
|
+
if (tmpString[i] == tmpEnclosureStart)
|
|
206
|
+
{
|
|
207
|
+
if (tmpEnclosureDepth == 0)
|
|
208
|
+
{
|
|
209
|
+
tmpEnclosureCount++;
|
|
210
|
+
}
|
|
211
|
+
tmpEnclosureDepth++;
|
|
212
|
+
}
|
|
213
|
+
else if (tmpString[i] == tmpEnclosureEnd)
|
|
214
|
+
{
|
|
215
|
+
tmpEnclosureDepth--;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return tmpEnclosureCount;
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get the value of the enclosure at the specified index.
|
|
225
|
+
*
|
|
226
|
+
* If the index is not a number, it will default to 0. If the string is not a string, it will return an empty string. If the enclosure is not found, it will return an empty string. If the enclosure
|
|
227
|
+
*
|
|
228
|
+
* @param {string} pString
|
|
229
|
+
* @param {number} pEnclosureIndexToGet
|
|
230
|
+
* @param {string} pEnclosureStart
|
|
231
|
+
* @param {string}} pEnclosureEnd
|
|
232
|
+
* @returns {string}
|
|
233
|
+
*/
|
|
234
|
+
stringGetEnclosureValueByIndex: (pString, pEnclosureIndexToGet, pEnclosureStart, pEnclosureEnd) =>
|
|
235
|
+
{
|
|
236
|
+
let tmpString = (typeof(pString) == 'string') ? pString : '';
|
|
237
|
+
let tmpEnclosureIndexToGet = (typeof(pEnclosureIndexToGet) == 'number') ? pEnclosureIndexToGet : 0;
|
|
238
|
+
let tmpEnclosureStart = (typeof(pEnclosureStart) == 'string') ? pEnclosureStart : '(';
|
|
239
|
+
let tmpEnclosureEnd = (typeof(pEnclosureEnd) == 'string') ? pEnclosureEnd : ')';
|
|
240
|
+
|
|
241
|
+
let tmpEnclosureCount = 0;
|
|
242
|
+
let tmpEnclosureDepth = 0;
|
|
243
|
+
|
|
244
|
+
let tmpMatchedEnclosureIndex = false;
|
|
245
|
+
let tmpEnclosedValueStartIndex = 0;
|
|
246
|
+
let tmpEnclosedValueEndIndex = 0;
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < tmpString.length; i++)
|
|
249
|
+
{
|
|
250
|
+
// This is the start of an enclosure
|
|
251
|
+
if (tmpString[i] == tmpEnclosureStart)
|
|
252
|
+
{
|
|
253
|
+
tmpEnclosureDepth++;
|
|
254
|
+
|
|
255
|
+
// Only count enclosures at depth 1, but still this parses both pairs of all of them.
|
|
256
|
+
if (tmpEnclosureDepth == 1)
|
|
257
|
+
{
|
|
258
|
+
tmpEnclosureCount++;
|
|
259
|
+
if (tmpEnclosureIndexToGet == (tmpEnclosureCount - 1))
|
|
260
|
+
{
|
|
261
|
+
// This is the start of *the* enclosure
|
|
262
|
+
tmpMatchedEnclosureIndex = true;
|
|
263
|
+
tmpEnclosedValueStartIndex = i;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// This is the end of an enclosure
|
|
268
|
+
else if (tmpString[i] == tmpEnclosureEnd)
|
|
269
|
+
{
|
|
270
|
+
tmpEnclosureDepth--;
|
|
271
|
+
|
|
272
|
+
// Again, only count enclosures at depth 1, but still this parses both pairs of all of them.
|
|
273
|
+
if ((tmpEnclosureDepth == 0) &&
|
|
274
|
+
tmpMatchedEnclosureIndex &&
|
|
275
|
+
(tmpEnclosedValueEndIndex <= tmpEnclosedValueStartIndex))
|
|
276
|
+
{
|
|
277
|
+
tmpEnclosedValueEndIndex = i;
|
|
278
|
+
tmpMatchedEnclosureIndex = false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (tmpEnclosureCount <= tmpEnclosureIndexToGet)
|
|
284
|
+
{
|
|
285
|
+
// Return an empty string if the enclosure is not found
|
|
286
|
+
return '';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if ((tmpEnclosedValueEndIndex > 0) && (tmpEnclosedValueEndIndex > tmpEnclosedValueStartIndex))
|
|
290
|
+
{
|
|
291
|
+
return tmpString.substring(tmpEnclosedValueStartIndex+1, tmpEnclosedValueEndIndex);
|
|
292
|
+
}
|
|
293
|
+
else
|
|
294
|
+
{
|
|
295
|
+
return tmpString.substring(tmpEnclosedValueStartIndex+1);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -18,7 +18,7 @@ const _ConditionalStanzaEnd = '?~>>';
|
|
|
18
18
|
const _ConditionalStanzaEndLength = _ConditionalStanzaEnd.length;
|
|
19
19
|
|
|
20
20
|
// Ugh dependency injection. Can't wait to make these all fable services.
|
|
21
|
-
let libObjectAddressCheckAddressExists = new (require('./Manyfest-ObjectAddress-CheckAddressExists.js'))();
|
|
21
|
+
//let libObjectAddressCheckAddressExists = new (require('./Manyfest-ObjectAddress-CheckAddressExists.js'))();
|
|
22
22
|
|
|
23
23
|
// Test the condition of a value in a record
|
|
24
24
|
const testCondition = (pManyfest, pRecord, pSearchAddress, pSearchComparator, pValue) =>
|
|
@@ -61,17 +61,15 @@ const testCondition = (pManyfest, pRecord, pSearchAddress, pSearchComparator, pV
|
|
|
61
61
|
break;
|
|
62
62
|
}
|
|
63
63
|
break;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return !libObjectAddressCheckAddressExists.checkAddressExists(pRecord, pSearchAddress);
|
|
74
|
-
break;
|
|
64
|
+
// TODO: Welcome to dependency hell. This fixes itself when we move to fable services.
|
|
65
|
+
// case 'EX':
|
|
66
|
+
// case 'EXISTS':
|
|
67
|
+
// return libObjectAddressCheckAddressExists.checkAddressExists(pRecord, pSearchAddress);
|
|
68
|
+
// break;
|
|
69
|
+
// case 'DNEX':
|
|
70
|
+
// case 'DOES_NOT_EXIST':
|
|
71
|
+
// return !libObjectAddressCheckAddressExists.checkAddressExists(pRecord, pSearchAddress);
|
|
72
|
+
// break;
|
|
75
73
|
case '!=':
|
|
76
74
|
return (pManyfest.getValueAtAddress(pRecord, pSearchAddress) != pValue);
|
|
77
75
|
break;
|
|
@@ -59,10 +59,11 @@ suite
|
|
|
59
59
|
Scope:'Archive.org',
|
|
60
60
|
Descriptors:
|
|
61
61
|
{
|
|
62
|
-
|
|
63
|
-
'files[]<<~?length,
|
|
64
|
-
'files[]<<~?length,DNEX
|
|
65
|
-
|
|
62
|
+
// TODO: STRICKEN OUT BY DEPENDENCY HELL
|
|
63
|
+
//'files[]<<~?length,EXISTS?~>>': {Name:'Files With a length Property', Hash:'FilesWithLength'},
|
|
64
|
+
// 'files[]<<~?length,DNEX?~>>': {Name:'Files Without a length Property', Hash:'FilesWithoutLength'},
|
|
65
|
+
// 'files[]<<~?length,DNEX?~>><<~?source,==,original?~>>': {Name:'Original Files With a length Property', Hash:'OriginalFilesWithLength'},
|
|
66
|
+
// 'files[]<<~?thumbnail,EXISTS?~>>': {Name:'Thumbnail Bit is Explicitly Set', Hash:'ThumbnailExplicitlySet'},
|
|
66
67
|
'files[]<<~?thumbnail,TRUE?~>>': {Name:'Thumbnail Files', Hash:'ThumbnailFiles'},
|
|
67
68
|
'files[]<<~?thumbnail,FALSE?~>>': {Name:'Not Thumbnail Files', Hash:'NotThumbnailFiles'},
|
|
68
69
|
'files[]<<~?format,LENGTH_LESS_THAN,8?~>>': {Name:'Short Format Files', Hash:'ShortFormatFiles'}
|
|
@@ -79,21 +80,22 @@ suite
|
|
|
79
80
|
Expect(tmpNotThumbnailFiles).to.be.an('array');
|
|
80
81
|
Expect(tmpNotThumbnailFiles.length).to.equal(1);
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Expect(tmpFilesWithLength
|
|
83
|
+
// TODO: Dependencies are the wurst
|
|
84
|
+
// let tmpFilesWithLength = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'FilesWithLength');
|
|
85
|
+
// Expect(tmpFilesWithLength).to.be.an('array');
|
|
86
|
+
// Expect(tmpFilesWithLength.length).to.equal(3);
|
|
85
87
|
|
|
86
|
-
let tmpOGFilesWithLength = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'OriginalFilesWithLength');
|
|
87
|
-
Expect(tmpOGFilesWithLength).to.be.an('array');
|
|
88
|
-
Expect(tmpOGFilesWithLength.length).to.equal(2);
|
|
88
|
+
// let tmpOGFilesWithLength = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'OriginalFilesWithLength');
|
|
89
|
+
// Expect(tmpOGFilesWithLength).to.be.an('array');
|
|
90
|
+
// Expect(tmpOGFilesWithLength.length).to.equal(2);
|
|
89
91
|
|
|
90
|
-
let tmpFilesWithoutLength = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'FilesWithoutLength');
|
|
91
|
-
Expect(tmpFilesWithoutLength).to.be.an('array');
|
|
92
|
-
Expect(tmpFilesWithoutLength.length).to.equal(14);
|
|
92
|
+
// let tmpFilesWithoutLength = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'FilesWithoutLength');
|
|
93
|
+
// Expect(tmpFilesWithoutLength).to.be.an('array');
|
|
94
|
+
// Expect(tmpFilesWithoutLength.length).to.equal(14);
|
|
93
95
|
|
|
94
|
-
let tmpExplicitlyExists = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'ThumbnailExplicitlySet');
|
|
95
|
-
Expect(tmpExplicitlyExists).to.be.an('array');
|
|
96
|
-
Expect(tmpExplicitlyExists.length).to.equal(2);
|
|
96
|
+
// let tmpExplicitlyExists = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'ThumbnailExplicitlySet');
|
|
97
|
+
// Expect(tmpExplicitlyExists).to.be.an('array');
|
|
98
|
+
// Expect(tmpExplicitlyExists.length).to.equal(2);
|
|
97
99
|
|
|
98
100
|
let tmpShortFormatFiles = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'ShortFormatFiles');
|
|
99
101
|
Expect(tmpShortFormatFiles).to.be.an('array');
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Mock Fable behaviors
|
|
3
|
+
*
|
|
4
|
+
* Shamelessly ripped from fable until we can get the fable service structure in place.
|
|
5
|
+
*
|
|
6
|
+
* @license MIT
|
|
7
|
+
*
|
|
8
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
var libFableDataFormatInjector = require('../source/Manyfest-ObjectAddress-Parser.js');
|
|
12
|
+
|
|
13
|
+
var Chai = require("chai");
|
|
14
|
+
var Expect = Chai.expect;
|
|
15
|
+
|
|
16
|
+
suite
|
|
17
|
+
(
|
|
18
|
+
'DataArithmatic String Tokenization',
|
|
19
|
+
function()
|
|
20
|
+
{
|
|
21
|
+
setup (()=> {} );
|
|
22
|
+
|
|
23
|
+
suite
|
|
24
|
+
(
|
|
25
|
+
'Manipulate Strings',
|
|
26
|
+
()=>
|
|
27
|
+
{
|
|
28
|
+
|
|
29
|
+
test
|
|
30
|
+
(
|
|
31
|
+
'Test counting enclosures',
|
|
32
|
+
(fTestComplete)=>
|
|
33
|
+
{
|
|
34
|
+
//let testFable = new libFable({LogStreams: false});
|
|
35
|
+
let _DataFormat = libFableDataFormatInjector;
|
|
36
|
+
Expect(_DataFormat
|
|
37
|
+
.stringCountEnclosures('Dogs (are) cool'))
|
|
38
|
+
.to.equal(1);
|
|
39
|
+
Expect(_DataFormat
|
|
40
|
+
.stringCountEnclosures('Dogs are cool'))
|
|
41
|
+
.to.equal(0);
|
|
42
|
+
// It should not count nested enclosures.
|
|
43
|
+
// Although with getEnclosureValueByIndex and recalling this, you can recursively get them.
|
|
44
|
+
Expect(_DataFormat
|
|
45
|
+
.stringCountEnclosures('There (are (many)) of these (things)'))
|
|
46
|
+
.to.equal(2);
|
|
47
|
+
Expect(_DataFormat
|
|
48
|
+
.stringCountEnclosures('There [are (many)] of these (things)'))
|
|
49
|
+
.to.equal(2);
|
|
50
|
+
// You can also specify the enclosure characters
|
|
51
|
+
Expect(_DataFormat
|
|
52
|
+
.stringCountEnclosures('There [are (many)] of these (things)', '[', ']'))
|
|
53
|
+
.to.equal(1);
|
|
54
|
+
// It does not *require* a closing character and still counts the enclosure.
|
|
55
|
+
// Hotly debated topic. A setting could be added to change this behavior.
|
|
56
|
+
Expect(_DataFormat
|
|
57
|
+
.stringCountEnclosures('There [are (many) of these (things)', '[', ']'))
|
|
58
|
+
.to.equal(1);
|
|
59
|
+
return fTestComplete();
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
test
|
|
63
|
+
(
|
|
64
|
+
'Test getting an enclosure value by index',
|
|
65
|
+
(fTestComplete)=>
|
|
66
|
+
{
|
|
67
|
+
//let testFable = new libFable({LogStreams: false});
|
|
68
|
+
let _DataFormat = libFableDataFormatInjector;
|
|
69
|
+
Expect(_DataFormat
|
|
70
|
+
.stringGetEnclosureValueByIndex('Dogs (are) cool', 0))
|
|
71
|
+
.to.equal('are');
|
|
72
|
+
Expect(_DataFormat
|
|
73
|
+
.stringGetEnclosureValueByIndex('Dogs are cool', 0))
|
|
74
|
+
.to.equal('');
|
|
75
|
+
Expect(_DataFormat
|
|
76
|
+
.stringGetEnclosureValueByIndex('There (are (many)) of these (things)', 0))
|
|
77
|
+
.to.equal('are (many)');
|
|
78
|
+
Expect(_DataFormat
|
|
79
|
+
.stringGetEnclosureValueByIndex('There [are (many)] of these (things)', 1))
|
|
80
|
+
.to.equal('things');
|
|
81
|
+
Expect(_DataFormat
|
|
82
|
+
.stringGetEnclosureValueByIndex('There [are (many)] of these (things)', 2))
|
|
83
|
+
.to.equal('');
|
|
84
|
+
Expect(_DataFormat
|
|
85
|
+
.stringGetEnclosureValueByIndex('(This enclosure is the whole string)', 0))
|
|
86
|
+
.to.equal('This enclosure is the whole string');
|
|
87
|
+
// You can also specify the enclosure characters
|
|
88
|
+
Expect(_DataFormat
|
|
89
|
+
.stringGetEnclosureValueByIndex('There [are (many)] of these (things)', 0, '[', ']'))
|
|
90
|
+
.to.equal('are (many)');
|
|
91
|
+
Expect(_DataFormat
|
|
92
|
+
.stringGetEnclosureValueByIndex('There [are (many) of these (things)', 0, '[', ']'))
|
|
93
|
+
.to.equal('are (many) of these (things)');
|
|
94
|
+
Expect(_DataFormat
|
|
95
|
+
.stringGetEnclosureValueByIndex('There (are (many) of these (things))', 0))
|
|
96
|
+
.to.equal('are (many) of these (things)');
|
|
97
|
+
return fTestComplete();
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
test
|
|
102
|
+
(
|
|
103
|
+
'Test counting segments and respecting enclosures',
|
|
104
|
+
(fTestComplete)=>
|
|
105
|
+
{
|
|
106
|
+
//let testFable = new libFable({LogStreams: false});
|
|
107
|
+
let _DataFormat = libFableDataFormatInjector;
|
|
108
|
+
Expect(_DataFormat
|
|
109
|
+
.stringCountSegments('Dogs.are.cool'))
|
|
110
|
+
.to.equal(3);
|
|
111
|
+
Expect(_DataFormat
|
|
112
|
+
.stringCountSegments('Dogs.are.cool'))
|
|
113
|
+
.to.equal(3);
|
|
114
|
+
Expect(_DataFormat
|
|
115
|
+
.stringCountSegments('Dogs.are(.)cool'))
|
|
116
|
+
.to.equal(2);
|
|
117
|
+
Expect(_DataFormat
|
|
118
|
+
.stringCountSegments('Dogs.are(.[....])co.(.)ol'))
|
|
119
|
+
.to.equal(3);
|
|
120
|
+
Expect(_DataFormat
|
|
121
|
+
.stringCountSegments('Dogs.are(.[....]),co.(.)ol', ','))
|
|
122
|
+
.to.equal(2);
|
|
123
|
+
return fTestComplete();
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
test
|
|
127
|
+
(
|
|
128
|
+
'Get the first segment of a string',
|
|
129
|
+
(fTestComplete)=>
|
|
130
|
+
{
|
|
131
|
+
//let testFable = new libFable({LogStreams: false});
|
|
132
|
+
let _DataFormat = libFableDataFormatInjector;
|
|
133
|
+
Expect(_DataFormat
|
|
134
|
+
.stringGetFirstSegment('Dogs.are.cool'))
|
|
135
|
+
.to.equal('Dogs');
|
|
136
|
+
Expect(_DataFormat
|
|
137
|
+
.stringGetFirstSegment('Dogs().are.cool'))
|
|
138
|
+
.to.equal('Dogs()');
|
|
139
|
+
Expect(_DataFormat
|
|
140
|
+
.stringGetFirstSegment('Dogs[This.That(),Equals(THEM)].are(.)cool'))
|
|
141
|
+
.to.equal('Dogs[This.That(),Equals(THEM)]');
|
|
142
|
+
Expect(_DataFormat
|
|
143
|
+
.stringGetFirstSegment('Dogs(.are(.[....])co.(.)ol'))
|
|
144
|
+
.to.equal('Dogs(.are(.[....])co.(.)ol');
|
|
145
|
+
Expect(_DataFormat
|
|
146
|
+
.stringGetFirstSegment('Dogs(.are(,[....])co.(.)ol', ','))
|
|
147
|
+
.to.equal('Dogs(.are(,[....])co.(.)ol');
|
|
148
|
+
Expect(_DataFormat
|
|
149
|
+
.stringGetFirstSegment('Dogs.are(,[....]),co.(.)ol', ','))
|
|
150
|
+
.to.equal('Dogs.are(,[....])');
|
|
151
|
+
Expect(_DataFormat
|
|
152
|
+
.stringGetFirstSegment('..Dogs.are(,[....]),co.(.)ol', '.'))
|
|
153
|
+
.to.equal('');
|
|
154
|
+
return fTestComplete();
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
);
|
|
@@ -64,6 +64,60 @@ suite
|
|
|
64
64
|
fTestComplete();
|
|
65
65
|
}
|
|
66
66
|
);
|
|
67
|
+
test
|
|
68
|
+
(
|
|
69
|
+
'Elements exist in function return values ... yiiiiikes.',
|
|
70
|
+
(fTestComplete)=>
|
|
71
|
+
{
|
|
72
|
+
// Create a compmlex mock object to check metadata on.
|
|
73
|
+
let _MockObject = (
|
|
74
|
+
{
|
|
75
|
+
"Name": "Yadda",
|
|
76
|
+
"TheFunction": (pValue) => { return `Value is: ${pValue}`; },
|
|
77
|
+
"ComplexFunction": (pValue, pOutput) => { return `Value is: ${pValue} and would output as ${pOutput}`; },
|
|
78
|
+
"Behaviors":
|
|
79
|
+
{
|
|
80
|
+
"Value": 0,
|
|
81
|
+
"Increment": function ()
|
|
82
|
+
{
|
|
83
|
+
this.Value++; return this.Value;
|
|
84
|
+
},
|
|
85
|
+
"SillyObject": function()
|
|
86
|
+
{
|
|
87
|
+
return { Cost: 1.00, Name: 'Beanie Baby', Stores: ['Aberdeen', 'Seattle', 'Tacoma'] }
|
|
88
|
+
},
|
|
89
|
+
"FormatOutput": function () { return `My magic value is: ${this.Value}`; }
|
|
90
|
+
},
|
|
91
|
+
"Manyfest":
|
|
92
|
+
{
|
|
93
|
+
Scope:'Function.Mock',
|
|
94
|
+
Descriptors:
|
|
95
|
+
{
|
|
96
|
+
"metadata.creator":
|
|
97
|
+
{
|
|
98
|
+
Name:'Creator',
|
|
99
|
+
Hash:'Creator'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"Data": _SampleDataArchiveOrgFrankenberry
|
|
104
|
+
});
|
|
105
|
+
let _Manyfest = new libManyfest(_MockObject.Manyfest);
|
|
106
|
+
|
|
107
|
+
// Function existence should just work
|
|
108
|
+
Expect(_Manyfest.checkAddressExists(_MockObject, 'Behaviors.SillyObject')).to.equal(true);
|
|
109
|
+
// If it is a function we can ask manyfest if it's a function and it will be true
|
|
110
|
+
Expect(_Manyfest.checkAddressExists(_MockObject, 'Behaviors.SillyObject()')).to.equal(true);
|
|
111
|
+
// Non functions will return false even if they exist.
|
|
112
|
+
Expect(_Manyfest.checkAddressExists(_MockObject, 'Behaviors.Value')).to.equal(true);
|
|
113
|
+
Expect(_Manyfest.checkAddressExists(_MockObject, 'Behaviors.Value()')).to.equal(false);
|
|
114
|
+
|
|
115
|
+
Expect(_Manyfest.checkAddressExists(_MockObject, 'Behaviors.SillyObject().Stores')).to.equal(true);
|
|
116
|
+
Expect(_Manyfest.checkAddressExists(_MockObject, 'Behaviors.SillyObject().Stores[0]')).to.equal(true);
|
|
117
|
+
|
|
118
|
+
fTestComplete();
|
|
119
|
+
}
|
|
120
|
+
);
|
|
67
121
|
}
|
|
68
122
|
);
|
|
69
123
|
}
|
|
@@ -194,6 +194,87 @@ suite
|
|
|
194
194
|
fTestComplete();
|
|
195
195
|
}
|
|
196
196
|
);
|
|
197
|
+
test
|
|
198
|
+
(
|
|
199
|
+
'Access Functions',
|
|
200
|
+
(fTestComplete)=>
|
|
201
|
+
{
|
|
202
|
+
let _Manyfest = new libManyfest();
|
|
203
|
+
let tmpComplexObject = (
|
|
204
|
+
{
|
|
205
|
+
"Name": "Yadda",
|
|
206
|
+
"TheFunction": (pValue) => { return `Value is: ${pValue}`; }
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
Expect(_Manyfest.getValueAtAddress(tmpComplexObject, 'Name'))
|
|
210
|
+
.to.equal('Yadda');
|
|
211
|
+
|
|
212
|
+
Expect(_Manyfest.getValueAtAddress(tmpComplexObject, 'TheFunction()'))
|
|
213
|
+
.to.equal('Value is: undefined');
|
|
214
|
+
|
|
215
|
+
Expect(_Manyfest.getValueAtAddress(tmpComplexObject, 'TheFunction(Name)'))
|
|
216
|
+
.to.equal('Value is: Yadda');
|
|
217
|
+
|
|
218
|
+
fTestComplete();
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
test
|
|
222
|
+
(
|
|
223
|
+
'Complex Functions that Mutate State and deal with Parameters',
|
|
224
|
+
(fTestComplete)=>
|
|
225
|
+
{
|
|
226
|
+
// Create a compmlex mock object to check metadata on.
|
|
227
|
+
let _MockObject = (
|
|
228
|
+
{
|
|
229
|
+
"Name": "Yadda",
|
|
230
|
+
"TheFunction": (pValue) => { return `Value is: ${pValue}`; },
|
|
231
|
+
"ComplexFunction": (pValue, pOutput) => { return `Value is: ${pValue} and would output as ${pOutput}`; },
|
|
232
|
+
"Behaviors":
|
|
233
|
+
{
|
|
234
|
+
"Value": 0,
|
|
235
|
+
"Increment": function ()
|
|
236
|
+
{
|
|
237
|
+
this.Value++; return this.Value;
|
|
238
|
+
},
|
|
239
|
+
"SillyObject": function()
|
|
240
|
+
{
|
|
241
|
+
return { Cost: 1.00, Name: 'Beanie Baby', Stores: ['Aberdeen', 'Seattle', 'Tacoma'] }
|
|
242
|
+
},
|
|
243
|
+
"FormatOutput": function () { return `My magic value is: ${this.Value}`; }
|
|
244
|
+
},
|
|
245
|
+
"Manyfest":
|
|
246
|
+
{
|
|
247
|
+
Scope:'Function.Mock',
|
|
248
|
+
Descriptors:
|
|
249
|
+
{
|
|
250
|
+
"metadata.creator":
|
|
251
|
+
{
|
|
252
|
+
Name:'Creator',
|
|
253
|
+
Hash:'Creator'
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"Data": _SampleDataArchiveOrgFrankenberry
|
|
258
|
+
});
|
|
259
|
+
let _Manyfest = new libManyfest(_MockObject.Manyfest);
|
|
260
|
+
|
|
261
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'Name')).to.equal('Yadda');
|
|
262
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'TheFunction()')).to.equal('Value is: undefined');
|
|
263
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'TheFunction(Name)')).to.equal('Value is: Yadda');
|
|
264
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'ComplexFunction(Name,Name)')).to.equal('Value is: Yadda and would output as Yadda');
|
|
265
|
+
|
|
266
|
+
// This is stupid but works
|
|
267
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'ComplexFunction(Name,TheFunction(Manyfest.Descriptors["metadata.creator"].Hash))'))
|
|
268
|
+
.to.equal('Value is: Yadda and would output as Value is: Creator');
|
|
269
|
+
|
|
270
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'Behaviors.Increment()'))
|
|
271
|
+
.to.equal(1);
|
|
272
|
+
|
|
273
|
+
Expect(_Manyfest.getValueAtAddress(_MockObject, 'Behaviors.SillyObject().Stores[0]')).to.equal('Aberdeen');
|
|
274
|
+
|
|
275
|
+
fTestComplete();
|
|
276
|
+
}
|
|
277
|
+
);
|
|
197
278
|
}
|
|
198
279
|
);
|
|
199
280
|
}
|