manyfest 1.0.6 → 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.
@@ -1,4 +1,4 @@
1
1
  bind-addr: 127.0.0.1:8080
2
2
  auth: password
3
- password: luxury
3
+ password: retold
4
4
  cert: false
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "optOut": false,
3
- "lastUpdateCheck": 1665932798321
3
+ "lastUpdateCheck": 1666021392557
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "manyfest",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "JSON Object Manifest for Data Description and Parsing",
5
5
  "main": "source/Manyfest.js",
6
6
  "scripts": {
@@ -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;
@@ -93,7 +93,7 @@ class ManyfestObjectAddressResolver
93
93
  // 2) The end bracket has something between them
94
94
  && (tmpBracketStopIndex > tmpBracketStartIndex)
95
95
  // 3) There is data
96
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
96
+ && (tmpBracketStopIndex - tmpBracketStartIndex > 1))
97
97
  {
98
98
  // The "Name" of the Object contained too the left of the bracket
99
99
  let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
@@ -167,7 +167,7 @@ class ManyfestObjectAddressResolver
167
167
  // 2) The end bracket has something between them
168
168
  && (tmpBracketStopIndex > tmpBracketStartIndex)
169
169
  // 3) There is data
170
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
170
+ && (tmpBracketStopIndex - tmpBracketStartIndex > 1))
171
171
  {
172
172
  let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
173
173
 
@@ -237,12 +237,17 @@ class ManyfestObjectAddressResolver
237
237
  }
238
238
 
239
239
  // Get the value of an element at an address
240
- getValueAtAddress (pObject, pAddress)
240
+ getValueAtAddress (pObject, pAddress, pParentAddress)
241
241
  {
242
242
  // Make sure pObject is an object
243
243
  if (typeof(pObject) != 'object') return undefined;
244
244
  // Make sure pAddress is a string
245
245
  if (typeof(pAddress) != 'string') return undefined;
246
+ let tmpParentAddress = "";
247
+ if (typeof(pParentAddress) == 'string')
248
+ {
249
+ tmpParentAddress = pParentAddress;
250
+ }
246
251
 
247
252
  // TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
248
253
  let tmpSeparatorIndex = pAddress.indexOf('.');
@@ -253,6 +258,11 @@ class ManyfestObjectAddressResolver
253
258
  // Check if the address refers to a boxed property
254
259
  let tmpBracketStartIndex = pAddress.indexOf('[');
255
260
  let tmpBracketStopIndex = pAddress.indexOf(']');
261
+
262
+ // Check for the Object Set Type marker.
263
+ // Note this will not work with a bracket in the same address box set
264
+ let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
265
+
256
266
  // Boxed elements look like this:
257
267
  // MyValues[10]
258
268
  // MyValues['Name']
@@ -266,7 +276,7 @@ class ManyfestObjectAddressResolver
266
276
  // 2) The end bracket has something between them
267
277
  && (tmpBracketStopIndex > tmpBracketStartIndex)
268
278
  // 3) There is data
269
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
279
+ && (tmpBracketStopIndex - tmpBracketStartIndex > 1))
270
280
  {
271
281
  // The "Name" of the Object contained too the left of the bracket
272
282
  let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
@@ -311,6 +321,37 @@ class ManyfestObjectAddressResolver
311
321
  return pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber];
312
322
  }
313
323
  }
324
+ // The requirements to detect a boxed set element are:
325
+ // 1) The start bracket is after character 0
326
+ else if ((tmpBracketStartIndex > 0)
327
+ // 2) The end bracket is after the start bracket
328
+ && (tmpBracketStopIndex > tmpBracketStartIndex)
329
+ // 3) There is nothing in the brackets
330
+ && (tmpBracketStopIndex - tmpBracketStartIndex == 1))
331
+ {
332
+ let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
333
+
334
+ if (!Array.isArray(pObject[tmpBoxedPropertyName]))
335
+ {
336
+ // We asked for a set from an array but it isnt' an array.
337
+ return false;
338
+ }
339
+
340
+ return pObject[tmpBoxedPropertyName];
341
+ }
342
+ // The object has been flagged as an object set, so treat it as such
343
+ else if (tmpObjectTypeMarkerIndex > 0)
344
+ {
345
+ let tmpObjectPropertyName = pAddress.substring(0, tmpObjectTypeMarkerIndex).trim();
346
+
347
+ if (typeof(pObject[tmpObjectPropertyName]) != 'object')
348
+ {
349
+ // We asked for a set from an array but it isnt' an array.
350
+ return false;
351
+ }
352
+
353
+ return pObject[tmpObjectPropertyName];
354
+ }
314
355
  else
