manyfest 1.0.5 → 1.0.7
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/configstore/update-notifier-npm.json +1 -1
- package/package.json +1 -1
- package/source/Manyfest-ObjectAddressResolver.js +122 -11
- package/source/Manyfest-SchemaManipulation.js +26 -0
- package/source/Manyfest.js +15 -1
- package/test/Manyfest_Object_ReadSets_tests.js +229 -0
- package/test/Manyfest_Object_SchemaManipulation_tests.js +110 -0
package/package.json
CHANGED
|
@@ -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 >
|
|
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 >
|
|
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 >
|
|
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 >
|
|
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
|
-
|
|
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 >
|
|
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 >
|
|
624
|
+
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
514
625
|
{
|
|
515
626
|
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
516
627
|
|
|
@@ -107,6 +107,32 @@ class ManyfestSchemaManipulation
|
|
|
107
107
|
this.resolveAddressMappings(tmpManyfestSchemaDescriptors, pAddressMapping);
|
|
108
108
|
return tmpManyfestSchemaDescriptors;
|
|
109
109
|
}
|
|
110
|
+
|
|
111
|
+
mergeAddressMappings(pManyfestSchemaDescriptorsDestination, pManyfestSchemaDescriptorsSource)
|
|
112
|
+
{
|
|
113
|
+
if ((typeof(pManyfestSchemaDescriptorsSource) != 'object') || (typeof(pManyfestSchemaDescriptorsDestination) != 'object'))
|
|
114
|
+
{
|
|
115
|
+
this.logError(`Attempted to merge two schema descriptors but both were not objects.`);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let tmpSource = JSON.parse(JSON.stringify(pManyfestSchemaDescriptorsSource));
|
|
120
|
+
let tmpNewManyfestSchemaDescriptors = JSON.parse(JSON.stringify(pManyfestSchemaDescriptorsDestination));
|
|
121
|
+
|
|
122
|
+
// The first passed-in set of descriptors takes precedence.
|
|
123
|
+
let tmpDescriptorAddresses = Object.keys(tmpSource);
|
|
124
|
+
|
|
125
|
+
tmpDescriptorAddresses.forEach(
|
|
126
|
+
(pDescriptorAddress) =>
|
|
127
|
+
{
|
|
128
|
+
if (!tmpNewManyfestSchemaDescriptors.hasOwnProperty(pDescriptorAddress))
|
|
129
|
+
{
|
|
130
|
+
tmpNewManyfestSchemaDescriptors[pDescriptorAddress] = tmpSource[pDescriptorAddress];
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return tmpNewManyfestSchemaDescriptors;
|
|
135
|
+
}
|
|
110
136
|
}
|
|
111
137
|
|
|
112
138
|
module.exports = ManyfestSchemaManipulation;
|
package/source/Manyfest.js
CHANGED
|
@@ -71,6 +71,19 @@ class Manyfest
|
|
|
71
71
|
this.elementDescriptors = {};
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
clone()
|
|
75
|
+
{
|
|
76
|
+
// Make a copy of the options in-place
|
|
77
|
+
let tmpNewOptions = JSON.parse(JSON.stringify(this.options));
|
|
78
|
+
|
|
79
|
+
let tmpNewManyfest = new Manyfest(this.getManifest(), this.logInfo, this.logError, tmpNewOptions);
|
|
80
|
+
|
|
81
|
+
// Import the hash translations
|
|
82
|
+
tmpNewManyfest.hashTranslations.addTranslation(this.hashTranslations.translationTable);
|
|
83
|
+
|
|
84
|
+
return tmpNewManyfest;
|
|
85
|
+
}
|
|
86
|
+
|
|
74
87
|
// Deserialize a Manifest from a string
|
|
75
88
|
deserialize(pManifestString)
|
|
76
89
|
{
|
|
@@ -125,6 +138,7 @@ class Manyfest
|
|
|
125
138
|
}
|
|
126
139
|
|
|
127
140
|
// Serialize the Manifest to a string
|
|
141
|
+
// TODO: Should this also serialize the translation table?
|
|
128
142
|
serialize()
|
|
129
143
|
{
|
|
130
144
|
return JSON.stringify(this.getManifest());
|
|
@@ -132,7 +146,7 @@ class Manyfest
|
|
|
132
146
|
|
|
133
147
|
getManifest()
|
|
134
148
|
{
|
|
135
|
-
|
|
149
|
+
return (
|
|
136
150
|
{
|
|
137
151
|
Scope: this.scope,
|
|
138
152
|
Descriptors: JSON.parse(JSON.stringify(this.elementDescriptors))
|
|
@@ -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
|
+
);
|
|
@@ -84,6 +84,116 @@ suite
|
|
|
84
84
|
Expect(tmpNewSchemaDescriptors.CarrotCost.Hash).to.equal('a');
|
|
85
85
|
Expect(tmpNewSchemaDescriptors.AppleCost.Hash).to.equal('b');
|
|
86
86
|
|
|
87
|
+
fTestComplete();
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
test
|
|
91
|
+
(
|
|
92
|
+
'We should be able to merge properties safely.',
|
|
93
|
+
(fTestComplete)=>
|
|
94
|
+
{
|
|
95
|
+
let tmpSchemaDescriptors = (
|
|
96
|
+
{
|
|
97
|
+
"a": { "Hash": "a", "Type": "Number" },
|
|
98
|
+
"b": { "Hash": "b", "Type": "Number" }
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
let tmpSchemaDescriptorsToMerge = (
|
|
102
|
+
{
|
|
103
|
+
"c": { "Hash": "c" },
|
|
104
|
+
"d": { "Hash": "d" },
|
|
105
|
+
"e": { "Hash": "e" },
|
|
106
|
+
"a": { "Hash": "ARBUCKLE", "Type": "Number" }
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
Expect(tmpSchemaDescriptors.a.Hash).to.equal('a');
|
|
110
|
+
|
|
111
|
+
let _Manyfest = new libManyfest();
|
|
112
|
+
// Now remap the schema (in-place)
|
|
113
|
+
let tmpNewSchemaDescriptors = _Manyfest.schemaManipulations.mergeAddressMappings(tmpSchemaDescriptors, tmpSchemaDescriptorsToMerge);
|
|
114
|
+
|
|
115
|
+
// The schema should be safe
|
|
116
|
+
Expect(tmpNewSchemaDescriptors.a.Hash).to.equal('a');
|
|
117
|
+
// And a new schema should have been created with the alterations
|
|
118
|
+
Expect(tmpNewSchemaDescriptors.b.Hash).to.equal('b');
|
|
119
|
+
Expect(tmpNewSchemaDescriptors.c.Hash).to.equal('c');
|
|
120
|
+
|
|
121
|
+
fTestComplete();
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
test
|
|
125
|
+
(
|
|
126
|
+
'Cloning should work.',
|
|
127
|
+
(fTestComplete)=>
|
|
128
|
+
{
|
|
129
|
+
let tmpSchemaDescriptors = (
|
|
130
|
+
{
|
|
131
|
+
"a": { "Hash": "a", "Type": "Number" },
|
|
132
|
+
"b": { "Hash": "b", "Type": "Number" }
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
let tmpTranslationTable = (
|
|
136
|
+
{
|
|
137
|
+
"a": "CarrotCost",
|
|
138
|
+
"b": "AppleCost"
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
|
|
142
|
+
// Property not schema, accessed by hash:
|
|
143
|
+
let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
|
|
144
|
+
Expect(tmpCreator).to.equal('General Mills');
|
|
145
|
+
let _ClonedManyfest = _Manyfest.clone();
|
|
146
|
+
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator')).to.equal('General Mills');
|
|
147
|
+
|
|
148
|
+
fTestComplete();
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
test
|
|
152
|
+
(
|
|
153
|
+
'Cloning should take into account translation.',
|
|
154
|
+
(fTestComplete)=>
|
|
155
|
+
{
|
|
156
|
+
let tmpSchemaDescriptors = (
|
|
157
|
+
{
|
|
158
|
+
"a": { "Hash": "a", "Type": "Number" },
|
|
159
|
+
"b": { "Hash": "b", "Type": "Number" }
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
let tmpTranslationTable = (
|
|
163
|
+
{
|
|
164
|
+
"a": "CarrotCost",
|
|
165
|
+
"b": "AppleCost"
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
|
|
169
|
+
// Property not schema, accessed by hash:
|
|
170
|
+
let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
|
|
171
|
+
Expect(tmpCreator).to.equal('General Mills');
|
|
172
|
+
// Create a translation between "Creator" and "Director" as well as "Author"
|
|
173
|
+
_Manyfest.hashTranslations.addTranslation({"Director":"Creator", "Author":"Creator", "Songwriter":"Creator"});
|
|
174
|
+
Expect(tmpCreator).to.equal('General Mills');
|
|
175
|
+
// Director should also work
|
|
176
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
|
|
177
|
+
// And Author!
|
|
178
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal('General Mills');
|
|
179
|
+
// Now remove Director
|
|
180
|
+
_Manyfest.hashTranslations.clearTranslations();
|
|
181
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal(undefined);
|
|
182
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
|
|
183
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Songwriter')).to.equal(undefined);
|
|
184
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator')).to.equal('General Mills');
|
|
185
|
+
|
|
186
|
+
let _ClonedManyfest = _Manyfest.clone();
|
|
187
|
+
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal(undefined);
|
|
188
|
+
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
|
|
189
|
+
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Songwriter')).to.equal(undefined);
|
|
190
|
+
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator')).to.equal('General Mills');
|
|
191
|
+
|
|
192
|
+
// New translations should not affect the old manyfest
|
|
193
|
+
_ClonedManyfest.hashTranslations.addTranslation({"Director":"Creator", "Author":"Creator", "Songwriter":"Creator"});
|
|
194
|
+
Expect(_ClonedManyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
|
|
195
|
+
Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
|
|
196
|
+
|
|
87
197
|
fTestComplete();
|
|
88
198
|
}
|
|
89
199
|
);
|