manyfest 1.0.7 → 1.0.9
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/code-server/config.yaml +1 -1
- package/.config/configstore/update-notifier-npm.json +1 -1
- package/package.json +5 -2
- package/source/Manyfest-ObjectAddressGeneration.js +123 -0
- package/source/Manyfest-ObjectAddressResolver.js +75 -6
- package/source/Manyfest.js +70 -4
- package/test/Manyfest_Embedded_Solvers_tests.js +86 -0
- package/test/Manyfest_Object_Read_tests.js +10 -4
- package/test/Manyfest_Object_SchemaManipulation_tests.js +46 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "manyfest",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "JSON Object Manifest for Data Description and Parsing",
|
|
5
5
|
"main": "source/Manyfest.js",
|
|
6
6
|
"scripts": {
|
|
@@ -58,7 +58,10 @@
|
|
|
58
58
|
"vinyl-buffer": "^1.0.1",
|
|
59
59
|
"vinyl-source-stream": "^2.0.0"
|
|
60
60
|
},
|
|
61
|
-
"dependencies": {
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"elucidator": "^1.0.2",
|
|
63
|
+
"precedent": "^1.0.6"
|
|
64
|
+
},
|
|
62
65
|
"author": "steven velozo <steven@velozo.com>",
|
|
63
66
|
"license": "MIT",
|
|
64
67
|
"bugs": {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* @author <steven@velozo.com>
|
|
4
|
+
*/
|
|
5
|
+
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Object Address Generation
|
|
9
|
+
*
|
|
10
|
+
* Automagically generate addresses and properties based on a passed-in object,
|
|
11
|
+
* to be used for easy creation of schemas. Meant to simplify the lives of
|
|
12
|
+
* developers wanting to create schemas without typing a bunch of stuff.
|
|
13
|
+
*
|
|
14
|
+
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
15
|
+
* be extremely clear what is going on in the recursion for
|
|
16
|
+
* each of the three address resolution functions.
|
|
17
|
+
*
|
|
18
|
+
* Although there is some opportunity to repeat ourselves a
|
|
19
|
+
* bit less in this codebase (e.g. with detection of arrays
|
|
20
|
+
* versus objects versus direct properties), it can make
|
|
21
|
+
* debugging.. challenging. The minified version of the code
|
|
22
|
+
* optimizes out almost anything repeated in here. So please
|
|
23
|
+
* be kind and rewind... meaning please keep the codebase less
|
|
24
|
+
* terse and more verbose so humans can comprehend it.
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
* @class ManyfestObjectAddressGeneration
|
|
28
|
+
*/
|
|
29
|
+
class ManyfestObjectAddressGeneration
|
|
30
|
+
{
|
|
31
|
+
constructor(pInfoLog, pErrorLog)
|
|
32
|
+
{
|
|
33
|
+
// Wire in logging
|
|
34
|
+
this.logInfo = (typeof(pInfoLog) == 'function') ? pInfoLog : libSimpleLog;
|
|
35
|
+
this.logError = (typeof(pErrorLog) == 'function') ? pErrorLog : libSimpleLog;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// generateAddressses
|
|
39
|
+
//
|
|
40
|
+
// This flattens an object into a set of key:value pairs for *EVERY SINGLE
|
|
41
|
+
// POSSIBLE ADDRESS* in the object. It can get ... really insane really
|
|
42
|
+
// quickly. This is not meant to be used directly to generate schemas, but
|
|
43
|
+
// instead as a starting point for scripts or UIs.
|
|
44
|
+
//
|
|
45
|
+
// This will return a mega set of key:value pairs with all possible schema
|
|
46
|
+
// permutations and default values (when not an object) and everything else.
|
|
47
|
+
generateAddressses (pObject, pBaseAddress, pSchema)
|
|
48
|
+
{
|
|
49
|
+
let tmpBaseAddress = (typeof(pBaseAddress) == 'string') ? pBaseAddress : '';
|
|
50
|
+
let tmpSchema = (typeof(pSchema) == 'object') ? pSchema : {};
|
|
51
|
+
|
|
52
|
+
let tmpObjectType = typeof(pObject);
|
|
53
|
+
|
|
54
|
+
let tmpSchemaObjectEntry = (
|
|
55
|
+
{
|
|
56
|
+
Address: tmpBaseAddress,
|
|
57
|
+
Hash: tmpBaseAddress,
|
|
58
|
+
Name: tmpBaseAddress,
|
|
59
|
+
// This is so scripts and UI controls can force a developer to opt-in.
|
|
60
|
+
InSchema: false
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
switch(tmpObjectType)
|
|
65
|
+
{
|
|
66
|
+
case 'string':
|
|
67
|
+
tmpSchemaObjectEntry.DataType = 'String';
|
|
68
|
+
tmpSchemaObjectEntry.Default = pObject;
|
|
69
|
+
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
70
|
+
break;
|
|
71
|
+
case 'number':
|
|
72
|
+
case 'bigint':
|
|
73
|
+
tmpSchemaObjectEntry.DataType = 'Number';
|
|
74
|
+
tmpSchemaObjectEntry.Default = pObject;
|
|
75
|
+
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
76
|
+
break;
|
|
77
|
+
case 'undefined':
|
|
78
|
+
tmpSchemaObjectEntry.DataType = 'Any';
|
|
79
|
+
tmpSchemaObjectEntry.Default = pObject;
|
|
80
|
+
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
81
|
+
break;
|
|
82
|
+
case 'object':
|
|
83
|
+
if (Array.isArray(pObject))
|
|
84
|
+
{
|
|
85
|
+
tmpSchemaObjectEntry.DataType = 'Array';
|
|
86
|
+
if (tmpBaseAddress != '')
|
|
87
|
+
{
|
|
88
|
+
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < pObject.length; i++)
|
|
92
|
+
{
|
|
93
|
+
this.generateAddressses(pObject[i], `${tmpBaseAddress}[${i}]`, tmpSchema);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
{
|
|
98
|
+
tmpSchemaObjectEntry.DataType = 'Object';
|
|
99
|
+
if (tmpBaseAddress != '')
|
|
100
|
+
{
|
|
101
|
+
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
102
|
+
tmpBaseAddress += '.';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let tmpObjectProperties = Object.keys(pObject);
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < tmpObjectProperties.length; i++)
|
|
108
|
+
{
|
|
109
|
+
this.generateAddressses(pObject[tmpObjectProperties[i]], `${tmpBaseAddress}${tmpObjectProperties[i]}`, tmpSchema);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case 'symbol':
|
|
114
|
+
case 'function':
|
|
115
|
+
// Symbols and functions neither recurse nor get added to the schema
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return tmpSchema;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
module.exports = ManyfestObjectAddressGeneration;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @author <steven@velozo.com>
|
|
4
4
|
*/
|
|
5
5
|
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
6
|
+
let libPrecedent = require('precedent');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Object Address Resolver
|
|
@@ -19,6 +20,8 @@ let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
|
19
20
|
* be kind and rewind... meaning please keep the codebase less
|
|
20
21
|
* terse and more verbose so humans can comprehend it.
|
|
21
22
|
*
|
|
23
|
+
* TODO: Once we validate this pattern is good to go, break these out into
|
|
24
|
+
* three separate modules.
|
|
22
25
|
*
|
|
23
26
|
* @class ManyfestObjectAddressResolver
|
|
24
27
|
*/
|
|
@@ -29,6 +32,9 @@ class ManyfestObjectAddressResolver
|
|
|
29
32
|
// Wire in logging
|
|
30
33
|
this.logInfo = (typeof(pInfoLog) == 'function') ? pInfoLog : libSimpleLog;
|
|
31
34
|
this.logError = (typeof(pErrorLog) == 'function') ? pErrorLog : libSimpleLog;
|
|
35
|
+
|
|
36
|
+
this.elucidatorSolver = false;
|
|
37
|
+
this.elucidatorSolverState = {};
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
// When a boxed property is passed in, it should have quotes of some
|
|
@@ -236,13 +242,57 @@ class ManyfestObjectAddressResolver
|
|
|
236
242
|
}
|
|
237
243
|
}
|
|
238
244
|
|
|
245
|
+
checkFilters(pAddress, pRecord)
|
|
246
|
+
{
|
|
247
|
+
let tmpPrecedent = new libPrecedent();
|
|
248
|
+
// If we don't copy the string, precedent takes it out for good.
|
|
249
|
+
// TODO: Consider adding a "don't replace" option for precedent
|
|
250
|
+
let tmpAddress = pAddress;
|
|
251
|
+
|
|
252
|
+
if (this.elucidatorSolver)
|
|
253
|
+
{
|
|
254
|
+
// This allows the magic filtration with elucidator configuration
|
|
255
|
+
// TODO: We could pass more state in (e.g. parent address, object, etc.)
|
|
256
|
+
// TODO: Discuss this metaprogramming AT LENGTH
|
|
257
|
+
let tmpFilterState = (
|
|
258
|
+
{
|
|
259
|
+
Record: pRecord,
|
|
260
|
+
keepRecord: true
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// This is about as complex as it gets.
|
|
264
|
+
// TODO: Optimize this so it is only initialized once.
|
|
265
|
+
// TODO: That means figuring out a healthy pattern for passing in state to this
|
|
266
|
+
tmpPrecedent.addPattern('{<~~', '~~>}',
|
|
267
|
+
(pInstructionHash) =>
|
|
268
|
+
{
|
|
269
|
+
// This is for internal config on the solution steps. Right now config is not shared across steps.
|
|
270
|
+
if (this.elucidatorSolverState.hasOwnProperty(pInstructionHash))
|
|
271
|
+
{
|
|
272
|
+
tmpFilterState.SolutionState = this.elucidatorSolverState[pInstructionHash];
|
|
273
|
+
}
|
|
274
|
+
this.elucidatorSolver.solveInternalOperation('Custom', pInstructionHash, tmpFilterState);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
tmpPrecedent.parseString(tmpAddress);
|
|
278
|
+
|
|
279
|
+
// It is expected that the operation will mutate this to some truthy value
|
|
280
|
+
return tmpFilterState.keepRecord;
|
|
281
|
+
}
|
|
282
|
+
else
|
|
283
|
+
{
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
239
288
|
// Get the value of an element at an address
|
|
240
289
|
getValueAtAddress (pObject, pAddress, pParentAddress)
|
|
241
290
|
{
|
|
242
|
-
// Make sure pObject is an object
|
|
291
|
+
// Make sure pObject (the object we are meant to be recursing) is an object (which could be an array or object)
|
|
243
292
|
if (typeof(pObject) != 'object') return undefined;
|
|
244
|
-
// Make sure pAddress is a string
|
|
293
|
+
// Make sure pAddress (the address we are resolving) is a string
|
|
245
294
|
if (typeof(pAddress) != 'string') return undefined;
|
|
295
|
+
// Stash the parent address for later resolution
|
|
246
296
|
let tmpParentAddress = "";
|
|
247
297
|
if (typeof(pParentAddress) == 'string')
|
|
248
298
|
{
|
|
@@ -337,7 +387,19 @@ class ManyfestObjectAddressResolver
|
|
|
337
387
|
return false;
|
|
338
388
|
}
|
|
339
389
|
|
|
340
|
-
|
|
390
|
+
let tmpInputArray = pObject[tmpBoxedPropertyName];
|
|
391
|
+
let tmpOutputArray = [];
|
|
392
|
+
for (let i = 0; i < tmpInputArray.length; i++)
|
|
393
|
+
{
|
|
394
|
+
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
395
|
+
let tmpKeepRecord = this.checkFilters(pAddress, tmpInputArray[i]);
|
|
396
|
+
if (tmpKeepRecord)
|
|
397
|
+
{
|
|
398
|
+
tmpOutputArray.push(tmpInputArray[i]);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return tmpOutputArray;
|
|
341
403
|
}
|
|
342
404
|
// The object has been flagged as an object set, so treat it as such
|
|
343
405
|
else if (tmpObjectTypeMarkerIndex > 0)
|
|
@@ -456,7 +518,8 @@ class ManyfestObjectAddressResolver
|
|
|
456
518
|
for (let i = 0; i < tmpArrayProperty.length; i++)
|
|
457
519
|
{
|
|
458
520
|
let tmpPropertyParentAddress = `${tmpParentAddress}[${i}]`;
|
|
459
|
-
let tmpValue = this.getValueAtAddress(pObject[tmpBoxedPropertyName][i], tmpNewAddress, tmpPropertyParentAddress)
|
|
521
|
+
let tmpValue = this.getValueAtAddress(pObject[tmpBoxedPropertyName][i], tmpNewAddress, tmpPropertyParentAddress);
|
|
522
|
+
|
|
460
523
|
tmpContainerObject[`${tmpPropertyParentAddress}.${tmpNewAddress}`] = tmpValue;
|
|
461
524
|
}
|
|
462
525
|
|
|
@@ -486,8 +549,14 @@ class ManyfestObjectAddressResolver
|
|
|
486
549
|
for (let i = 0; i < tmpObjectPropertyKeys.length; i++)
|
|
487
550
|
{
|
|
488
551
|
let tmpPropertyParentAddress = `${tmpParentAddress}.${tmpObjectPropertyKeys[i]}`;
|
|
489
|
-
let tmpValue = this.getValueAtAddress(pObject[tmpObjectPropertyName][tmpObjectPropertyKeys[i]], tmpNewAddress, tmpPropertyParentAddress)
|
|
490
|
-
|
|
552
|
+
let tmpValue = this.getValueAtAddress(pObject[tmpObjectPropertyName][tmpObjectPropertyKeys[i]], tmpNewAddress, tmpPropertyParentAddress);
|
|
553
|
+
|
|
554
|
+
// The filtering is complex but allows config-based metaprogramming directly from schema
|
|
555
|
+
let tmpKeepRecord = this.checkFilters(pAddress, tmpValue);
|
|
556
|
+
if (tmpKeepRecord)
|
|
557
|
+
{
|
|
558
|
+
tmpContainerObject[`${tmpPropertyParentAddress}.${tmpNewAddress}`] = tmpValue;
|
|
559
|
+
}
|
|
491
560
|
}
|
|
492
561
|
|
|
493
562
|
return tmpContainerObject;
|
package/source/Manyfest.js
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
* @author <steven@velozo.com>
|
|
4
4
|
*/
|
|
5
5
|
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
let libPrecedent = require('precedent');
|
|
8
|
+
|
|
7
9
|
let libHashTranslation = require('./Manyfest-HashTranslation.js');
|
|
10
|
+
let libObjectAddressResolver = require('./Manyfest-ObjectAddressResolver.js');
|
|
11
|
+
let libObjectAddressGeneration = require('./Manyfest-ObjectAddressGeneration.js');
|
|
8
12
|
let libSchemaManipulation = require('./Manyfest-SchemaManipulation.js');
|
|
9
13
|
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Manyfest object address-based descriptions and manipulations.
|
|
12
17
|
*
|
|
@@ -45,6 +50,10 @@ class Manyfest
|
|
|
45
50
|
this.elementAddresses = undefined;
|
|
46
51
|
this.elementHashes = undefined;
|
|
47
52
|
this.elementDescriptors = undefined;
|
|
53
|
+
// This can cause a circular dependency chain, so it only gets initialized if the schema specifically calls for it.
|
|
54
|
+
this.dataSolvers = undefined;
|
|
55
|
+
// So solvers can use their own state
|
|
56
|
+
this.dataSolverState = undefined;
|
|
48
57
|
|
|
49
58
|
this.reset();
|
|
50
59
|
|
|
@@ -54,6 +63,7 @@ class Manyfest
|
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
this.schemaManipulations = new libSchemaManipulation(this.logInfo, this.logError);
|
|
66
|
+
this.objectAddressGeneration = new libObjectAddressGeneration(this.logInfo, this.logError);
|
|
57
67
|
|
|
58
68
|
this.hashTranslations = new libHashTranslation(this.logInfo, this.logError);
|
|
59
69
|
}
|
|
@@ -69,6 +79,11 @@ class Manyfest
|
|
|
69
79
|
this.elementAddresses = [];
|
|
70
80
|
this.elementHashes = {};
|
|
71
81
|
this.elementDescriptors = {};
|
|
82
|
+
this.dataSolvers = undefined;
|
|
83
|
+
this.dataSolverState = {};
|
|
84
|
+
|
|
85
|
+
this.libElucidator = undefined;
|
|
86
|
+
this.objectAddressResolver.elucidatorSolver = false;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
clone()
|
|
@@ -135,6 +150,35 @@ class Manyfest
|
|
|
135
150
|
{
|
|
136
151
|
this.logError(`(${this.scope}) Error loading object description from manifest object. Property "Descriptors" does not exist in the root of the Manifest object.`, pManifest);
|
|
137
152
|
}
|
|
153
|
+
|
|
154
|
+
// This seems like it would create a circular dependency issue but it only goes as deep as the schema defines Solvers
|
|
155
|
+
if ((pManifest.hasOwnProperty('Solvers')) && (typeof(pManifest.Solvers) == 'object'))
|
|
156
|
+
{
|
|
157
|
+
// There are elucidator solvers passed-in, so we will create one to filter data.
|
|
158
|
+
let libElucidator = require('elucidator');
|
|
159
|
+
// WARNING THESE CAN MUTATE THE DATA
|
|
160
|
+
// The pattern for the solver is: {<~~SolverName~~>} anywhere in a property.
|
|
161
|
+
// Yes, this means your Javascript elements can't have my self-styled jellyfish brackets in them.
|
|
162
|
+
// This does, though, mean we can filter at multiple layers safely.
|
|
163
|
+
// Because these can be put at any address
|
|
164
|
+
// The solver themselves:
|
|
165
|
+
// They are passed-in an object, and the current record is in the Record subobject.
|
|
166
|
+
// Basic operations can just write to the root object but...
|
|
167
|
+
// IF YOU PERMUTE THE Record SUBOBJECT YOU CAN AFFECT RECURSION
|
|
168
|
+
// This is mostly meant for if statements to filter.
|
|
169
|
+
// Basically on aggregation, if a filter is set it will set "keep record" to true and let the solver decide differently.
|
|
170
|
+
this.dataSolvers = new libElucidator(pManifest.Solvers, this.logInfo, this.logError);
|
|
171
|
+
this.objectAddressResolver.elucidatorSolver = this.dataSolvers;
|
|
172
|
+
|
|
173
|
+
// Load the solver state in so each instruction can have internal config
|
|
174
|
+
// TODO: Should this just be a part of the lower layer pattern?
|
|
175
|
+
let tmpSolverKeys = Object.keys(pManifest.Solvers)
|
|
176
|
+
for (let i = 0; i < tmpSolverKeys.length; i++)
|
|
177
|
+
{
|
|
178
|
+
this.dataSolverState[tmpSolverKeys] = pManifest.Solvers[tmpSolverKeys[i]];
|
|
179
|
+
}
|
|
180
|
+
this.objectAddressResolver.elucidatorSolverState = this.dataSolverState;
|
|
181
|
+
}
|
|
138
182
|
}
|
|
139
183
|
|
|
140
184
|
// Serialize the Manifest to a string
|
|
@@ -256,13 +300,29 @@ class Manyfest
|
|
|
256
300
|
// Get the value of an element by its hash
|
|
257
301
|
getValueByHash (pObject, pHash)
|
|
258
302
|
{
|
|
259
|
-
|
|
303
|
+
let tmpValue = this.getValueAtAddress(pObject, this.resolveHashAddress(pHash));
|
|
304
|
+
|
|
305
|
+
if (typeof(tmpValue) == 'undefined')
|
|
306
|
+
{
|
|
307
|
+
// Try to get a default if it exists
|
|
308
|
+
tmpValue = this.getDefaultValue(this.getDescriptorByHash(pHash));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return tmpValue;
|
|
260
312
|
}
|
|
261
313
|
|
|
262
314
|
// Get the value of an element at an address
|
|
263
315
|
getValueAtAddress (pObject, pAddress)
|
|
264
316
|
{
|
|
265
|
-
|
|
317
|
+
let tmpValue = this.objectAddressResolver.getValueAtAddress(pObject, pAddress);
|
|
318
|
+
|
|
319
|
+
if (typeof(tmpValue) == 'undefined')
|
|
320
|
+
{
|
|
321
|
+
// Try to get a default if it exists
|
|
322
|
+
tmpValue = this.getDefaultValue(this.getDescriptor(pAddress));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return tmpValue;
|
|
266
326
|
}
|
|
267
327
|
|
|
268
328
|
// Set the value of an element by its hash
|
|
@@ -304,9 +364,10 @@ class Manyfest
|
|
|
304
364
|
for (let i = 0; i < this.elementAddresses.length; i++)
|
|
305
365
|
{
|
|
306
366
|
let tmpDescriptor = this.getDescriptor(this.elementAddresses[i]);
|
|
367
|
+
let tmpValueExists = this.checkAddressExists(pObject, tmpDescriptor.Address);
|
|
307
368
|
let tmpValue = this.getValueAtAddress(pObject, tmpDescriptor.Address);
|
|
308
369
|
|
|
309
|
-
if (typeof(tmpValue) == 'undefined')
|
|
370
|
+
if ((typeof(tmpValue) == 'undefined') || !tmpValueExists)
|
|
310
371
|
{
|
|
311
372
|
// This will technically mean that `Object.Some.Value = undefined` will end up showing as "missing"
|
|
312
373
|
// TODO: Do we want to do a different message based on if the property exists but is undefined?
|
|
@@ -385,6 +446,11 @@ class Manyfest
|
|
|
385
446
|
// Returns a default value, or, the default value for the data type (which is overridable with configuration)
|
|
386
447
|
getDefaultValue(pDescriptor)
|
|
387
448
|
{
|
|
449
|
+
if (typeof(pDescriptor) != 'object')
|
|
450
|
+
{
|
|
451
|
+
return undefined;
|
|
452
|
+
}
|
|
453
|
+
|
|
388
454
|
if (pDescriptor.hasOwnProperty('Default'))
|
|
389
455
|
{
|
|
390
456
|
return pDescriptor.Default;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Manyfest
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var Chai = require("chai");
|
|
10
|
+
var Expect = Chai.expect;
|
|
11
|
+
|
|
12
|
+
let libManyfest = require('../source/Manyfest.js');
|
|
13
|
+
|
|
14
|
+
let _SampleDataArchiveOrgFrankenberry = require('./Data-Archive-org-Frankenberry.json');
|
|
15
|
+
|
|
16
|
+
suite
|
|
17
|
+
(
|
|
18
|
+
'Embedded Solvers',
|
|
19
|
+
function()
|
|
20
|
+
{
|
|
21
|
+
setup (()=> {} );
|
|
22
|
+
|
|
23
|
+
suite
|
|
24
|
+
(
|
|
25
|
+
'Set Filtration',
|
|
26
|
+
()=>
|
|
27
|
+
{
|
|
28
|
+
test
|
|
29
|
+
(
|
|
30
|
+
'We should be able to access sets of properties from objects with schema.',
|
|
31
|
+
(fTestComplete)=>
|
|
32
|
+
{
|
|
33
|
+
let _Manyfest = new libManyfest(
|
|
34
|
+
{
|
|
35
|
+
Scope:'Archive.org',
|
|
36
|
+
Descriptors:
|
|
37
|
+
{
|
|
38
|
+
'files[]': {Name:'Files', Hash:'FileSet'},
|
|
39
|
+
'files[].size': {Name:'FileSizes', Hash:'FileSizes'},
|
|
40
|
+
'files[]{<~~ThumbnailFilesOnly~~>}': {Name:'Thumbnail Files', Hash:'ThumbnailFiles'},
|
|
41
|
+
'metadata.creator': {Name:'Creator', Hash:'Creator'},
|
|
42
|
+
'metadata{}': {Name:'Metadata', Hash:'Metadata'}
|
|
43
|
+
},
|
|
44
|
+
Solvers:
|
|
45
|
+
{
|
|
46
|
+
'ThumbnailFilesOnly':
|
|
47
|
+
{
|
|
48
|
+
"Description":
|
|
49
|
+
{
|
|
50
|
+
"Operation": "ThumbnailFilesOnly",
|
|
51
|
+
"Synopsis": "Filter files down to just thumbnails"
|
|
52
|
+
},
|
|
53
|
+
"Config": { "SearchTerm": "Thumbnail" },
|
|
54
|
+
"Steps":
|
|
55
|
+
[
|
|
56
|
+
{
|
|
57
|
+
"Namespace": "Logic",
|
|
58
|
+
"Instruction": "if",
|
|
59
|
+
|
|
60
|
+
"InputHashAddressMap":
|
|
61
|
+
{
|
|
62
|
+
"leftValue": "Record.format",
|
|
63
|
+
"rightValue": "SolutionState.Config.SearchTerm",
|
|
64
|
+
"comparator": "=="
|
|
65
|
+
},
|
|
66
|
+
"OutputHashAddressMap": { "truthinessResult":"keepRecord" }
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
let tmpThumbnailFiles = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'ThumbnailFiles');
|
|
73
|
+
Expect(tmpThumbnailFiles).to.be.an('array');
|
|
74
|
+
// There are 7 thumbnail files in the set.
|
|
75
|
+
Expect(tmpThumbnailFiles.length).to.equal(7);
|
|
76
|
+
let tmpFiles = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'FileSet');
|
|
77
|
+
Expect(tmpFiles).to.be.an('array');
|
|
78
|
+
// There are 7 thumbnail files in the set.
|
|
79
|
+
Expect(tmpFiles.length).to.equal(17);
|
|
80
|
+
fTestComplete();
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
@@ -57,7 +57,7 @@ suite
|
|
|
57
57
|
);
|
|
58
58
|
test
|
|
59
59
|
(
|
|
60
|
-
'
|
|
60
|
+
'Return default values when none are supplied.',
|
|
61
61
|
(fTestComplete)=>
|
|
62
62
|
{
|
|
63
63
|
let animalManyfest = new libManyfest(
|
|
@@ -79,13 +79,19 @@ suite
|
|
|
79
79
|
"Name":"Comfortable Environmental Temperature",
|
|
80
80
|
"NameShort":"Comf Env Temp",
|
|
81
81
|
"Hash":"ComfET",
|
|
82
|
-
"Description":"The most comfortable temperature for this animal to survive in."
|
|
82
|
+
"Description":"The most comfortable temperature for this animal to survive in.",
|
|
83
|
+
"Default": "96.8"
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
});
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
.to.equal(
|
|
88
|
+
Expect(animalManyfest.getValueByHash({MedicalStats: { Temps: { CET:200 }},Name:'Froggy'}, 'ComfET')).to.equal(200);
|
|
89
|
+
Expect(animalManyfest.getValueByHash({MedicalStats: { Temps: { MinET:200 }},Name:'Froggy'}, 'ComfET')).to.equal('96.8');
|
|
90
|
+
Expect(animalManyfest.getValueByHash({MedicalStats: { Temps: { MinET:200 }},Name:'Froggy'}, 'CurrentTemperature')).to.equal(undefined);
|
|
91
|
+
|
|
92
|
+
Expect(animalManyfest.getValueAtAddress({MedicalStats: { Temps: { CET:200 }},Name:'Froggy'}, 'MedicalStats.Temps.CET')).to.equal(200);
|
|
93
|
+
Expect(animalManyfest.getValueAtAddress({MedicalStats: { Temps: { MinET:200 }},Name:'Froggy'}, 'MedicalStats.Temps.CET')).to.equal('96.8');
|
|
94
|
+
Expect(animalManyfest.getValueAtAddress({MedicalStats: { Temps: { MinET:200 }},Name:'Froggy'}, 'MedicalStats.Temps.HighET')).to.equal(undefined);
|
|
89
95
|
|
|
90
96
|
fTestComplete();
|
|
91
97
|
}
|
|
@@ -194,6 +194,52 @@ suite
|
|
|
194
194
|
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
|
|
195
195
|
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
|
|
196
196
|
|
|
197
|
+
fTestComplete();
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
test
|
|
201
|
+
(
|
|
202
|
+
'Schema definition prototypes should be able to be generated from any JSON object shape.',
|
|
203
|
+
(fTestComplete)=>
|
|
204
|
+
{
|
|
205
|
+
let tmpSchemaDescriptors = (
|
|
206
|
+
{
|
|
207
|
+
"a": { "Hash": "a", "Type": "Number" },
|
|
208
|
+
"b": { "Hash": "b", "Type": "Number" },
|
|
209
|
+
"TranslationTable":
|
|
210
|
+
{
|
|
211
|
+
"a": "CarrotCost",
|
|
212
|
+
"b": "AppleCost"
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
let _Manyfest = new libManyfest();
|
|
217
|
+
// Now remap the schema (in-place)
|
|
218
|
+
let tmpSchemaPrototype = _Manyfest.objectAddressGeneration.generateAddressses(tmpSchemaDescriptors);
|
|
219
|
+
|
|
220
|
+
// The schema should be fundamentally altered to point these addresses to the old hashes
|
|
221
|
+
Expect(tmpSchemaPrototype).to.be.an('object');
|
|
222
|
+
|
|
223
|
+
Expect(tmpSchemaPrototype['TranslationTable.a'].DataType).to.equal('String');
|
|
224
|
+
|
|
225
|
+
fTestComplete();
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
test
|
|
229
|
+
(
|
|
230
|
+
'Make a much bigger schema prototype.',
|
|
231
|
+
(fTestComplete)=>
|
|
232
|
+
{
|
|
233
|
+
let _Manyfest = new libManyfest();
|
|
234
|
+
// Now remap the schema (in-place)
|
|
235
|
+
let tmpSchemaPrototype = _Manyfest.objectAddressGeneration.generateAddressses(_SampleDataArchiveOrgFrankenberry);
|
|
236
|
+
|
|
237
|
+
// The schema should be fundamentally altered to point these addresses to the old hashes
|
|
238
|
+
Expect(tmpSchemaPrototype).to.be.an('object');
|
|
239
|
+
|
|
240
|
+
Expect(tmpSchemaPrototype['files_count'].Default).to.equal(17);
|
|
241
|
+
Expect(tmpSchemaPrototype['files_count'].DataType).to.equal('Number');
|
|
242
|
+
|
|
197
243
|
fTestComplete();
|
|
198
244
|
}
|
|
199
245
|
);
|