manyfest 1.0.13 → 1.0.15
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/README.md +2 -0
- package/package.json +1 -1
- package/source/Manyfest-CleanWrapCharacters.js +28 -0
- package/source/Manyfest-ObjectAddress-CheckAddressExists.js +217 -0
- package/source/Manyfest-ObjectAddress-DeleteValue.js +452 -0
- package/source/{Manyfest-ObjectAddressResolver.js → Manyfest-ObjectAddress-GetValue.js} +27 -404
- package/source/Manyfest-ObjectAddress-SetValue.js +217 -0
- package/source/Manyfest-ObjectAddressGeneration.js +8 -8
- package/source/Manyfest-SchemaManipulation.js +3 -3
- package/source/Manyfest.js +42 -13
- package/test/Manyfest_Object_Delete_tests.js +96 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* @author <steven@velozo.com>
|
|
4
|
+
*/
|
|
5
|
+
let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
6
|
+
let libPrecedent = require('precedent');
|
|
7
|
+
let fCleanWrapCharacters = require('./Manyfest-CleanWrapCharacters.js');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Object Address Resolver - SetValue
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
13
|
+
* be extremely clear what is going on in the recursion for
|
|
14
|
+
* each of the three address resolution functions.
|
|
15
|
+
*
|
|
16
|
+
* Although there is some opportunity to repeat ourselves a
|
|
17
|
+
* bit less in this codebase (e.g. with detection of arrays
|
|
18
|
+
* versus objects versus direct properties), it can make
|
|
19
|
+
* debugging.. challenging. The minified version of the code
|
|
20
|
+
* optimizes out almost anything repeated in here. So please
|
|
21
|
+
* be kind and rewind... meaning please keep the codebase less
|
|
22
|
+
* terse and more verbose so humans can comprehend it.
|
|
23
|
+
*
|
|
24
|
+
*
|
|
25
|
+
* @class ManyfestObjectAddressSetValue
|
|
26
|
+
*/
|
|
27
|
+
class ManyfestObjectAddressSetValue
|
|
28
|
+
{
|
|
29
|
+
constructor(pInfoLog, pErrorLog)
|
|
30
|
+
{
|
|
31
|
+
// Wire in logging
|
|
32
|
+
this.logInfo = (typeof(pInfoLog) == 'function') ? pInfoLog : libSimpleLog;
|
|
33
|
+
this.logError = (typeof(pErrorLog) == 'function') ? pErrorLog : libSimpleLog;
|
|
34
|
+
|
|
35
|
+
this.elucidatorSolver = false;
|
|
36
|
+
this.elucidatorSolverState = {};
|
|
37
|
+
|
|
38
|
+
this.cleanWrapCharacters = fCleanWrapCharacters;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Set the value of an element at an address
|
|
42
|
+
setValueAtAddress (pObject, pAddress, pValue)
|
|
43
|
+
{
|
|
44
|
+
// Make sure pObject is an object
|
|
45
|
+
if (typeof(pObject) != 'object') return false;
|
|
46
|
+
// Make sure pAddress is a string
|
|
47
|
+
if (typeof(pAddress) != 'string') return false;
|
|
48
|
+
|
|
49
|
+
let tmpSeparatorIndex = pAddress.indexOf('.');
|
|
50
|
+
|
|
51
|
+
if (tmpSeparatorIndex == -1)
|
|
52
|
+
{
|
|
53
|
+
// Check if it's a boxed property
|
|
54
|
+
let tmpBracketStartIndex = pAddress.indexOf('[');
|
|
55
|
+
let tmpBracketStopIndex = pAddress.indexOf(']');
|
|
56
|
+
// Boxed elements look like this:
|
|
57
|
+
// MyValues[10]
|
|
58
|
+
// MyValues['Name']
|
|
59
|
+
// MyValues["Age"]
|
|
60
|
+
// MyValues[`Cost`]
|
|
61
|
+
//
|
|
62
|
+
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
63
|
+
// The requirements to detect a boxed element are:
|
|
64
|
+
// 1) The start bracket is after character 0
|
|
65
|
+
if ((tmpBracketStartIndex > 0)
|
|
66
|
+
// 2) The end bracket has something between them
|
|
67
|
+
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
68
|
+
// 3) There is data
|
|
69
|
+
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
70
|
+
{
|
|
71
|
+
// The "Name" of the Object contained too the left of the bracket
|
|
72
|
+
let tmpBoxedPropertyName = pAddress.substring(0, tmpBracketStartIndex).trim();
|
|
73
|
+
|
|
74
|
+
// If the subproperty doesn't test as a proper Object, none of the rest of this is possible.
|
|
75
|
+
// This is a rare case where Arrays testing as Objects is useful
|
|
76
|
+
if (typeof(pObject[tmpBoxedPropertyName]) !== 'object')
|
|
77
|
+
{
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// The "Reference" to the property within it, either an array element or object property
|
|
82
|
+
let tmpBoxedPropertyReference = pAddress.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
|
|
83
|
+
// Attempt to parse the reference as a number, which will be used as an array element
|
|
84
|
+
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
85
|
+
|
|
86
|
+
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
87
|
+
// This seems confusing to me at first read, so explaination:
|
|
88
|
+
// Is the Boxed Object an Array? TRUE
|
|
89
|
+
// And is the Reference inside the boxed Object not a number? TRUE
|
|
90
|
+
// --> So when these are in agreement, it's an impossible access state
|
|
91
|
+
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
|
|
92
|
+
{
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
97
|
+
// otherwise we will try to treat it as a dynamic object property.
|
|
98
|
+
if (isNaN(tmpBoxedPropertyNumber))
|
|
99
|
+
{
|
|
100
|
+
// This isn't a number ... let's treat it as a dynamic object property.
|
|
101
|
+
// We would expect the property to be wrapped in some kind of quotes so strip them
|
|
102
|
+
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
103
|
+
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
104
|
+
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
105
|
+
|
|
106
|
+
// Return the value in the property
|
|
107
|
+
pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference] = pValue;
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
else
|
|
111
|
+
{
|
|
112
|
+
pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber] = pValue;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
{
|
|
118
|
+
// Now is the time in recursion to set the value in the object
|
|
119
|
+
pObject[pAddress] = pValue;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else
|
|
124
|
+
{
|
|
125
|
+
let tmpSubObjectName = pAddress.substring(0, tmpSeparatorIndex);
|
|
126
|
+
let tmpNewAddress = pAddress.substring(tmpSeparatorIndex+1);
|
|
127
|
+
|
|
128
|
+
// Test if the tmpNewAddress is an array or object
|
|
129
|
+
// Check if it's a boxed property
|
|
130
|
+
let tmpBracketStartIndex = tmpSubObjectName.indexOf('[');
|
|
131
|
+
let tmpBracketStopIndex = tmpSubObjectName.indexOf(']');
|
|
132
|
+
// Boxed elements look like this:
|
|
133
|
+
// MyValues[42]
|
|
134
|
+
// MyValues['Color']
|
|
135
|
+
// MyValues["Weight"]
|
|
136
|
+
// MyValues[`Diameter`]
|
|
137
|
+
//
|
|
138
|
+
// When we are passed SomeObject["Name"] this code below recurses as if it were SomeObject.Name
|
|
139
|
+
// The requirements to detect a boxed element are:
|
|
140
|
+
// 1) The start bracket is after character 0
|
|
141
|
+
if ((tmpBracketStartIndex > 0)
|
|
142
|
+
// 2) The end bracket has something between them
|
|
143
|
+
&& (tmpBracketStopIndex > tmpBracketStartIndex)
|
|
144
|
+
// 3) There is data
|
|
145
|
+
&& (tmpBracketStopIndex - tmpBracketStartIndex > 1))
|
|
146
|
+
{
|
|
147
|
+
let tmpBoxedPropertyName = tmpSubObjectName.substring(0, tmpBracketStartIndex).trim();
|
|
148
|
+
|
|
149
|
+
let tmpBoxedPropertyReference = tmpSubObjectName.substring(tmpBracketStartIndex+1, tmpBracketStopIndex).trim();
|
|
150
|
+
|
|
151
|
+
let tmpBoxedPropertyNumber = parseInt(tmpBoxedPropertyReference, 10);
|
|
152
|
+
|
|
153
|
+
// Guard: If the referrant is a number and the boxed property is not an array, or vice versa, return undefined.
|
|
154
|
+
// This seems confusing to me at first read, so explaination:
|
|
155
|
+
// Is the Boxed Object an Array? TRUE
|
|
156
|
+
// And is the Reference inside the boxed Object not a number? TRUE
|
|
157
|
+
// --> So when these are in agreement, it's an impossible access state
|
|
158
|
+
// This could be a failure in the recursion chain because they passed something like this in:
|
|
159
|
+
// StudentData.Sections.Algebra.Students[1].Tardy
|
|
160
|
+
// BUT
|
|
161
|
+
// StudentData.Sections.Algebra.Students is an object, so the [1].Tardy is not possible to access
|
|
162
|
+
// This could be a failure in the recursion chain because they passed something like this in:
|
|
163
|
+
// StudentData.Sections.Algebra.Students["JaneDoe"].Grade
|
|
164
|
+
// BUT
|
|
165
|
+
// StudentData.Sections.Algebra.Students is an array, so the ["JaneDoe"].Grade is not possible to access
|
|
166
|
+
// TODO: Should this be an error or something? Should we keep a log of failures like this?
|
|
167
|
+
if (Array.isArray(pObject[tmpBoxedPropertyName]) == isNaN(tmpBoxedPropertyNumber))
|
|
168
|
+
{
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//This is a bracketed value
|
|
173
|
+
// 4) If the middle part is *only* a number (no single, double or backtick quotes) it is an array element,
|
|
174
|
+
// otherwise we will try to reat it as a dynamic object property.
|
|
175
|
+
if (isNaN(tmpBoxedPropertyNumber))
|
|
176
|
+
{
|
|
177
|
+
// This isn't a number ... let's treat it as a dynanmic object property.
|
|
178
|
+
tmpBoxedPropertyReference = this.cleanWrapCharacters('"', tmpBoxedPropertyReference);
|
|
179
|
+
tmpBoxedPropertyReference = this.cleanWrapCharacters('`', tmpBoxedPropertyReference);
|
|
180
|
+
tmpBoxedPropertyReference = this.cleanWrapCharacters("'", tmpBoxedPropertyReference);
|
|
181
|
+
|
|
182
|
+
// Recurse directly into the subobject
|
|
183
|
+
return this.setValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyReference], tmpNewAddress, pValue);
|
|
184
|
+
}
|
|
185
|
+
else
|
|
186
|
+
{
|
|
187
|
+
// We parsed a valid number out of the boxed property name, so recurse into the array
|
|
188
|
+
return this.setValueAtAddress(pObject[tmpBoxedPropertyName][tmpBoxedPropertyNumber], tmpNewAddress, pValue);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// If there is an object property already named for the sub object, but it isn't an object
|
|
193
|
+
// then the system can't set the value in there. Error and abort!
|
|
194
|
+
if (pObject.hasOwnProperty(tmpSubObjectName) && typeof(pObject[tmpSubObjectName]) !== 'object')
|
|
195
|
+
{
|
|
196
|
+
if (!pObject.hasOwnProperty('__ERROR'))
|
|
197
|
+
pObject['__ERROR'] = {};
|
|
198
|
+
// Put it in an error object so data isn't lost
|
|
199
|
+
pObject['__ERROR'][pAddress] = pValue;
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
else if (pObject.hasOwnProperty(tmpSubObjectName))
|
|
203
|
+
{
|
|
204
|
+
// If there is already a subobject pass that to the recursive thingy
|
|
205
|
+
return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
|
|
206
|
+
}
|
|
207
|
+
else
|
|
208
|
+
{
|
|
209
|
+
// Create a subobject and then pass that
|
|
210
|
+
pObject[tmpSubObjectName] = {};
|
|
211
|
+
return this.setValueAtAddress(pObject[tmpSubObjectName], tmpNewAddress, pValue);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
module.exports = ManyfestObjectAddressSetValue;
|
|
@@ -7,14 +7,14 @@ let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
|
7
7
|
/**
|
|
8
8
|
* Object Address Generation
|
|
9
9
|
*
|
|
10
|
-
* Automagically generate addresses and properties based on a passed-in object,
|
|
10
|
+
* Automagically generate addresses and properties based on a passed-in object,
|
|
11
11
|
* to be used for easy creation of schemas. Meant to simplify the lives of
|
|
12
12
|
* developers wanting to create schemas without typing a bunch of stuff.
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
* IMPORTANT NOTE: This code is intentionally more verbose than necessary, to
|
|
15
15
|
* be extremely clear what is going on in the recursion for
|
|
16
16
|
* each of the three address resolution functions.
|
|
17
|
-
*
|
|
17
|
+
*
|
|
18
18
|
* Although there is some opportunity to repeat ourselves a
|
|
19
19
|
* bit less in this codebase (e.g. with detection of arrays
|
|
20
20
|
* versus objects versus direct properties), it can make
|
|
@@ -22,7 +22,7 @@ let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
|
22
22
|
* optimizes out almost anything repeated in here. So please
|
|
23
23
|
* be kind and rewind... meaning please keep the codebase less
|
|
24
24
|
* terse and more verbose so humans can comprehend it.
|
|
25
|
-
*
|
|
25
|
+
*
|
|
26
26
|
*
|
|
27
27
|
* @class ManyfestObjectAddressGeneration
|
|
28
28
|
*/
|
|
@@ -42,7 +42,7 @@ class ManyfestObjectAddressGeneration
|
|
|
42
42
|
// quickly. This is not meant to be used directly to generate schemas, but
|
|
43
43
|
// instead as a starting point for scripts or UIs.
|
|
44
44
|
//
|
|
45
|
-
// This will return a mega set of key:value pairs with all possible schema
|
|
45
|
+
// This will return a mega set of key:value pairs with all possible schema
|
|
46
46
|
// permutations and default values (when not an object) and everything else.
|
|
47
47
|
generateAddressses (pObject, pBaseAddress, pSchema)
|
|
48
48
|
{
|
|
@@ -93,7 +93,7 @@ class ManyfestObjectAddressGeneration
|
|
|
93
93
|
{
|
|
94
94
|
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
for (let i = 0; i < pObject.length; i++)
|
|
98
98
|
{
|
|
99
99
|
this.generateAddressses(pObject[i], `${tmpBaseAddress}[${i}]`, tmpSchema);
|
|
@@ -107,13 +107,13 @@ class ManyfestObjectAddressGeneration
|
|
|
107
107
|
tmpSchema[tmpBaseAddress] = tmpSchemaObjectEntry;
|
|
108
108
|
tmpBaseAddress += '.';
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
let tmpObjectProperties = Object.keys(pObject);
|
|
112
112
|
|
|
113
113
|
for (let i = 0; i < tmpObjectProperties.length; i++)
|
|
114
114
|
{
|
|
115
115
|
this.generateAddressses(pObject[tmpObjectProperties[i]], `${tmpBaseAddress}${tmpObjectProperties[i]}`, tmpSchema);
|
|
116
|
-
}
|
|
116
|
+
}
|
|
117
117
|
}
|
|
118
118
|
break;
|
|
119
119
|
case 'symbol':
|
|
@@ -30,7 +30,7 @@ class ManyfestSchemaManipulation
|
|
|
30
30
|
// And then an address mapping (basically a Hash->Address map)
|
|
31
31
|
// {
|
|
32
32
|
// "a": "New.Address.Of.a",
|
|
33
|
-
// "b": "New.Address.Of.b"
|
|
33
|
+
// "b": "New.Address.Of.b"
|
|
34
34
|
// }
|
|
35
35
|
//
|
|
36
36
|
// NOTE: This mutates the schema object permanently, altering the base hash.
|
|
@@ -123,14 +123,14 @@ class ManyfestSchemaManipulation
|
|
|
123
123
|
let tmpDescriptorAddresses = Object.keys(tmpSource);
|
|
124
124
|
|
|
125
125
|
tmpDescriptorAddresses.forEach(
|
|
126
|
-
(pDescriptorAddress) =>
|
|
126
|
+
(pDescriptorAddress) =>
|
|
127
127
|
{
|
|
128
128
|
if (!tmpNewManyfestSchemaDescriptors.hasOwnProperty(pDescriptorAddress))
|
|
129
129
|
{
|
|
130
130
|
tmpNewManyfestSchemaDescriptors[pDescriptorAddress] = tmpSource[pDescriptorAddress];
|
|
131
131
|
}
|
|
132
132
|
});
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
return tmpNewManyfestSchemaDescriptors;
|
|
135
135
|
}
|
|
136
136
|
}
|
package/source/Manyfest.js
CHANGED
|
@@ -7,7 +7,10 @@ let libSimpleLog = require('./Manyfest-LogToConsole.js');
|
|
|
7
7
|
let libPrecedent = require('precedent');
|
|
8
8
|
|
|
9
9
|
let libHashTranslation = require('./Manyfest-HashTranslation.js');
|
|
10
|
-
let
|
|
10
|
+
let libObjectAddressCheckAddressExists = require('./Manyfest-ObjectAddress-CheckAddressExists.js');
|
|
11
|
+
let libObjectAddressGetValue = require('./Manyfest-ObjectAddress-GetValue.js');
|
|
12
|
+
let libObjectAddressSetValue = require('./Manyfest-ObjectAddress-SetValue.js');
|
|
13
|
+
let libObjectAddressDeleteValue = require('./Manyfest-ObjectAddress-DeleteValue.js');
|
|
11
14
|
let libObjectAddressGeneration = require('./Manyfest-ObjectAddressGeneration.js');
|
|
12
15
|
let libSchemaManipulation = require('./Manyfest-SchemaManipulation.js');
|
|
13
16
|
|
|
@@ -26,12 +29,15 @@ class Manyfest
|
|
|
26
29
|
this.logError = (typeof(pErrorLog) === 'function') ? pErrorLog : libSimpleLog;
|
|
27
30
|
|
|
28
31
|
// Create an object address resolver and map in the functions
|
|
29
|
-
this.
|
|
32
|
+
this.objectAddressCheckAddressExists = new libObjectAddressCheckAddressExists(this.logInfo, this.logError);
|
|
33
|
+
this.objectAddressGetValue = new libObjectAddressGetValue(this.logInfo, this.logError);
|
|
34
|
+
this.objectAddressSetValue = new libObjectAddressSetValue(this.logInfo, this.logError);
|
|
35
|
+
this.objectAddressDeleteValue = new libObjectAddressDeleteValue(this.logInfo, this.logError);
|
|
30
36
|
|
|
31
37
|
this.options = (
|
|
32
38
|
{
|
|
33
39
|
strict: false,
|
|
34
|
-
defaultValues:
|
|
40
|
+
defaultValues:
|
|
35
41
|
{
|
|
36
42
|
"String": "",
|
|
37
43
|
"Number": 0,
|
|
@@ -83,7 +89,19 @@ class Manyfest
|
|
|
83
89
|
this.dataSolverState = {};
|
|
84
90
|
|
|
85
91
|
this.libElucidator = undefined;
|
|
86
|
-
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
setElucidatorSolvers(pElucidatorSolver, pElucidatorSolverState)
|
|
95
|
+
{
|
|
96
|
+
this.objectAddressCheckAddressExists.elucidatorSolver = pElucidatorSolver;
|
|
97
|
+
this.objectAddressGetValue.elucidatorSolver = pElucidatorSolver;
|
|
98
|
+
this.objectAddressSetValue.elucidatorSolver = pElucidatorSolver;
|
|
99
|
+
this.objectAddressDeleteValue.elucidatorSolver = pElucidatorSolver;
|
|
100
|
+
|
|
101
|
+
this.objectAddressCheckAddressExists.elucidatorSolverState = pElucidatorSolverState;
|
|
102
|
+
this.objectAddressGetValue.elucidatorSolverState = pElucidatorSolverState;
|
|
103
|
+
this.objectAddressSetValue.elucidatorSolverState = pElucidatorSolverState;
|
|
104
|
+
this.objectAddressDeleteValue.elucidatorSolverState = pElucidatorSolverState;
|
|
87
105
|
}
|
|
88
106
|
|
|
89
107
|
clone()
|
|
@@ -168,7 +186,6 @@ class Manyfest
|
|
|
168
186
|
// This is mostly meant for if statements to filter.
|
|
169
187
|
// Basically on aggregation, if a filter is set it will set "keep record" to true and let the solver decide differently.
|
|
170
188
|
this.dataSolvers = new libElucidator(pManifest.Solvers, this.logInfo, this.logError);
|
|
171
|
-
this.objectAddressResolver.elucidatorSolver = this.dataSolvers;
|
|
172
189
|
|
|
173
190
|
// Load the solver state in so each instruction can have internal config
|
|
174
191
|
// TODO: Should this just be a part of the lower layer pattern?
|
|
@@ -177,7 +194,8 @@ class Manyfest
|
|
|
177
194
|
{
|
|
178
195
|
this.dataSolverState[tmpSolverKeys] = pManifest.Solvers[tmpSolverKeys[i]];
|
|
179
196
|
}
|
|
180
|
-
|
|
197
|
+
|
|
198
|
+
this.setElucidatorSolvers(this.dataSolvers, this.dataSolverState);
|
|
181
199
|
}
|
|
182
200
|
}
|
|
183
201
|
|
|
@@ -236,7 +254,7 @@ class Manyfest
|
|
|
236
254
|
{
|
|
237
255
|
this.logError(`(${this.scope}) Error loading object descriptor for address '${pAddress}' from manifest object. Expecting an object but property was type ${typeof(pDescriptor)}.`);
|
|
238
256
|
return false;
|
|
239
|
-
}
|
|
257
|
+
}
|
|
240
258
|
}
|
|
241
259
|
|
|
242
260
|
getDescriptorByHash(pHash)
|
|
@@ -272,7 +290,7 @@ class Manyfest
|
|
|
272
290
|
// Check if an element exists at an address
|
|
273
291
|
checkAddressExists (pObject, pAddress)
|
|
274
292
|
{
|
|
275
|
-
return this.
|
|
293
|
+
return this.objectAddressCheckAddressExists.checkAddressExists(pObject, pAddress);
|
|
276
294
|
}
|
|
277
295
|
|
|
278
296
|
// Turn a hash into an address, factoring in the translation table.
|
|
@@ -293,7 +311,7 @@ class Manyfest
|
|
|
293
311
|
{
|
|
294
312
|
tmpAddress = this.elementHashes[this.hashTranslations.translate(pHash)];
|
|
295
313
|
}
|
|
296
|
-
// Use the level of indirection only in the Translation Table
|
|
314
|
+
// Use the level of indirection only in the Translation Table
|
|
297
315
|
else if (tmpInTranslationTable)
|
|
298
316
|
{
|
|
299
317
|
tmpAddress = this.hashTranslations.translate(pHash);
|
|
@@ -325,7 +343,7 @@ class Manyfest
|
|
|
325
343
|
// Get the value of an element at an address
|
|
326
344
|
getValueAtAddress (pObject, pAddress)
|
|
327
345
|
{
|
|
328
|
-
let tmpValue = this.
|
|
346
|
+
let tmpValue = this.objectAddressGetValue.getValueAtAddress(pObject, pAddress);
|
|
329
347
|
|
|
330
348
|
if (typeof(tmpValue) == 'undefined')
|
|
331
349
|
{
|
|
@@ -342,11 +360,22 @@ class Manyfest
|
|
|
342
360
|
return this.setValueAtAddress(pObject, this.resolveHashAddress(pHash), pValue);
|
|
343
361
|
}
|
|
344
362
|
|
|
345
|
-
|
|
346
363
|
// Set the value of an element at an address
|
|
347
364
|
setValueAtAddress (pObject, pAddress, pValue)
|
|
348
365
|
{
|
|
349
|
-
return this.
|
|
366
|
+
return this.objectAddressSetValue.setValueAtAddress(pObject, pAddress, pValue);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Delete the value of an element by its hash
|
|
370
|
+
deleteValueByHash(pObject, pHash, pValue)
|
|
371
|
+
{
|
|
372
|
+
return this.deleteValueAtAddress(pObject, this.resolveHashAddress(pHash), pValue);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Delete the value of an element at an address
|
|
376
|
+
deleteValueAtAddress (pObject, pAddress, pValue)
|
|
377
|
+
{
|
|
378
|
+
return this.objectAddressDeleteValue.deleteValueAtAddress(pObject, pAddress, pValue);
|
|
350
379
|
}
|
|
351
380
|
|
|
352
381
|
// Validate the consistency of an object against the schema
|
|
@@ -438,7 +467,7 @@ class Manyfest
|
|
|
438
467
|
{
|
|
439
468
|
addValidationError(tmpDescriptor.Address, `has a DataType ${tmpDescriptor.DataType} but is not parsable as a Date by Javascript`);
|
|
440
469
|
}
|
|
441
|
-
|
|
470
|
+
|
|
442
471
|
default:
|
|
443
472
|
// Check if this is a string, in the default case
|
|
444
473
|
// Note this is only when a DataType is specified and it is an unrecognized data type.
|
|
@@ -0,0 +1,96 @@
|
|
|
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 = JSON.parse(JSON.stringify(require('./Data-Archive-org-Frankenberry.json')));
|
|
15
|
+
|
|
16
|
+
suite
|
|
17
|
+
(
|
|
18
|
+
'Manyfest Object Property Delete',
|
|
19
|
+
function()
|
|
20
|
+
{
|
|
21
|
+
setup (()=> {} );
|
|
22
|
+
|
|
23
|
+
suite
|
|
24
|
+
(
|
|
25
|
+
'Basic Delete',
|
|
26
|
+
()=>
|
|
27
|
+
{
|
|
28
|
+
test
|
|
29
|
+
(
|
|
30
|
+
'It should be trivial to delete subproperties without a schema.',
|
|
31
|
+
(fTestComplete)=>
|
|
32
|
+
{
|
|
33
|
+
let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
|
|
34
|
+
// Property not in schema:
|
|
35
|
+
Expect(_Manyfest.deleteValueAtAddress(_SampleDataArchiveOrgFrankenberry, 'metadata.title'))
|
|
36
|
+
.to.equal(true);
|
|
37
|
+
Expect(_SampleDataArchiveOrgFrankenberry.metadata.title)
|
|
38
|
+
.to.equal(undefined);
|
|
39
|
+
fTestComplete();
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
test
|
|
43
|
+
(
|
|
44
|
+
'It should be trivial to delete subproperties with a schema by hash.',
|
|
45
|
+
(fTestComplete)=>
|
|
46
|
+
{
|
|
47
|
+
let _Manyfest = new libManyfest({ Scope:'Archive.org', Descriptors: {'metadata.creator': {Name:'Creator', Hash:'Creator'}}});
|
|
48
|
+
// Property not schema, accessed by hash:
|
|
49
|
+
let tmpDeleted = _Manyfest.deleteValueByHash(_SampleDataArchiveOrgFrankenberry, 'Creator');
|
|
50
|
+
Expect(_SampleDataArchiveOrgFrankenberry.metadata.creator)
|
|
51
|
+
.to.equal(undefined);
|
|
52
|
+
Expect(tmpDeleted)
|
|
53
|
+
.to.equal(true);
|
|
54
|
+
fTestComplete();
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
test
|
|
58
|
+
(
|
|
59
|
+
'Nothing bad should happen deleting a non existant property.',
|
|
60
|
+
(fTestComplete)=>
|
|
61
|
+
{
|
|
62
|
+
let animalManyfest = new libManyfest(
|
|
63
|
+
{
|
|
64
|
+
"Scope": "Animal",
|
|
65
|
+
"Descriptors":
|
|
66
|
+
{
|
|
67
|
+
"IDAnimal": { "Name":"Database ID", "Description":"The unique integer-based database identifier for an Animal record.", "DataType":"Integer" },
|
|
68
|
+
"Name": { "Description":"The animal's colloquial species name (e.g. Rabbit, Dog, Bear, Mongoose)." },
|
|
69
|
+
"Type": { "Description":"Whether or not the animal is wild, domesticated, agricultural, in a research lab or a part of a zoo.." },
|
|
70
|
+
"MedicalStats":
|
|
71
|
+
{
|
|
72
|
+
"Name":"Medical Statistics", "Description":"Basic medical statistics for this animal"
|
|
73
|
+
},
|
|
74
|
+
"MedicalStats.Temps.MinET": { "Name":"Minimum Environmental Temperature", "NameShort":"MinET", "Description":"Safest minimum temperature for this animal to survive in."},
|
|
75
|
+
"MedicalStats.Temps.MaxET": { "Name":"Maximum Environmental Temperature", "NameShort":"MaxET", "Description":"Safest maximum temperature for this animal to survive in."},
|
|
76
|
+
"MedicalStats.Temps.CET":
|
|
77
|
+
{
|
|
78
|
+
"Name":"Comfortable Environmental Temperature",
|
|
79
|
+
"NameShort":"Comf Env Temp",
|
|
80
|
+
"Hash":"ComfET",
|
|
81
|
+
"Description":"The most comfortable temperature for this animal to survive in.",
|
|
82
|
+
"Default": "96.8"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
Expect(animalManyfest.deleteValueAtAddress({MedicalStats: { Temps: { MinET:200 }},Name:'Froggy'}, 'MedicalStats.Temps.HighETBUTIDONTEXISTDOI'))
|
|
88
|
+
.to.equal(true);
|
|
89
|
+
|
|
90
|
+
fTestComplete();
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
);
|