manyfest 1.0.7 → 1.0.8
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
|
@@ -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;
|
package/source/Manyfest.js
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
* @author <steven@velozo.com>
|
|
4
4
|
*/
|
|
5
5
|
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
let libHashTranslation = require('./Manyfest-HashTranslation.js');
|
|
8
|
+
let libObjectAddressResolver = require('./Manyfest-ObjectAddressResolver.js');
|
|
9
|
+
let libObjectAddressGeneration = require('./Manyfest-ObjectAddressGeneration.js');
|
|
8
10
|
let libSchemaManipulation = require('./Manyfest-SchemaManipulation.js');
|
|
9
11
|
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* Manyfest object address-based descriptions and manipulations.
|
|
12
15
|
*
|
|
@@ -54,6 +57,7 @@ class Manyfest
|
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
this.schemaManipulations = new libSchemaManipulation(this.logInfo, this.logError);
|
|
60
|
+
this.objectAddressGeneration = new libObjectAddressGeneration(this.logInfo, this.logError);
|
|
57
61
|
|
|
58
62
|
this.hashTranslations = new libHashTranslation(this.logInfo, this.logError);
|
|
59
63
|
}
|
|
@@ -256,13 +260,29 @@ class Manyfest
|
|
|
256
260
|
// Get the value of an element by its hash
|
|
257
261
|
getValueByHash (pObject, pHash)
|
|
258
262
|
{
|
|
259
|
-
|
|
263
|
+
let tmpValue = this.getValueAtAddress(pObject, this.resolveHashAddress(pHash));
|
|
264
|
+
|
|
265
|
+
if (typeof(tmpValue) == 'undefined')
|
|
266
|
+
{
|
|
267
|
+
// Try to get a default if it exists
|
|
268
|
+
tmpValue = this.getDefaultValue(this.getDescriptorByHash(pHash));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return tmpValue;
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
// Get the value of an element at an address
|
|
263
275
|
getValueAtAddress (pObject, pAddress)
|
|
264
276
|
{
|
|
265
|
-
|
|
277
|
+
let tmpValue = this.objectAddressResolver.getValueAtAddress(pObject, pAddress);
|
|
278
|
+
|
|
279
|
+
if (typeof(tmpValue) == 'undefined')
|
|
280
|
+
{
|
|
281
|
+
// Try to get a default if it exists
|
|
282
|
+
tmpValue = this.getDefaultValue(this.getDescriptor(pAddress));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return tmpValue;
|
|
266
286
|
}
|
|
267
287
|
|
|
268
288
|
// Set the value of an element by its hash
|
|
@@ -304,9 +324,10 @@ class Manyfest
|
|
|
304
324
|
for (let i = 0; i < this.elementAddresses.length; i++)
|
|
305
325
|
{
|
|
306
326
|
let tmpDescriptor = this.getDescriptor(this.elementAddresses[i]);
|
|
327
|
+
let tmpValueExists = this.checkAddressExists(pObject, tmpDescriptor.Address);
|
|
307
328
|
let tmpValue = this.getValueAtAddress(pObject, tmpDescriptor.Address);
|
|
308
329
|
|
|
309
|
-
if (typeof(tmpValue) == 'undefined')
|
|
330
|
+
if ((typeof(tmpValue) == 'undefined') || !tmpValueExists)
|
|
310
331
|
{
|
|
311
332
|
// This will technically mean that `Object.Some.Value = undefined` will end up showing as "missing"
|
|
312
333
|
// TODO: Do we want to do a different message based on if the property exists but is undefined?
|
|
@@ -385,6 +406,11 @@ class Manyfest
|
|
|
385
406
|
// Returns a default value, or, the default value for the data type (which is overridable with configuration)
|
|
386
407
|
getDefaultValue(pDescriptor)
|
|
387
408
|
{
|
|
409
|
+
if (typeof(pDescriptor) != 'object')
|
|
410
|
+
{
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
|
|
388
414
|
if (pDescriptor.hasOwnProperty('Default'))
|
|
389
415
|
{
|
|
390
416
|
return pDescriptor.Default;
|
|
@@ -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
|
);
|