315
356
  {
316
357
  // Now is the point in recursion to return the value in the address
@@ -322,6 +363,7 @@ class ManyfestObjectAddressResolver
322
363
  let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
323
364
  let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
324
365
 
366
+ // BOXED ELEMENTS
325
367
  // Test if the tmpNewAddress is an array or object
326
368
  // Check if it's a boxed property
327
369
  let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
@@ -339,7 +381,7 @@ class ManyfestObjectAddressResolver
339
381
  // 2) The end bracket has something between them
340
382
  && (tmpBracketStopIndex > tmpBracketStartIndex)
341
383
  // 3) There is data
342
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
384
+ && (tmpBracketStopIndex - tmpBracketStartIndex > 1))
343
385
  {
344
386
  let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
345
387
 
@@ -376,15 +418,80 @@ class ManyfestObjectAddressResolver
376
418
  tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
377
419
  tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
378
420
 
421
+ // Continue to manage the parent address for recursion
422
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
379
423
  // Recurse directly into the subobject
380
- return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress);
424
+ return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, tmpParentAddress);
381
425
  }
382
426
  else
383
427
  {
428
+ // Continue to manage the parent address for recursion
429
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
384
430
  // We parsed a valid number out of the boxed property name, so recurse into the array
385
- return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress);
431
+ return this.getValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, tmpParentAddress);
386
432
  }
387
433
  }
434
+ // The requirements to detect a boxed set element are:
435
+ // 1) The start bracket is after character 0
436
+ else if ((tmpBracketStartIndex > 0)
437
+ // 2) The end bracket is after the start bracket
438
+ && (tmpBracketStopIndex > tmpBracketStartIndex)
439
+ // 3) There is nothing in the brackets
440
+ && (tmpBracketStopIndex - tmpBracketStartIndex == 1))
441
+ {
442
+ let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
443
+
444
+ if (!Array.isArray(pObject[tmpBoxedPropertyName]))
445
+ {
446
+ // We asked for a set from an array but it isnt' an array.
447
+ return false;
448
+ }
449
+
450
+ // We need to enumerate the array and grab the addresses from there.
451
+ let tmpArrayProperty = pObject[tmpBoxedPropertyName];
452
+ // Managing the parent address is a bit more complex here -- the box will be added for each element.
453
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpBoxedPropertyName}`;
454
+ // The container object is where we have the "Address":SOMEVALUE pairs
455
+ let tmpContainerObject = {};
456
+ for (let i = 0; i < tmpArrayProperty.length; i++)
457
+ {
458
+ let tmpPropertyParentAddress = `${tmpParentAddress}[${i}]`;
459
+ let tmpValue = this.getValueAtAddress(pObject[tmpBoxedPropertyName][i], tmpNewAddress, tmpPropertyParentAddress);;
460
+ tmpContainerObject[`${tmpPropertyParentAddress}.${tmpNewAddress}`] = tmpValue;
461
+ }
462
+
463
+ return tmpContainerObject;
464
+ }
465
+
466
+ // OBJECT SET
467
+ // Note this will not work with a bracket in the same address box set
468
+ let tmpObjectTypeMarkerIndex = pAddress.indexOf('{}');
469
+ if (tmpObjectTypeMarkerIndex > 0)
470
+ {
471
+ let tmpObjectPropertyName = pAddress.substring(0, tmpObjectTypeMarkerIndex).trim();
472
+
473
+ if (typeof(pObject[tmpObjectPropertyName]) != 'object')
474
+ {
475
+ // We asked for a set from an array but it isnt' an array.
476
+ return false;
477
+ }
478
+
479
+ // We need to enumerate the Object and grab the addresses from there.
480
+ let tmpObjectProperty = pObject[tmpObjectPropertyName];
481
+ let tmpObjectPropertyKeys = Object.keys(tmpObjectProperty);
482
+ // Managing the parent address is a bit more complex here -- the box will be added for each element.
483
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpObjectPropertyName}`;
484
+ // The container object is where we have the "Address":SOMEVALUE pairs
485
+ let tmpContainerObject = {};
486
+ for (let i = 0; i < tmpObjectPropertyKeys.length; i++)
487
+ {
488
+ let tmpPropertyParentAddress = `${tmpParentAddress}.${tmpObjectPropertyKeys[i]}`;
489
+ let tmpValue = this.getValueAtAddress(pObject[tmpObjectPropertyName][tmpObjectPropertyKeys[i]], tmpNewAddress, tmpPropertyParentAddress);;
490
+ tmpContainerObject[`${tmpPropertyParentAddress}.${tmpNewAddress}`] = tmpValue;
491
+ }
492
+
493
+ return tmpContainerObject;
494
+ }
388
495
 
