functional-models 2.0.7 → 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 CHANGED
@@ -4,66 +4,272 @@
4
4
  ![Feature Tests](https://github.com/monolithst/functional-models/actions/workflows/feature.yml/badge.svg?branch=master)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/monolithst/functional-models/badge.svg?branch=master)](https://coveralls.io/github/monolithst/functional-models?branch=master)
6
6
 
7
- Love functional javascript but still like composing objects/models? This is the library for you.
8
- This library empowers the creation of pure JavaScript function based models that can be used on a client, a web frontend, and/or a backend all the same time. Use this library to create readable, read-only, models.
7
+ Love functional javascript/typescript but still like composing objects/models? This is the library for you.
9
8
 
10
- ## Example Usage
9
+ This library empowers the creation of pure JavaScript function based models that can be used on a client, a web frontend, and/or a backend all the same time. Use this library to create models that can be reused everywhere.
10
+
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
+
13
+ Functional Models was born out of the enjoyment and power of working with Django models.
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)
23
+
24
+ ## Simple JavaScript Example Usage
11
25
 
12
26
  const {
13
- field,
14
- constantValueField,
15
- textField,
16
- dateField,
17
- integerField,
18
- uniqueId,
19
- createModel,
20
- validation,
21
- }= require('functional-models')
22
-
23
- const Truck = createModel({
24
- type: constantValueField('truck'),
25
- id: uniqueId({required: true}),
26
- make: textField({ maxLength: 20, minLength: 3, required: true}),
27
- model: textField({ maxLength: 20, minLength: 3, required: true}),
28
- color: textField({ maxLength: 10, minLength: 3, validators: [
29
- validation.meetsRegex(/Red/),
30
- ]}),
31
- year: integerField({ maxValue: 2500, minValue: 1900}),
32
- lastModified: dateField({ autoNow: true}),
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
+ }
33
47
  })
34
48
 
49
+ // Create an instance of the model.
50
+ const myTruck = Trucks.create({ make: 'Ford', model: 'F-150', color: 'white', year: 2013})
35
51
 
36
- const myTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 2013})
37
52
 
38
- console.log(await myTruck.getId()) // a random uuid
39
- console.log(await myTruck.getMake()) // 'Ford'
40
- console.log(await myTruck.getModel()) // 'F-150'
41
- console.log(await myTruck.getColor()) // 'White'
42
- console.log(await myTruck.getYear()) // 2013
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
43
59
 
44
- const asJson = await myTruck.functions.toJson()
45
- console.log(asJson)
60
+ // Get a raw javascript object representation of the model.
61
+ const obj = await myTruck.toObj()
62
+ console.log(obj)
46
63
  /*
47
64
  {
48
65
  "id": "a-random-uuid",
49
66
  "make": "Ford",
50
67
  "model": "F-150",
51
- "color": "White",
68
+ "color": "white",
52
69
  "year": 2013
53
70
  }
54
71
  */
55
72
 
56
- const sameTruck = Truck(asJson)
57
- console.log(await sameTruck.getId()) // same uuid as above
58
- console.log(await sameTruck.getMake()) // 'Ford'
59
- console.log(await sameTruck.getModel()) // 'F-150'
60
- console.log(await sameTruck.getColor()) // 'White'
61
- console.log(await sameTruck.getYear()) // 2013
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
62
80
 
63
- // Validation
64
- const errors = await sameTruck.functions.validate.model() // {}
81
+ // Validate the model. An empty object, means no errors.
82
+ const errors = await sameTruck.validate()
83
+ console.log(errors) // {}
65
84
 
66
- const newTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 20130})
67
- const errors2 = await newTruck.functions.validate.model()
85
+ const newTruck = Truck({ make: 'Ford', model: 'F-150', color: 'white', year: 20130})
86
+ const errors2 = await newTruck.validate()
68
87
  console.log(errors2)
69
- // {"year": 'Value is too long'}
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.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
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":";;;;;;;;;;;;;;;;;;;;;;;;;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"}
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.7",
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
- "dist": "tsc -p ./tsconfig.build.json && cp package.json ./dist && cp README.md ./dist && cd dist && npm publish"
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",
@@ -58,6 +59,7 @@
58
59
  "@types/proxyquire": "^1.3.28",
59
60
  "@types/sinon": "^10.0.6",
60
61
  "@typescript-eslint/eslint-plugin": "^5.6.0",
62
+ "@typescript-eslint/parser": "^5.3.0",
61
63
  "babel-eslint": "^10.1.0",
62
64
  "chai": "^4.3.0",
63
65
  "chai-as-promised": "^7.1.1",
@@ -73,7 +75,6 @@
73
75
  "typescript": "^4.4.4"
74
76
  },
75
77
  "dependencies": {
76
- "@typescript-eslint/parser": "^5.3.0",
77
78
  "async-lock": "^1.3.0",
78
79
  "get-random-values": "^1.2.2",
79
80
  "lodash": "^4.17.21"
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 | undefined, errorMessage?: string) => ValuePropertyValidatorComponent<T>;
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>;