manyfest 1.0.3 → 1.0.5

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": 1665680610935
3
+ "lastUpdateCheck": 1665932798321
4
4
  }
package/README.md CHANGED
@@ -75,13 +75,15 @@ Scope | The scope of this representation; generally the clustered or parent reco
75
75
  Schema | The stateful representation of an object's structural definition.
76
76
  Element | A defined element of data in an object.
77
77
  Address | The address where that data lies in the object.
78
+ Hash | A unique within this scope string-based key for this element. Used for easy access of data.
78
79
  Descriptor | A description of an element including data such as Name, NameShort, Hash, Description, and other important properties.
79
80
  Name | The name of the element. Meant to be the most succinct human readable name possible.
80
81
  NameShort | A shorter name for the element. Meant to be useful enough to identify the property in log lines, tabular views, graphs and anywhere where we don't always want to see the full name.
81
82
  Description | A description for the element. Very useful when consuming other APIs with their own terse naming standards (or no naming standards)!
82
- Hash | A unique within this scope string-based key for this element. Used for easy access of data.
83
83
  Required | Set to true if this element is required.
84
84
 
85
+ Okay so these are a lot of crazy words. The important two are *Address* and *Hash*. Every element in a schema must have an address. Having a hash just multiplies the usefulness of these addresses.
86
+
85
87
  ## A More Advanced Schema Example
86
88
 
87
89
  Addresses are meant to be kinda magic. They describe locations in nested JSON just as well as simple objects. Further, they can allow us to manipulate and read JSON values at specific addresses.
@@ -205,6 +207,10 @@ console.log(animalManyfest.getValueByHash(favAnimal,'ComfET'));
205
207
 
206
208
  For any elements that haven't defined a Hash, the Address is used. This allows code to gracefully fall back.
207
209
 
210
+ ## Hash Translation Tables
211
+
212
+ Sometimes we want to reuse the structure of a schema, but look up values by hash using translations.
213
+
208
214
  ## Programmatically Defining a Schema
209
215
 
210
216
  Sometimes we don't have schemas, and want to define object element structure on the fly. This can be done programmatically. As a refresher, here is how we loaded our simplest schema manifest above:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "manyfest",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "JSON Object Manifest for Data Description and Parsing",
5
5
  "main": "source/Manyfest.js",
