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.
@@ -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.5",
3
+ "version": "1.0.7",
4
4
  "description": "JSON Object Manifest for Data Description and Parsing",
5
5
  "main": "source/Manyfest.js",
6
6
  "scripts": {
@@ -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
 
@@ -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;
@@ -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
- let tmpManifest = (
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
  );