Skip to content

Commit 6894171

Browse files
author
Matheus Marsiglio
committed
New update mathod and overall fixes
- add update spec file - improve update method to do not try to update when state is not dispatched yet - add script for single tests run and all tests - fix bugs in NucleoList dispatch - change version in package.json - improve README documentation - Add commented functionality for custom primitive types - finish and polish update functionality - Add licence file - Fixes in README documentation - Create index file with all Nucleo library methods
1 parent c30d6bc commit 6894171

File tree

9 files changed

+288
-39
lines changed

9 files changed

+288
-39
lines changed

LICENCE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) Matheus Marsiglio. http://mtmr0x.com
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const productsContract = new NucleoObject({
5959
const contracts = {
6060
user: userContract,
6161
products: productsContract
62-
}
62+
};
6363
```
6464

6565
### Creating the store:
@@ -85,17 +85,17 @@ Dispatch function, considering user contract above:
8585

8686
```javascript
8787

88-
dispatch('user', { name: { firstName: 'John', lastName: 'Nor' } });
88+
dispatch('user')({ name: { firstName: 'John', lastName: 'Nor' } });
8989
// it'll fail because it's missing age field
9090

91-
dispatch('user', { name: { firstName: 'John', lastName: 'Nor' }, age: 27 });
91+
dispatch('user')({ name: { firstName: 'John', lastName: 'Nor' }, age: 27 });
9292
// it'll save the data to store properly
9393
```
9494

9595
Update function, considering user contract above:
9696

9797
```javascript
98-
update('user', { name: { firstName: 'John' }});
98+
update('user')({ name: { firstName: 'John' }});
9999
// it'll update only the user first name and only if this item has been already created in the store before
100100
```
101101

@@ -115,9 +115,7 @@ listener({ contractName }); // This way you can understand better what was updat
115115

116116
## Error management
117117

118-
Considering Nucleo is strongly typed and is designed to run in a client side environment, errors might be tricky to handle in how your user is inputting data and Nucleo is also designed to make sure your rules will work out and your user can make mistakes.
119-
120-
`dispatch` and `update` methods return an object containing the following structure:
118+
Nucleo makes error management easy by type checking every level of contracts and returning an Object human and machine readable. The `update` and `dispatch` methods return an object with the following structure:
121119

122120
```javascript
123121
{
@@ -133,29 +131,30 @@ Considering Nucleo is strongly typed and is designed to run in a client side env
133131
}
134132
```
135133

136-
Let's go deep into an example using a Nucleo custom primitive type with an inferred rule:
134+
Code example:
137135

138136
```javascript
139137
import {
140138
NucleoString,
141139
NucleoObject
142140
} from 'nucleo-js';
143141

144-
const ageRule = (value) => {
145-
return value < 100; // you defined here that age can not be higher than 100
146-
}
147-
148142
const userType = new NucleoObject({
149143
name: 'user',
150144
fields: {
151-
name: NucleoString(),
152-
age: NucleoString(ageRule)
145+
name: NucleoString,
153146
}
154147
});
155148

156-
const dispatcher = update('user', { age: 140 });
149+
const contracts = {
150+
user: userType
151+
};
152+
153+
const { update } = createStore(contracts); // send contracts to create the store
154+
155+
const user = update('user')({ age: 140 });
157156

158-
console.log(dispatcher.error); // here you'll find the error below:
157+
console.log(user.errors); // here you'll find the error below:
159158
/*
160159
161160
[

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "super-cool-data-tool",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"main": "src/index.js",
55
"author": "Matheus Marsiglio <[email protected]>",
66
"license": "MIT",
@@ -17,7 +17,8 @@
1717
"scripts": {
1818
"start": "ts-node ./src/index.ts",
1919
"nodemon": "nodemon",
20-
"test": "mocha -r ts-node/register src/**/*.spec.ts",
20+
"tests": "mocha -r ts-node/register src/**/*.spec.ts",
21+
"test": "mocha -r ts-node/register",
2122
"compile": "tsc"
2223
}
2324
}

src/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createStore } from './store';
2+
3+
import {
4+
NucleoString,
5+
NucleoNumber,
6+
NucleoBoolean
7+
} from './nucleoTypes/primitive'
8+
9+
import NucleoObject from './nucleoTypes/NucleoObject';
10+
import NucleoList from './nucleoTypes/NucleoList';
11+
12+
export {
13+
createStore,
14+
NucleoString,
15+
NucleoNumber,
16+
NucleoBoolean,
17+
NucleoList,
18+
NucleoObject
19+
};
20+

src/lawyer.ts

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,90 @@ import NucleoList from './nucleoTypes/NucleoList';
33