6
6
  "scripts": {
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @license MIT
3
+ * @author <steven@velozo.com>
4
+ */
5
+ let libSimpleLog = require('./Manyfest-LogToConsole.js');
6
+
7
+ /**
8
+ * Hash Translation
9
+ *
10
+ * This is a very simple translation table for hashes, which allows the same schema to resolve
11
+ * differently based on a loaded translation table.
12
+ *
13
+ * This is to prevent the requirement for mutating schemas over and over again when we want to
14
+ * reuse the structure but look up data elements by different addresses.
15
+ *
16
+ * One side-effect of this is that a translation table can "override" the built-in hashes, since
17
+ * this is always used to resolve hashes before any of the functionCallByHash(pHash, ...) perform
18
+ * their lookups by hash.
19
+ *
20
+ * @class ManyfestHashTranslation
21
+ */
22
+ class ManyfestHashTranslation
23
+ {
24
+ constructor(pInfoLog, pErrorLog)
25
+ {
26
+ // Wire in logging
27
+ this.logInfo = (typeof(pInfoLog) === 'function') ? pInfoLog : libSimpleLog;
28
+ this.logError = (typeof(pErrorLog) === 'function') ? pErrorLog : libSimpleLog;
29
+
30
+ this.translationTable = {};
31
+ }
32
+
33
+ translationCount()
34
+ {
35
+ return Object.keys(this.translationTable).length;
36
+ }
37
+
38
+ addTranslation(pTranslation)
39
+ {
40
+ // This adds a translation in the form of:
41
+ // { "SourceHash": "DestinationHash", "SecondSourceHash":"SecondDestinationHash" }
42
+ if (typeof(pTranslation) != 'object')
43
+ {
44
+ this.logError(`Hash translation addTranslation expected a translation be type object but was passed in ${typeof(pTranslation)}`);
45
+ return false;
46
+ }
47
+
48
+ let tmpTranslationSources = Object.keys(pTranslation)
49
+
50
+ tmpTranslationSources.forEach(
51
+ (pTranslationSource) =>
52
+ {
53
+ if (typeof(pTranslation[pTranslationSource]) != 'string')
54
+ {
55
+ this.logError(`Hash translation addTranslation expected a translation destination hash for [${pTranslationSource}] to be a string but the referrant was a ${typeof(pTranslation[pTranslationSource])}`);
56
+ }
57
+ else
58
+ {
59
+ this.translationTable[pTranslationSource] = pTranslation[pTranslationSource];
60
+ }
61
+ });
62
+ }
63
+
64
+ removeTranslationHash(pTranslationHash)
65
+ {
66
+ if (this.translationTable.hasOwnProperty(pTranslationHash))
67
+ {
68
+ delete this.translationTable[pTranslationHash];
69
+ }
70
+ }
71
+
72
+ // This removes translations.
73
+ // If passed a string, just removes the single one.
74
+ // If passed an object, it does all the source keys.
75
+ removeTranslation(pTranslation)
76
+ {
77
+ if (typeof(pTranslation) == 'string')
78
+ {
79
+ this.removeTranslationHash(pTranslation);
80
+ return true;
81
+ }
82
+ else if (typeof(pTranslation) == 'object')
83
+ {
84
+ let tmpTranslationSources = Object.keys(pTranslation)
85
+
86
+ tmpTranslationSources.forEach(
87
+ (pTranslationSource) =>
88
+ {
89
+ this.removeTranslation(pTranslationSource);
90
+ });
91
+ return true;
92
+ }
93
+ else
94
+ {
95
+ this.logError(`Hash translation removeTranslation expected either a string or an object but the passed-in translation was type ${typeof(pTranslation)}`);
96
+ return false;
97
+ }
98
+ }
99
+
100
+ clearTranslations()
101
+ {
102
+ this.translationTable = {};
103
+ }
104
+
105
+ translate(pTranslation)
106
+ {
107
+ if (this.translationTable.hasOwnProperty(pTranslation))
108
+ {
109
+ return this.translationTable[pTranslation];
110
+ }
111
+ else
112
+ {
113
+ return pTranslation;
114
+ }
115
+ }
116
+ }
117
+
118
+ module.exports = ManyfestHashTranslation;
@@ -27,8 +27,8 @@ class ManyfestObjectAddressResolver
27
27
  constructor(pInfoLog, pErrorLog)
28
28
  {
29
29
  // Wire in logging
30
- this.logInfo = (typeof(pInfoLog) === 'function') ? pInfoLog : libSimpleLog;
31
- this.logError = (typeof(pErrorLog) === 'function') ? pErrorLog : libSimpleLog;
30
+ this.logInfo = (typeof(pInfoLog) == 'function') ? pInfoLog : libSimpleLog;
31
+ this.logError = (typeof(pErrorLog) == 'function') ? pErrorLog : libSimpleLog;
32
32
  }
33
33
 
34
34
  // When a boxed property is passed in, it should have quotes of some
@@ -67,15 +67,15 @@ class ManyfestObjectAddressResolver
67
67
  {
68
68
  // TODO: Should these throw an error?
69
69
  // Make sure pObject is an object
70
- if (!typeof(pObject) === 'object') return false;
70
+ if (typeof(pObject) != 'object') return false;
71
71
  // Make sure pAddress is a string
72
- if (!typeof(pAddress) === 'string') return false;
72
+ if (typeof(pAddress) != 'string') return false;
73
73
 
74
74
  // TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
75
75
  let tmpSeparatorIndex = pAddress.indexOf('.');
76
76
 
77
77
  // This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
78
- if (tmpSeparatorIndex === -1)
78
+ if (tmpSeparatorIndex == -1)
79
79
  {
80
80
  // Check if the address refers to a boxed property
81
81
  let tmpBracketStartIndex = pAddress.indexOf('[');
@@ -240,15 +240,15 @@ class ManyfestObjectAddressResolver
240
240
  getValueAtAddress (pObject, pAddress)
241
241
  {
242
242
  // Make sure pObject is an object
243
- if (!typeof(pObject) === 'object') return undefined;
243
+ if (typeof(pObject) != 'object') return undefined;
244
244
  // Make sure pAddress is a string
245
- if (!typeof(pAddress) === 'string') return undefined;
245
+ if (typeof(pAddress) != 'string') return undefined;
246
246
 
247
247
  // TODO: Make this work for things like SomeRootObject.Metadata["Some.People.Use.Bad.Object.Property.Names"]
248
248
  let tmpSeparatorIndex = pAddress.indexOf('.');
249
249
 
250
250
  // This is the terminal address string (no more dots so the RECUSION ENDS IN HERE somehow)
251
- if (tmpSeparatorIndex === -1)
251
+ if (tmpSeparatorIndex == -1)
252
252
  {
253
253
  // Check if the address refers to a boxed property
254
254
  let tmpBracketStartIndex = pAddress.indexOf('[');
@@ -410,13 +410,13 @@ class ManyfestObjectAddressResolver
410
410
  setValueAtAddress (pObject, pAddress, pValue)
411
411
  {
412
412
  // Make sure pObject is an object
413
- if (!typeof(pObject) === 'object') return false;
413
+ if (typeof(pObject) != 'object') return false;
414
414
  // Make sure pAddress is a string
415
- if (!typeof(pAddress) === 'string') return false;
415
+ if (typeof(pAddress) != 'string') return false;
416
416
 
417
417
  let tmpSeparatorIndex = pAddress.indexOf('.');
418
418
 
419
- if (tmpSeparatorIndex === -1)
419
+ if (tmpSeparatorIndex == -1)
420
420
  {
421
421
  // Check if it's a boxed property
422
422
  let tmpBracketStartIndex = pAddress.indexOf('[');
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @license MIT
3
+ * @author <steven@velozo.com>
4
+ */
5
+ let libSimpleLog = require('./Manyfest-LogToConsole.js');
6
+
7
+ /**
8
+ * Schema Manipulation Functions
9
+ *
10
+ * @class ManyfestSchemaManipulation
11
+ */
12
+ class ManyfestSchemaManipulation
13
+ {
14
+ constructor(pInfoLog, pErrorLog)
15
+ {
16
+ // Wire in logging
17
+ this.logInfo = (typeof(pInfoLog) === 'function') ? pInfoLog : libSimpleLog;
18
+ this.logError = (typeof(pErrorLog) === 'function') ? pErrorLog : libSimpleLog;
19
+ }
20
+
21
+ // This translates the default address mappings to something different.
22
+ //
23
+ // For instance you can pass in manyfest schema descriptor object:
24
+ // {
25
+ // "Address.Of.a": { "Hash": "a", "Type": "Number" },
26
+ // "Address.Of.b": { "Hash": "b", "Type": "Number" }
27
+ // }
28
+ //
29
+ //
30
+ // And then an address mapping (basically a Hash->Address map)
31
+ // {
32
+ // "a": "New.Address.Of.a",
33
+ // "b": "New.Address.Of.b"
34
+ // }
35
+ //
36
+ // NOTE: This mutates the schema object permanently, altering the base hash.
37
+ // If there is a collision with an existing address, it can lead to overwrites.
38
+ // TODO: Discuss what should happen on collisions.
39
+ resolveAddressMappings(pManyfestSchemaDescriptors, pAddressMapping)
40
+ {
41
+ if (typeof(pManyfestSchemaDescriptors) != 'object')
42
+ {
43
+ this.logError(`Attempted to resolve address mapping but the descriptor was not an object.`);
44
+ return false;
45
+ }
46
+
47
+ if (typeof(pAddressMapping) != 'object')
48
+ {
49
+ // No mappings were passed in
50
+ return true;
51
+ }
52
+
53
+ // Get the arrays of both the schema definition and the hash mapping
54
+ let tmpManyfestAddresses = Object.keys(pManyfestSchemaDescriptors);
55
+ let tmpHashMapping = {};
56
+ tmpManyfestAddresses.forEach(
57
+ (pAddress) =>
58
+ {
59
+ if (pManyfestSchemaDescriptors[pAddress].hasOwnProperty('Hash'))
60
+ {
61
+ tmpHashMapping[pManyfestSchemaDescriptors[pAddress].Hash] = pAddress;
62
+ }
63
+ });
64
+
65
+ let tmpAddressMappingSet = Object.keys(pAddressMapping);
66
+
67
+ tmpAddressMappingSet.forEach(
68
+ (pInputAddress) =>
69
+ {
70
+ let tmpNewDescriptorAddress = pAddressMapping[pInputAddress];
71
+ let tmpOldDescriptorAddress = false;
72
+ let tmpDescriptor = false;
73
+
74
+ // See if there is a matching descriptor either by Address directly or Hash
75
+ if (pManyfestSchemaDescriptors.hasOwnProperty(pInputAddress))
76
+ {
77
+ tmpOldDescriptorAddress = pInputAddress;
78
+ }
79
+ else if (tmpHashMapping.hasOwnProperty(pInputAddress))
80
+ {
81
+ tmpOldDescriptorAddress = tmpHashMapping[pInputAddress];
82
+ }
83
+
84
+ // If there was a matching descriptor in the manifest, store it in the temporary descriptor
85
+ if (tmpOldDescriptorAddress)
86
+ {
87
+ tmpDescriptor = pManyfestSchemaDescriptors[tmpOldDescriptorAddress];
88
+ delete pManyfestSchemaDescriptors[tmpOldDescriptorAddress];
89
+ }
90
+ else
91
+ {
92
+ // Create a new descriptor! Map it to the input address.
93
+ tmpDescriptor = { Hash:pInputAddress };
94
+ }
95
+
96
+ // Now re-add the descriptor to the manyfest schema
97
+ pManyfestSchemaDescriptors[tmpNewDescriptorAddress] = tmpDescriptor;
98
+ });
99
+
100
+ return true;
101
+ }
102
+
103
+ safeResolveAddressMappings(pManyfestSchemaDescriptors, pAddressMapping)
104
+ {
105
+ // This returns the descriptors as a new object, safely remapping without mutating the original schema Descriptors
106
+ let tmpManyfestSchemaDescriptors = JSON.parse(JSON.stringify(pManyfestSchemaDescriptors));
107
+ this.resolveAddressMappings(tmpManyfestSchemaDescriptors, pAddressMapping);
108
+ return tmpManyfestSchemaDescriptors;
109
+ }
110
+ }
111
+
112
+ module.exports = ManyfestSchemaManipulation;
@@ -4,6 +4,8 @@
4
4
  */
5
5
  let libSimpleLog = require('./Manyfest-LogToConsole.js');
6
6
  let libObjectAddressResolver = require('./Manyfest-ObjectAddressResolver.js');
7
+ let libHashTranslation = require('./Manyfest-HashTranslation.js');
8
+ let libSchemaManipulation = require('./Manyfest-SchemaManipulation.js');
7
9
 
8
10
  /**
9
11
  * Manyfest object address-based descriptions and manipulations.
@@ -50,6 +52,10 @@ class Manyfest
50
52
  {
51
53
  this.loadManifest(pManifest);
52
54
  }
55
+
56
+ this.schemaManipulations = new libSchemaManipulation(this.logInfo, this.logError);
57
+
58
+ this.hashTranslations = new libHashTranslation(this.logInfo, this.logError);
53
59
  }
54
60
 
55
61
  /*************************************************************************
@@ -153,14 +159,18 @@ class Manyfest
153
159
  this.elementDescriptors[pAddress] = pDescriptor;
154
160
 
155
161
  // Always add the address as a hash
156
- // TODO: Check if this is a good idea or not.
157
- // Collisions are bound to happen with both representations of the address/hash in here.
158
162
  this.elementHashes[pAddress] = pAddress;
159
163
 
160
164
  if (pDescriptor.hasOwnProperty('Hash'))
161
165
  {
166
+ // TODO: Check if this is a good idea or not..
167
+ // Collisions are bound to happen with both representations of the address/hash in here and developers being able to create their own hashes.
162
168
  this.elementHashes[pDescriptor.Hash] = pAddress;
163
169
  }
170
+ else
171
+ {
172
+ pDescriptor.Hash = pAddress;
173
+ }
164
174
 
165
175
  return true;
166
176
  }
@@ -173,15 +183,7 @@ class Manyfest
173
183
 
174
184
  getDescriptorByHash(pHash)
175
185
  {
176
- if (this.elementHashes.hasOwnProperty(pHash))
177
- {
178
- return this.getDescriptor(this.elementHashes[pHash]);
179
- }
180
- else
181
- {
182
- this.logError(`(${this.scope}) Error in getDescriptorByHash; the Hash ${pHash} doesn't exist in the schema.`);
183
- return undefined;
184
- }
186
+ return this.getDescriptor(this.resolveHashAddress(pHash));
185
187
  }
186
188
 
187
189
  getDescriptor(pAddress)
@@ -195,15 +197,7 @@ class Manyfest
195
197
  // Check if an element exists by its hash
196
198
  checkAddressExistsByHash (pObject, pHash)
197
199
  {
198
- if (this.elementHashes.hasOwnProperty(pHash))
199
- {
200
- return this.checkAddressExists(pObject, this.elementHashes[pHash]);
201
- }
202
- else
203
- {
204
- this.logError(`(${this.scope}) Error in checkAddressExistsByHash; the Hash ${pHash} doesn't exist in the schema.`);
205
- return undefined;
206
- }
200
+ return this.checkAddressExists(pObject,this.resolveHashAddress(pHash));
207
201
  }
208
202
 
209
203
  // Check if an element exists at an address
@@ -212,19 +206,43 @@ class Manyfest
212
206
  return this.objectAddressResolver.checkAddressExists(pObject, pAddress);
213
207
  }
214
208
 
215
-
216
- // Get the value of an element by its hash
217
- getValueByHash (pObject, pHash)
209
+ // Turn a hash into an address, factoring in the translation table.
210
+ resolveHashAddress(pHash)
218
211
  {
219
- if (this.elementHashes.hasOwnProperty(pHash))
212
+ let tmpAddress = undefined;
213
+
214
+ let tmpInElementHashTable = this.elementHashes.hasOwnProperty(pHash);
215
+ let tmpInTranslationTable = this.hashTranslations.translationTable.hasOwnProperty(pHash);
216
+
217
+ // The most straightforward: the hash exists, no translations.
218
+ if (tmpInElementHashTable && !tmpInTranslationTable)
220
219
  {
221
- return this.getValueAtAddress(pObject, this.elementHashes[pHash]);
220
+ tmpAddress = this.elementHashes[pHash];
222
221
  }
222
+ // There is a translation from one hash to another, and, the elementHashes contains the pointer end
223
+ else if (tmpInTranslationTable && this.elementHashes.hasOwnProperty(this.hashTranslations.translate(pHash)))
224
+ {
225
+ tmpAddress = this.elementHashes[this.hashTranslations.translate(pHash)];
226
+ }
227
+ // Use the level of indirection only in the Translation Table
228
+ else if (tmpInTranslationTable)
229
+ {
230
+ tmpAddress = this.hashTranslations.translate(pHash);
231
+ }
232
+ // Just treat the hash as an address.
233
+ // TODO: Discuss this ... it is magic but controversial
223
234
  else
224
235
  {
225
- this.logError(`(${this.scope}) Error in getValueByHash; the Hash ${pHash} doesn't exist in the schema.`);
226
- return undefined;
236
+ tmpAddress = pHash;
227
237
  }
238
+
239
+ return tmpAddress;
240
+ }
241
+
242
+ // Get the value of an element by its hash
243
+ getValueByHash (pObject, pHash)
244
+ {
245
+ return this.getValueAtAddress(pObject, this.resolveHashAddress(pHash));
228
246
  }
229
247
 
230
248
  // Get the value of an element at an address
@@ -236,15 +254,7 @@ class Manyfest
236
254
  // Set the value of an element by its hash
237
255
  setValueByHash(pObject, pHash, pValue)
238
256
  {
239
- if (this.elementHashes.hasOwnProperty(pHash))
240
- {
241
- return this.setValueAtAddress(pObject, this.elementHashes[pHash], pValue);
242
- }
243
- else
244
- {
245
- this.logError(`(${this.scope}) Error in setValueByHash; the Hash ${pHash} doesn't exist in the schema. Value ${pValue} will not be written!`);
246
- return undefined;
247
- }
257
+ return this.setValueAtAddress(pObject, this.resolveHashAddress(pHash), pValue);
248
258
  }
249
259
 
250
260
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Meadow
2
+ * Unit tests for Manyfest
3
3
  *
4
4
  * @license MIT
5
5
  *
@@ -0,0 +1,182 @@
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 Hash Translations',
19
+ function()
20
+ {
21
+ setup (()=> {} );
22
+
23
+ suite
24
+ (
25
+ 'Translation Operations',
26
+ ()=>
27
+ {
28
+ test
29
+ (
30
+ 'A simple hash translation.',
31
+ (fTestComplete)=>
32
+ {
33
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
34
+ // Property not schema, accessed by hash:
35
+ let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
36
+ Expect(tmpCreator)
37
+ .to.equal('General Mills');
38
+ // Create a translation between "Creator" and "Director"
39
+ _Manyfest.hashTranslations.addTranslation({"Director":"Creator"});
40
+ // Creator should still work
41
+ tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
42
+ Expect(tmpCreator)
43
+ .to.equal('General Mills');
44
+ // Director should also work
45
+ tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director');
46
+ Expect(tmpCreator)
47
+ .to.equal('General Mills');
48
+
49
+ fTestComplete();
50
+ }
51
+ );
52
+ test
53
+ (
54
+ 'Multiple translations.',
55
+ (fTestComplete)=>
56
+ {
57
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
58
+ // Property not schema, accessed by hash:
59
+ let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
60
+ Expect(tmpCreator).to.equal('General Mills');
61
+ // Create a translation between "Creator" and "Director" as well as "Author"
62
+ _Manyfest.hashTranslations.addTranslation({"Director":"Creator", "Author":"Creator"});
63
+ // Creator should still work
64
+ tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
65
+ Expect(tmpCreator).to.equal('General Mills');
66
+ // Director should also work
67
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
68
+ // And Author!
69
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal('General Mills');
70
+
71
+ fTestComplete();
72
+ }
73
+ );
74
+ test
75
+ (
76
+ 'Remove a translation.',
77
+ (fTestComplete)=>
78
+ {
79
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
80
+ // Property not schema, accessed by hash:
81
+ let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
82
+ Expect(tmpCreator).to.equal('General Mills');
83
+ // Create a translation between "Creator" and "Director" as well as "Author"
84
+ _Manyfest.hashTranslations.addTranslation({"Director":"Creator", "Author":"Creator"});
85
+ Expect(tmpCreator).to.equal('General Mills');
86
+ // Director should also work
87
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
88
+ // And Author!
89
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal('General Mills');
90
+ // Now remove Director
91
+ _Manyfest.hashTranslations.removeTranslation('Director');
92
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal('General Mills');
93
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
94
+
95
+ fTestComplete();
96
+ }
97
+ );
98
+ test
99
+ (
100
+ 'Remove multiple translations.',
101
+ (fTestComplete)=>
102
+ {
103
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
104
+ // Property not schema, accessed by hash:
105
+ let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
106
+ Expect(tmpCreator).to.equal('General Mills');
107
+ // Create a translation between "Creator" and "Director" as well as "Author"
108
+ _Manyfest.hashTranslations.addTranslation({"Director":"Creator", "Author":"Creator", "Songwriter":"Creator"});
109
+ Expect(tmpCreator).to.equal('General Mills');
110
+ // Director should also work
111
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
112
+ // And Author!
113
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal('General Mills');
114
+ // Now remove Director
115
+ _Manyfest.hashTranslations.removeTranslation({'Director':true,'Author':'TheseValuesDontMatter'});
116
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal(undefined);
117
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
118
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Songwriter')).to.equal('General Mills');
119
+
120
+ fTestComplete();
121
+ }
122
+ );
123
+ test
124
+ (
125
+ 'Remove all translations.',
126
+ (fTestComplete)=>
127
+ {
128
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
129
+ // Property not schema, accessed by hash:
130
+ let tmpCreator = _Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
131
+ Expect(tmpCreator).to.equal('General Mills');
132
+ // Create a translation between "Creator" and "Director" as well as "Author"
133
+ _Manyfest.hashTranslations.addTranslation({"Director":"Creator", "Author":"Creator", "Songwriter":"Creator"});
134
+ Expect(tmpCreator).to.equal('General Mills');
135
+ // Director should also work
136
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal('General Mills');
137
+ // And Author!
138
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal('General Mills');
139
+ // Now remove Director
140
+ _Manyfest.hashTranslations.clearTranslations();
141
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Author')).to.equal(undefined);
142
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Director')).to.equal(undefined);
143
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Songwriter')).to.equal(undefined);
144
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator')).to.equal('General Mills');
145
+
146
+ fTestComplete();
147
+ }
148
+ );
149
+ test
150
+ (
151
+ 'Translate to a value not in the hashes, falling back to address.',
152
+ (fTestComplete)=>
153
+ {
154
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
155
+ // Create a translation between "Creator" and "metadata.identifier", which isn't in the manifest in any way
156
+ _Manyfest.hashTranslations.addTranslation({"Creator":"metadata.identifier"});
157
+ // This address is not in the descriptor address list or the hash list
158
+ Expect(_Manyfest.getValueAtAddress(_SampleDataArchiveOrgFrankenberry, 'metadata.identifier')).to.equal('FrankenberryCountChoculaTevevisionCommercial1971');
159
+ // But now we've pointed the Creator hash to it!
160
+ Expect(_Manyfest.getValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator')).to.equal('FrankenberryCountChoculaTevevisionCommercial1971');
161
+
162
+ fTestComplete();
163
+ }
164
+ );
165
+ test
166
+ (
167
+ 'Add a bogus translation.',
168
+ (fTestComplete)=>
169
+ {
170
+ let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
171
+
172
+ Expect(_Manyfest.hashTranslations.addTranslation('THIS SHOULD BE AN OBJECT')).to.equal(false);
173
+
174
+ Expect(_Manyfest.hashTranslations.translationCount()).to.equal(0);
175
+
176
+ fTestComplete();
177
+ }
178
+ );
179
+ }
180
+ );
181
+ }
182
+ );
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Meadow
2
+ * Unit tests for Manyfest
3
3
  *
4
4
  * @license MIT
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Meadow
2
+ * Unit tests for Manyfest
3
3
  *
4
4
  * @license MIT
5
5
  *
@@ -0,0 +1,93 @@
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 Schema Manipulation',
19
+ function()
20
+ {
21
+ setup (()=> {} );
22
+
23
+ suite
24
+ (
25
+ 'Address Mapping Resolution',
26
+ ()=>
27
+ {
28
+ test
29
+ (
30
+ 'We should be able to remap properties in place.',
31
+ (fTestComplete)=>
32
+ {
33
+ let tmpSchemaDescriptors = (
34
+ {
35
+ "a": { "Hash": "a", "Type": "Number" },
36
+ "b": { "Hash": "b", "Type": "Number" }
37
+ });
38
+
39
+ let tmpTranslationTable = (
40
+ {
41
+ "a": "CarrotCost",
42
+ "b": "AppleCost"
43
+ });
44
+
45
+ Expect(tmpSchemaDescriptors.a.Hash).to.equal('a');
46
+
47
+ let _Manyfest = new libManyfest();
48
+ // Now remap the schema (in-place)
49
+ _Manyfest.schemaManipulations.resolveAddressMappings(tmpSchemaDescriptors, tmpTranslationTable);
50
+
51
+ // The schema should be fundamentally altered to point these addresses to the old hashes
52
+ Expect(tmpSchemaDescriptors.CarrotCost.Hash).to.equal('a');
53
+ Expect(tmpSchemaDescriptors.AppleCost.Hash).to.equal('b');
54
+
55
+ fTestComplete();
56
+ }
57
+ );
58
+ test
59
+ (
60
+ 'We should be able to remap properties safely.',
61
+ (fTestComplete)=>
62
+ {
63
+ let tmpSchemaDescriptors = (
64
+ {
65
+ "a": { "Hash": "a", "Type": "Number" },
66
+ "b": { "Hash": "b", "Type": "Number" }
67
+ });
68
+
69
+ let tmpTranslationTable = (
70
+ {
71
+ "a": "CarrotCost",
72
+ "b": "AppleCost"
73
+ });
74
+
75
+ Expect(tmpSchemaDescriptors.a.Hash).to.equal('a');
76
+
77
+ let _Manyfest = new libManyfest();
78
+ // Now remap the schema (in-place)
79
+ let tmpNewSchemaDescriptors = _Manyfest.schemaManipulations.safeResolveAddressMappings(tmpSchemaDescriptors, tmpTranslationTable);
80
+
81
+ // The schema should be safe
82
+ Expect(tmpSchemaDescriptors.a.Hash).to.equal('a');
83
+ // And a new schema should have been created with the alterations
84
+ Expect(tmpNewSchemaDescriptors.CarrotCost.Hash).to.equal('a');
85
+ Expect(tmpNewSchemaDescriptors.AppleCost.Hash).to.equal('b');
86
+
87
+ fTestComplete();
88
+ }
89
+ );
90
+ }
91
+ );
92
+ }
93
+ );
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Meadow
2
+ * Unit tests for Manyfest
3
3
  *
4
4
  * @license MIT
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Meadow
2
+ * Unit tests for Manyfest
3
3
  *
4
4
  * @license MIT
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Unit tests for Meadow
2
+ * Unit tests for Manyfest
3
3
  *
4
4
  * @license MIT
5
5
  *