functional-models 2.0.10 → 2.0.11
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/README.md +242 -41
- package/index.js +5 -1
- package/index.js.map +1 -1
- package/package.json +3 -2
- package/validation.d.ts +1 -1
package/README.md
CHANGED
|
@@ -10,65 +10,266 @@ This library empowers the creation of pure JavaScript function based models that
|
|
|
10
10
|
|
|
11
11
|
This library is fully supportive of both Typescript and Javascript. In fact, the typescript empowers some really sweet dynamic type checking, and autocomplete! It handles validation as well.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Functional Models was born out of the enjoyment and power of working with Django models.
|
|
14
14
|
|
|
15
|
+
# Primary Features
|
|
16
|
+
- Define models that have properties, methods, and methods on the model itself.
|
|
17
|
+
- Create instances of models
|
|
18
|
+
- Validate model instances
|
|
19
|
+
- ORM ready via the functional-models-orm package, with DynamoDb, Mongo, in-memory datastores supported.
|
|
20
|
+
- Many common properties out of the box.
|
|
21
|
+
- Supports foreign keys, 1 to 1 as well as 1 to many (via an Array).
|
|
22
|
+
- Supports custom primary key name. (id is used by default)
|
|
15
23
|
|
|
24
|
+
## Simple JavaScript Example Usage
|
|
16
25
|
|
|
17
26
|
const {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
BaseModel: Model,
|
|
28
|
+
DateProperty,
|
|
29
|
+
NumberProperty,
|
|
30
|
+
TextProperty,
|
|
31
|
+
} = require('functional-models')
|
|
32
|
+
|
|
33
|
+
// Create your model. Standard is to use a plural name.
|
|
34
|
+
const Trucks = Model('Trucks', {
|
|
35
|
+
properties: {
|
|
36
|
+
// id: UniqueId(), # this property is provided for free!
|
|
37
|
+
make: TextProperty({ maxLength: 20, minLength: 3, required: true}),
|
|
38
|
+
model: TextProperty({ maxLength: 20, minLength: 3, required: true}),
|
|
39
|
+
color: TextProperty({
|
|
40
|
+
maxLength: 10,
|
|
41
|
+
minLength: 3,
|
|
42
|
+
choices: ['red', 'green', 'blue', 'black', 'white'],
|
|
43
|
+
}),
|
|
44
|
+
year: NumberProperty({ maxValue: 2500, minValue: 1900}),
|
|
45
|
+
lastModified: DateProperty({ autoNow: true}),
|
|
46
|
+
}
|
|
38
47
|
})
|
|
39
48
|
|
|
49
|
+
// Create an instance of the model.
|
|
50
|
+
const myTruck = Trucks.create({ make: 'Ford', model: 'F-150', color: 'white', year: 2013})
|
|
40
51
|
|
|
41
|
-
const myTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 2013})
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
console.log(await myTruck.
|
|
45
|
-
console.log(
|
|
46
|
-
console.log(
|
|
47
|
-
console.log(
|
|
53
|
+
// Get the properties of the model instance.
|
|
54
|
+
console.log(await myTruck.get.id()) // a random uuid NOTE: this is a promise by default
|
|
55
|
+
console.log(myTruck.get.make()) // 'Ford'
|
|
56
|
+
console.log(myTruck.get.model()) // 'F-150'
|
|
57
|
+
console.log(myTruck.get.color()) // 'white'
|
|
58
|
+
console.log(myTruck.get.year()) // 2013
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
// Get a raw javascript object representation of the model.
|
|
61
|
+
const obj = await myTruck.toObj()
|
|
62
|
+
console.log(obj)
|
|
51
63
|
/*
|
|
52
64
|
{
|
|
53
65
|
"id": "a-random-uuid",
|
|
54
66
|
"make": "Ford",
|
|
55
67
|
"model": "F-150",
|
|
56
|
-
"color": "
|
|
68
|
+
"color": "white",
|
|
57
69
|
"year": 2013
|
|
58
70
|
}
|
|
59
71
|
*/
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.log(await
|
|
64
|
-
console.log(
|
|
65
|
-
console.log(
|
|
66
|
-
console.log(
|
|
73
|
+
// Create a copy of the model from the raw javascript object.
|
|
74
|
+
const sameTruck = Truck.create(obj)
|
|
75
|
+
console.log(await myTruck.get.id()) // same as above.
|
|
76
|
+
console.log(myTruck.get.make()) // 'Ford'
|
|
77
|
+
console.log(myTruck.get.model()) // 'F-150'
|
|
78
|
+
console.log(myTruck.get.color()) // 'white'
|
|
79
|
+
console.log(myTruck.get.year()) // 2013
|
|
67
80
|
|
|
68
|
-
//
|
|
69
|
-
const errors = await sameTruck.
|
|
81
|
+
// Validate the model. An empty object, means no errors.
|
|
82
|
+
const errors = await sameTruck.validate()
|
|
83
|
+
console.log(errors) // {}
|
|
70
84
|
|
|
71
|
-
const newTruck = Truck({ make: 'Ford', model: 'F-150', color: '
|
|
72
|
-
const errors2 = await newTruck.
|
|
85
|
+
const newTruck = Truck({ make: 'Ford', model: 'F-150', color: 'white', year: 20130})
|
|
86
|
+
const errors2 = await newTruck.validate()
|
|
73
87
|
console.log(errors2)
|
|
74
|
-
|
|
88
|
+
|
|
89
|
+
// Key is the property's name, and an array of validation errors for that property.
|
|
90
|
+
// {"year": ['Value is too long']}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## Simple TypeScript Example Usage
|
|
94
|
+
While functional-mode3ls works very well and easy without TypeScript, using typescript empowers
|
|
95
|
+
modern code completion engines to show the properties/methods on models and model instances.
|
|
96
|
+
Libraries built ontop of functional-models is encouraged to use TypeScript, while applications,
|
|
97
|
+
may or may not be as useful, given the overhead of typing. NOTE: Behind the covers functional-models
|
|
98
|
+
typing, is extremely strict, and verbose, which can make it somewhat difficult to work with, but
|
|
99
|
+
it provides the backbone of expressive and clear typing.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
import {
|
|
103
|
+
BaseModel as Model,
|
|
104
|
+
DateProperty,
|
|
105
|
+
NumberProperty,
|
|
106
|
+
TextProperty,
|
|
107
|
+
} from 'functional-models'
|
|
108
|
+
|
|
109
|
+
// Create your model's type
|
|
110
|
+
type TruckType = {
|
|
111
|
+
/*
|
|
112
|
+
* id: string # this property is provided for free. No need to put. If using a custom
|
|
113
|
+
* primary key, the property needs to be explicit.
|
|
114
|
+
*/
|
|
115
|
+
make: string,
|
|
116
|
+
model: string,
|
|
117
|
+
color: string,
|
|
118
|
+
year?: number, // NOTE: Clearly indicates a property is optional.
|
|
119
|
+
lastModified: Date,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Create your model. Standard is to use a plural name.
|
|
123
|
+
const Trucks = Model<TruckType>('Trucks', {
|
|
124
|
+
properties: {
|
|
125
|
+
// id: UniqueId(), # this property is provided for free!
|
|
126
|
+
make: TextProperty({ maxLength: 20, minLength: 3, required: true}),
|
|
127
|
+
model: TextProperty({ maxLength: 20, minLength: 3, required: true}),
|
|
128
|
+
color: TextProperty({
|
|
129
|
+
maxLength: 10,
|
|
130
|
+
minLength: 3,
|
|
131
|
+
choices: ['red', 'green', 'blue', 'black', 'white'],
|
|
132
|
+
}),
|
|
133
|
+
year: NumberProperty({ maxValue: 2500, minValue: 1900}),
|
|
134
|
+
lastModified: DateProperty({ autoNow: true}),
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// Create an instance of the model.
|
|
139
|
+
const myTruck = Trucks.create({ make: 'Ford', model: 'F-150', color: 'white', year: 2013})
|
|
140
|
+
|
|
141
|
+
// Will cause a typescript error because "make" is a string, not a number.
|
|
142
|
+
const myTruck2 = Trucks.create({ make: 5, model: 'F-150', color: 'white', year: 2013})
|
|
143
|
+
|
|
144
|
+
// Will NOT cause a typescript error because year is optional.
|
|
145
|
+
const myTruck = Trucks.create({ make: 'Ford', model: 'F-150', color: 'white' })
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
// Will cause a typescript error because model is not implemented, even though its required.
|
|
149
|
+
const Trucks2 = Model<TruckType>('Trucks2', {
|
|
150
|
+
properties: {
|
|
151
|
+
make: TextProperty({ maxLength: 20, minLength: 3, required: true}),
|
|
152
|
+
color: TextProperty({
|
|
153
|
+
maxLength: 10,
|
|
154
|
+
minLength: 3,
|
|
155
|
+
choices: ['red', 'green', 'blue', 'black', 'white'],
|
|
156
|
+
}),
|
|
157
|
+
year: NumberProperty({ maxValue: 2500, minValue: 1900}),
|
|
158
|
+
lastModified: DateProperty({ autoNow: true}),
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
## Validation
|
|
164
|
+
Validation is baked into the functional-models framwork. Both individual properties as well as an entire model instance can be covered by validators. The following are the interfaces for a validator. Validation overall is a combination of property validator components as well as model validator components. These components combine together to create a complete validation picture of a model.
|
|
165
|
+
|
|
166
|
+
Here is an example of a model instance failing validation.
|
|
167
|
+
|
|
168
|
+
// Call .validate() on the model, and await its result.
|
|
169
|
+
const errors = await myModelInstance.validate()
|
|
170
|
+
|
|
171
|
+
console.log(errors)
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
* {
|
|
175
|
+
* "overall": [
|
|
176
|
+
* "This is a custom model validator that failed for the entire model",
|
|
177
|
+
* "Here is a second model failing message",
|
|
178
|
+
* ],
|
|
179
|
+
* "aDateProperty": [
|
|
180
|
+
* "A value is required",
|
|
181
|
+
* "Value is not a date",
|
|
182
|
+
* ],
|
|
183
|
+
* "anArrayProperty": [
|
|
184
|
+
* "BadChoice is not a valid choice",
|
|
185
|
+
* ],
|
|
186
|
+
* // the 'otherProperty' did not fail, and therefore is not shown here.
|
|
187
|
+
* }
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
### Property validators
|
|
192
|
+
A property validator validates the value of a property. The inputs are the value, a model instance, the JavaScript object representation of the model, and optional configurations that are passed into the validator. The return can either be a string error or undefined if there are no errors. This function can be asynchronous, such as doing database lookups.
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* An example validator function that only allows the value of 5.
|
|
196
|
+
* @constructor
|
|
197
|
+
* @param {string} value - The value to be tested.
|
|
198
|
+
* @param {string} instance - A model instance, which can be used for cross referencing.
|
|
199
|
+
* @param {string} instanceData - The JavaScript object representation of the model.
|
|
200
|
+
* @param {string} configurations - An optional configuration object passed in as part of validating.
|
|
201
|
+
* @return {string|undefined} - If error, returns a string, otherwise returns undefined.
|
|
202
|
+
*/
|
|
203
|
+
const valueIsFiveValidator = (
|
|
204
|
+
value, // any kind of value.
|
|
205
|
+
instance, // A ModelInstance,
|
|
206
|
+
instanceData: JavaScript object representation,
|
|
207
|
+
configurations: {}
|
|
208
|
+
) => {
|
|
209
|
+
return value === 5
|
|
210
|
+
? undefined
|
|
211
|
+
: 'Value is not 5'
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* An example async validator function that checks a database using an object passed into the configurations.
|
|
216
|
+
* @constructor
|
|
217
|
+
* @param {string} value - The value to be tested.
|
|
218
|
+
* @param {string} instance - A model instance, which can be used for cross referencing.
|
|
219
|
+
* @param {string} instanceData - The JavaScript object representation of the model.
|
|
220
|
+
* @param {string} configurations - An optional configuration object passed in as part of validating.
|
|
221
|
+
* @return {Promise<string|undefined>} - Returns a promise, If error, returns a string, otherwise returns undefined.
|
|
222
|
+
*/
|
|
223
|
+
const checkDatabaseError = async (
|
|
224
|
+
value, // any kind of value.
|
|
225
|
+
instance, // A ModelInstance,
|
|
226
|
+
instanceData: JavaScript object representation,
|
|
227
|
+
configurations: {}
|
|
228
|
+
) => {
|
|
229
|
+
const result = await configurations.someDatabaseObj.check(value)
|
|
230
|
+
if (result) {
|
|
231
|
+
return 'Some sort of database error'
|
|
232
|
+
}
|
|
233
|
+
return undefined
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
### Model Validators
|
|
237
|
+
Model validators allows one to check values across a model, ensuring that multiple values work together. The inputs are the model instance, the JavaScript object representation, and optional configurations. The return can either be a string error or undefined if there are no errors. This function can be asynchronous, such as doing database lookups.
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* An example model validator that checks to see if two properties have the same value.
|
|
241
|
+
* @constructor
|
|
242
|
+
* @param {string} instance - A model instance, used for cross referencing.
|
|
243
|
+
* @param {string} instanceData - The JavaScript object representation of the model.
|
|
244
|
+
* @param {string} configurations - An optional configuration object passed in as part of validating.
|
|
245
|
+
* @return {string|undefined} - If error, returns a string, otherwise returns undefined.
|
|
246
|
+
*/
|
|
247
|
+
const checkForDuplicateValues = (
|
|
248
|
+
instance, // A ModelInstance,
|
|
249
|
+
instanceData: JavaScript object representation,
|
|
250
|
+
configurations: {}
|
|
251
|
+
) => {
|
|
252
|
+
if(instanceData.firstProperty === instanceData.secondProperty) {
|
|
253
|
+
return 'Both properties must have different values'
|
|
254
|
+
}
|
|
255
|
+
return undefined
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
## Properties and Custom Properties
|
|
260
|
+
There are numerous properties that are supported out of the box that cover most data modeling needs. It is also very easy to create custom properties that encapsulate unique choices
|
|
261
|
+
validation requirements, etc.
|
|
262
|
+
|
|
263
|
+
### List of Properties Out-Of-The-Box
|
|
264
|
+
- UniqueId // A UUID property
|
|
265
|
+
- DateProperty // A property for dates. Includes the ability to "autoNow".
|
|
266
|
+
- ArrayProperty // An array property which can be used to limit types within it.
|
|
267
|
+
- ModelReferenceProperty // A property that references another property. (Think Foreign Key)
|
|
268
|
+
- AdvancedModelReferenceProperty // A more fuller/advanced property for referencing other properties.
|
|
269
|
+
- IntegerProperty // A property for integers.
|
|
270
|
+
- TextProperty // A text or string property.
|
|
271
|
+
- ConstantValueProperty // A property that contains a single, unchanging, static value.
|
|
272
|
+
- NumberProperty // A property for float/number types.
|
|
273
|
+
- ObjectProperty // A property that has a JavaScript object. (Not a foreign key references)
|
|
274
|
+
- EmailProperty // An email property.
|
|
275
|
+
- BooleanProperty // A true or false value property.
|
package/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uDAAwC;AAW/B,8BAAS;AAVlB,iDAAkC;AAUd,wBAAM;AAT1B,yDAA0C;AASd,gCAAU;AARtC,+DAAgD;AAQR,sCAAa;AAPrD,+CAAgC;AAOuB,sBAAK;AAN5D,yDAA0C;AAMoB,gCAAU;AAJxE,2CAAwB;AACxB,+CAA4B;AAC5B,4CAAyB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functional-models",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11",
|
|
4
4
|
"description": "A library for creating JavaScript function based models.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"test:coverage": "nyc npm run test",
|
|
10
10
|
"feature-tests": "./node_modules/.bin/cucumber-js -p default",
|
|
11
11
|
"coverage": "nyc --all --reporter=lcov npm test",
|
|
12
|
-
"
|
|
12
|
+
"build": "tsc -p ./tsconfig.build.json && cp package.json ./dist && cp README.md ./dist",
|
|
13
|
+
"dist": "npm run build && cd dist && npm publish"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
package/validation.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ declare const isBoolean: ValuePropertyValidatorComponent<boolean>;
|
|
|
13
13
|
declare const isString: ValuePropertyValidatorComponent<string>;
|
|
14
14
|
declare const isArray: ValuePropertyValidatorComponent<readonly FunctionalValue[]>;
|
|
15
15
|
declare const arrayType: <T extends FunctionalValue>(type: string) => ValuePropertyValidatorComponent<readonly T[]>;
|
|
16
|
-
declare const meetsRegex: <T extends FunctionalValue>(regex: string | RegExp, flags?: string
|
|
16
|
+
declare const meetsRegex: <T extends FunctionalValue>(regex: string | RegExp, flags?: string, errorMessage?: string) => ValuePropertyValidatorComponent<T>;
|
|
17
17
|
declare const choices: <T extends FunctionalValue>(choiceArray: readonly T[]) => ValuePropertyValidatorComponent<T>;
|
|
18
18
|
declare const isDate: ValuePropertyValidatorComponent<Date>;
|
|
19
19
|
declare const isRequired: ValuePropertyValidatorComponent<any>;
|