44
import { NucleoObjectType } from './_types/NucleoObjectType';
55

6-
const executeListeners = (contractName: string, listeners: Array<Function>) => {
6+
const executeListeners = (contractName: string, listeners: Array<Function>) => {
77
for (let i = 0; i < listeners.length; i++) {
88
listeners[i]({ contractName });
99
}
1010
};
1111

12+
const indexSearch = (contractData: any, data: any, newData:any = {}) => {
13+
const dataKeys = Object.keys(data);
14+
const contractDataKeys = Object.keys(contractData);
15+
16+
for (let i = 0; contractDataKeys.length > i; i++) {
17+
// reflection for appending data to newData
18+
const dataTypeReflection = () => ({
19+
'object': () => {
20+
// if current data is object, recusirvely call indexSearch
21+
const bufferData = data[contractDataKeys[i]] || contractData[contractDataKeys[i]];
22+
newData[contractDataKeys[i]] = {}
23+
return indexSearch(contractData[contractDataKeys[i]], bufferData, newData[contractDataKeys[i]]);
24+
},
25+
'primitive': () => {
26+
if (data[contractDataKeys[i]]) {
27+
return newData[contractDataKeys[i]] = data[contractDataKeys[i]];
28+
}
29+
return newData[contractDataKeys[i]] = contractData[contractDataKeys[i]];
30+
}
31+
});
32+
if (typeof contractData[contractDataKeys[i]] === 'object') {
33+
dataTypeReflection()['object']();
34+
continue;
35+
}
36+
dataTypeReflection()['primitive']();
37+
}
38+
39+
return newData;
40+
}
41+
1242
const saveMethodReflection = (store: any, contractName: string) => ({
1343
dispatch: (data: any) => {
1444
return store[contractName] = data;
1545
},
1646
update: (data: any) => {
17-
return store[contractName] = Object.assign(store[contractName], data);
47+
return store[contractName] = indexSearch(store[contractName], data);
1848
}
1949
});
2050

21-
export default function lawyer(contract: NucleoObjectType, data: any, saveMethod:'update'|'dispatch') {
51+
interface LawyerInterface {
52+
contract: NucleoObjectType;
53+
data: any;
54+
saveMethod: 'update'|'dispatch';
55+
__errors__: Array<any>;
56+
}
57+
58+
export default function lawyer({
59+
contract,
60+
data,
61+
saveMethod,
62+
__errors__,
63+
}:LawyerInterface) {
2264
const { fields: contractFields }:any = contract;
2365
const dataKeys:Array<string> = Object.keys(data);
2466
const contractName:string = contract.name;
25-
let __errors__: Array<any> = [];
2667

2768
if (dataKeys.length !== Object.keys(contractFields).length && saveMethod === 'dispatch') {
2869
throw Error(
29-
`Fata error: In dispatch, the dispatched data and the contract must match in every level. For changing just few values from ${contractName} contract, use update() method.`
70+
`Fatal error: In dispatch, the dispatched data and the contract must match in every level. For changing just few values from ${contractName} contract, use update() method.`
3071
);
3172
}
3273

33-
// loop checking object values comparison
74+
// loop object values comparison
3475
for (let i = 0; dataKeys.length > i; i++) {
3576
const currentDataKey = data[dataKeys[i]];
36-
// recursion to call itself when is NucleoObject instance
77+
// REGION NucleoObject
3778
if (contractFields[dataKeys[i]] instanceof NucleoObject) {
38-
lawyer(contractFields[dataKeys[i]], currentDataKey, saveMethod);
79+
lawyer({
80+
contract: contractFields[dataKeys[i]],
81+
data: currentDataKey,
82+
saveMethod,
83+
__errors__
84+
});
3985
continue;
4086
}
87+
// END REGION NucleoObject
4188

89+
// REGION NucleoList
4290
if ((contractFields[dataKeys[i]] instanceof NucleoList) && Array.isArray(currentDataKey)) {
4391
const _listItemType = contractFields[dataKeys[i]].getListChildrenType();
4492
const _NucleoItemType = contractFields[dataKeys[i]][_listItemType];
@@ -59,7 +107,12 @@ export default function lawyer(contract: NucleoObjectType, data: any, saveMethod
59107
NucleoObject: () => {
60108
if (_NucleoItemType instanceof NucleoObject) {
61109
for (let d = 0; d < currentDataKey.length; d++) {
62-
lawyer(_NucleoItemType, currentDataKey[d], saveMethod);
110+
lawyer({
111+
contract: _NucleoItemType,
112+
data: currentDataKey[d],
113+
saveMethod,
114+
__errors__
115+
});
63116
}
64117
}
65118
},
@@ -73,21 +126,24 @@ export default function lawyer(contract: NucleoObjectType, data: any, saveMethod
73126
error: `NucleoList should receive data as list, but got ${typeof currentDataKey}`
74127
});
75128
}
129+
// END REGION NucleoList
76130

131+
// REGION primitive types validation
77132
if (!contractFields[dataKeys[i]]) {
78133
__errors__.push({
79134
contract: contractName,
80135
error: `${dataKeys[i]} is not in ${contractName} contract and can not be saved in store.`
81136
});
82137
}
83138

84-
if (contractFields[dataKeys[i]] && !contractFields[dataKeys[i]].serialize(currentDataKey)) {
139+
if (contractFields[dataKeys[i]] && contractFields[dataKeys[i]].serialize && !contractFields[dataKeys[i]].serialize(currentDataKey)) {
85140
__errors__.push({
86141
contract: contractName,
87142
field: contractFields[dataKeys[i]],
88143
error: `${dataKeys[i]} does not match its rules according to ${contractName} contract`
89144
});
90145
}
146+
// END REGION primitive types validation
91147
}
92148

93149
let operationStatus:''|'NOK'|'OK' = '';
@@ -98,8 +154,10 @@ export default function lawyer(contract: NucleoObjectType, data: any, saveMethod
98154
}
99155

100156
return (store:any, listeners:Array<Function>) => {
101-
saveMethodReflection(store, contractName)[saveMethod](data);
102-
executeListeners(contractName, listeners);
157+
if (!__errors__.length) {
158+
executeListeners(contractName, listeners);
159+
saveMethodReflection(store, contractName)[saveMethod](data);
160+
}
103161

104162
return {
105163
status: operationStatus,

src/nucleoTypes/primitive.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
// TODO: choose a better name for serialize, it doesn't fit perfectly
22
import { NucleoPrimitiveType } from './../_types/NucleoPrimitiveType';
33

4+
// class NucleoCustomPrimitive {
5+
// Type: string;
6+
// userFormatValidation: Function|void;
7+
// nativeType: string;
8+
//
9+
// serialize(value:string|number|boolean):boolean {
10+
// if (typeof value !== this.nativeType || this.formatValidation()) {
11+
// return false;
12+
// }
13+
// return true;
14+
// }
15+
//
16+
// formatValidation():boolean {
17+
// if (this.userFormatValidation) {
18+
// return this.userFormatValidation();
19+
// }
20+
// return false;
21+
// }
22+
// }
23+
424
export const NucleoString: NucleoPrimitiveType = {
525
Type: 'NucleoString',
626
serialize: (value: string):boolean => {
@@ -34,3 +54,30 @@ export const NucleoBoolean: NucleoPrimitiveType = {
3454
}
3555
};
3656

57+
// export class NucleoString extends NucleoCustomPrimitive {
58+
// constructor(userFormatValidation:Function|void) {
59+
// super();
60+
// this.Type = 'NucleoString';
61+
// this.nativeType = 'string';
62+
// this.userFormatValidation = userFormatValidation;
63+
// }
64+
// }
65+
//
66+
// export class NucleoNumber extends NucleoCustomPrimitive {
67+
// constructor(userFormatValidation:Function|void) {
68+
// super();
69+
// this.Type = 'NucleoNumber';
70+
// this.nativeType = 'number';
71+
// this.userFormatValidation = userFormatValidation;
72+
// }
73+
// }
74+
//
75+
// export class NucleoBoolean extends NucleoCustomPrimitive {
76+
// constructor(userFormatValidation:Function|void) {
77+
// super();
78+
// this.Type = 'NucleoBoolean';
79+
// this.nativeType = 'boolean';
80+
// this.userFormatValidation = userFormatValidation;
81+
// }
82+
// }
83+

src/save.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@ export default function save({
1616
return (contractName: string) => {
1717
if (!contracts[contractName]) {
1818
throw Error(
19-
`The provided contract named as "${contractName}" could not be found in store contracts`
19+
`Fatal error: The provided contract named as "${contractName}" could not be found in store contracts`
20+
);
21+
}
22+
23+
if (saveMethod === 'update' && !store[contractName]) {
24+
throw Error(
25+
`Fatal error: you can not update an item in store if it is not created yet.
26+
First use dispatch to save it and then you can perform updates at ${contractName} contract.`
2027
);
2128
}
2229
return (data: any) => {
23-
return lawyer({ name: contractName, fields: contracts[contractName]}, data, saveMethod)(store, listeners);
30+
return lawyer({
31+
contract: { name: contractName, fields: contracts[contractName]},
32+
data,
33+
saveMethod,
34+
__errors__: []
35+
})(store, listeners);
2436
};
2537
}
2638
};

0 commit comments

Comments
 (0)