389
496
  // If there is an object property already named for the sub object, but it isn't an object
390
497
  // then the system can't set the value in there. Error and abort!
@@ -395,13 +502,17 @@ class ManyfestObjectAddressResolver
395
502
  else if (pObject.hasOwnProperty(tmpSubObjectName))
396
503
  {
397
504
  // If there is already a subobject pass that to the recursive thingy
398
- return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress);
505
+ // Continue to manage the parent address for recursion
506
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
507
+ return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, tmpParentAddress);
399
508
  }
400
509
  else
401
510
  {
402
511
  // Create a subobject and then pass that
512
+ // Continue to manage the parent address for recursion
513
+ tmpParentAddress = `${tmpParentAddress}${(tmpParentAddress.length > 0) ? '.' : ''}${tmpSubObjectName}`;
403
514
  pObject[tmpSubObjectName] = {};
404
- return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress);
515
+ return this.getValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, tmpParentAddress);
405
516
  }
406
517
  }
407
518
  }
@@ -434,7 +545,7 @@ class ManyfestObjectAddressResolver
434
545
  // 2) The end bracket has something between them
435
546
  && (tmpBracketStopIndex > tmpBracketStartIndex)
436
547
  // 3) There is data
437
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
548
+ && (tmpBracketStopIndex - tmpBracketStartIndex > 1))
438
549
  {
439
550
  // The "Name" of the Object contained too the left of the bracket
440
551
  let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
@@ -510,7 +621,7 @@ class ManyfestObjectAddressResolver
510
621
  // 2) The end bracket has something between them
511
622
  && (tmpBracketStopIndex > tmpBracketStartIndex)
512
623
  // 3) There is data
513
- && (tmpBracketStopIndex - tmpBracketStartIndex > 0))
624
+ && (tmpBracketStopIndex - tmpBracketStartIndex > 1))
514
625
  {
515
626
  let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
516
627
 
@@ -3,10 +3,13 @@
3
3
  * @author <steven@velozo.com>
4
4
  */
5
5
  let libSimpleLog = require('./Manyfest-LogToConsole.js');
6
- let libObjectAddressResolver = require('./Manyfest-ObjectAddressResolver.js');
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
- return this.getValueAtAddress(pObject, this.resolveHashAddress(pHash));
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
- return this.objectAddressResolver.getValueAtAddress(pObject, pAddress);
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;
@@ -0,0 +1,229 @@
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
+ 'Manyfest Object Read of Sets',
19
+ function()
20
+ {
21
+ setup (()=> {} );
22
+
23
+ suite
24
+ (
25
+ 'Basic Array Set Reads',
26
+ ()=>
27
+ {
28
+ test
29
+ (
30
+ 'We should be able to access sets of properties from arrays without 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
+ 'metadata.creator': {Name:'Creator', Hash:'Creator'}
41
+ }
42
+ });
43
+ let tmpFileSet = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'FileSet');
44
+ Expect(Array.isArray(tmpFileSet)).to.equal(true);
45
+ fTestComplete();
46
+ }
47
+ );
48
+ test
49
+ (
50
+ 'We should be able to access arrays with or without boxes on the address.',
51
+ (fTestComplete)=>
52
+ {
53
+ let _Manyfest = new libManyfest(
54
+ {
55
+ Scope:'Archive.org',
56
+ Descriptors:
57
+ {
58
+ 'files[]': {Name:'Files', Hash:'FileSet'},
59
+ 'files[].size': {Name:'FileSizes', Hash:'FileSizes'},
60
+ 'metadata.creator': {Name:'Creator', Hash:'Creator'}
61
+ }
62
+ });
63
+ let tmpFileSet = _Manyfest.getValueAtAddress(_SampleDataArchiveOrgFrankenberry, 'files[]');
64
+ Expect(Array.isArray(tmpFileSet)).to.equal(true);
65
+ // There are 17 versions of this damn commercial....
66
+ Expect(tmpFileSet.length).to.equal(17);
67
+ let tmpFileSetUnboxed = _Manyfest.getValueAtAddress(_SampleDataArchiveOrgFrankenberry, 'files');
68
+ Expect(Array.isArray(tmpFileSetUnboxed)).to.equal(true);
69
+ fTestComplete();
70
+ }
71
+ );
72
+ test
73
+ (
74
+ 'We should be able to access arrays with a hash.',
75
+ (fTestComplete)=>
76
+ {
77
+ let _Manyfest = new libManyfest(
78
+ {
79
+ Scope:'Archive.org',
80
+ Descriptors:
81
+ {
82
+ 'files[]': {Name:'Files', Hash:'FileSet'},
83
+ 'files[].size': {Name:'FileSizes', Hash:'FileSizes'},
84
+ 'metadata.creator': {Name:'Creator', Hash:'Creator'}
85
+ }
86
+ });
87
+ let tmpFileSet = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'FileSet');
88
+ Expect(Array.isArray(tmpFileSet)).to.equal(true);
89
+ // There are 17 versions of this damn commercial....
90
+ Expect(tmpFileSet.length).to.equal(17);
91
+ fTestComplete();
92
+ }
93
+ );
94
+ test
95
+ (
96
+ 'Attempting to access non-array properties as arrays should return false.',
97
+ (fTestComplete)=>
98
+ {
99
+ let _Manyfest = new libManyfest(
100
+ {
101
+ Scope:'Archive.org',
102
+ Descriptors:
103
+ {
104
+ 'files[]': {Name:'Files', Hash:'FileSet'},
105
+ 'files[].size': {Name:'FileSizes', Hash:'FileSizes'},
106
+ 'metadata.creator': {Name:'Creator', Hash:'Creator'}
107
+ }
108
+ });
109
+ let tmpFileSet = _Manyfest.getValueAtAddress(_SampleDataArchiveOrgFrankenberry, 'metadata.creator[]');
110
+ Expect(tmpFileSet).to.equal(false);
111
+ fTestComplete();
112
+ }
113
+ );
114
+ test
115
+ (
116
+ 'Sub-objects in arrays can be parsed and the values will be pulled in magically.',
117
+ (fTestComplete)=>
118
+ {
119
+ let _Manyfest = new libManyfest(
120
+ {
121
+ Scope:'Archive.org',
122
+ Descriptors:
123
+ {
124
+ 'files[]': {Name:'Files', Hash:'FileSet'},
125
+ 'files[].size': {Name:'FileSizes', Hash:'FileSizes'},
126
+ 'metadata.creator': {Name:'Creator', Hash:'Creator'}
127
+ }
128
+ });
129
+ let tmpFileSet = _Manyfest.getValueAtAddress(_SampleDataArchiveOrgFrankenberry, 'files[].size');
130
+ Expect(tmpFileSet).to.be.an('object');
131
+ Expect(tmpFileSet['files[13].size']).to.equal('31625216');
132
+ fTestComplete();
133
+ }
134
+ );
135
+ }
136
+ );
137
+ suite
138
+ (
139
+ 'Basic Object Set Reads',
140
+ ()=>
141
+ {
142
+ test
143
+ (
144
+ 'We should be able to access sets of properties from objects with schema.',
145
+ (fTestComplete)=>
146
+ {
147
+ let _Manyfest = new libManyfest(
148
+ {
149
+ Scope:'Archive.org',
150
+ Descriptors:
151
+ {
152
+ 'files[]': {Name:'Files', Hash:'FileSet'},
153
+ 'files[].size': {Name:'FileSizes', Hash:'FileSizes'},
154
+ 'metadata.creator': {Name:'Creator', Hash:'Creator'},
155
+ 'metadata{}': {Name:'Metadata', Hash:'Metadata'}
156
+ }
157
+ });
158
+ let tmpMetadata = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Metadata');
159
+ Expect(tmpMetadata).to.be.an('object');
160
+ fTestComplete();
161
+ }
162
+ );
163
+ test
164
+ (
165
+ 'We should be able to get declared objects without schema.',
166
+ (fTestComplete)=>
167
+ {
168
+ let _Manyfest = new libManyfest(
169
+ {
170
+ Scope:'RandomObject',
171
+ Descriptors:{}
172
+ });
173
+ let tmpData = (
174
+ {
175
+ Name: "Menu",
176
+ Ingredients: {
177
+ "GUID-001": { cost:100, Name:'Carrot' },
178
+ "GUID-002": { cost:102, Name:'Pea' },
179
+ "GUID-003": { cost:13, Name:'Potato' },
180
+ "GUID-004": { cost:1, Name:'Apple' },
181
+ "GUID-005": { cost:190, Name:'Wine' },
182
+ "GUID-006": { cost:2500, Name:'Steak' },
183
+ "GUID-007": { cost:"44", Name:'Gristle' },
184
+ "GUID-008": { cost:2, Name:'Cherry' },
185
+ "GUID-009": { cost:14, Name:'Orange' },
186
+ "GUID-010": { cost:1, Name:'Dandelion' },
187
+ }
188
+ });
189
+ let tmpMetadata = _Manyfest.getValueAtAddress(tmpData, 'Ingredients{}');
190
+ Expect(tmpMetadata).to.be.an('object');
191
+ fTestComplete();
192
+ }
193
+ );
194
+ test
195
+ (
196
+ 'We should be able to declared object subobjects without schema.',
197
+ (fTestComplete)=>
198
+ {
199
+ let _Manyfest = new libManyfest(
200
+ {
201
+ Scope:'RandomObject',
202
+ Descriptors:{}
203
+ });
204
+ let tmpData = (
205
+ {
206
+ Name: "Menu",
207
+ Ingredients: {
208
+ "GUID-001": { cost:100, Name:'Carrot' },
209
+ "GUID-002": { cost:102, Name:'Pea' },
210
+ "GUID-003": { cost:13, Name:'Potato' },
211
+ "GUID-004": { cost:1, Name:'Apple' },
212
+ "GUID-005": { cost:190, Name:'Wine' },
213
+ "GUID-006": { cost:2500, Name:'Steak' },
214
+ "GUID-007": { cost:"44", Name:'Gristle' },
215
+ "GUID-008": { cost:2, Name:'Cherry' },
216
+ "GUID-009": { cost:14, Name:'Orange' },
217
+ "GUID-010": { cost:1, Name:'Dandelion' },
218
+ }
219
+ });
220
+ let tmpMetadata = _Manyfest.getValueAtAddress(tmpData, 'Ingredients{}.cost');
221
+ Expect(tmpMetadata).to.be.an('object');
222
+ Expect(tmpMetadata['Ingredients.GUID-003.cost']).to.equal(13);
223
+ fTestComplete();
224
+ }
225
+ );
226
+ }
227
+ );
228
+ }
229
+ );
@@ -57,7 +57,7 @@ suite
57
57
  );
58
58
  test
59
59
  (
60
- 'Exercise more hash accesss scenarios..',
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
- Expect(animalManyfest.getValueByHash({MedicalStats: { Temps: { CET:200 }},Name:'Froggy'}, 'ComfET'))
88
- .to.equal(200);
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
  );