mythix-orm 1.7.2 → 1.8.0

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.
@@ -83,6 +83,10 @@ declare class ConnectionBase extends EventEmitter {
83
83
  public toQueryEngine(queryEngineLike: any): QueryEngine | undefined;
84
84
  public registerModel<T = ModelClass>(Model: T): T;
85
85
  public registerModels(models: Models | Array<ModelClass>): Models | undefined;
86
+ public getContextValue(key: any, defaultValue?: any): any;
87
+ public setContextValue(key: any, value: any): any;
88
+ public buildConnectionContext(connection?: ConnectionBase): Map<any, any>;
89
+ public createContext(callback: Function, connection?: ConnectionBase, thisArg?: any): Promise<any>;
86
90
  public findModelField(finder: IterateFieldsCallback): Array<Field>;
87
91
  public parseQualifiedName(fullyQualifiedName: string): FullyQualifiedFieldDefinition;
88
92
  public getModels(): Models;
@@ -227,7 +227,7 @@ class ConnectionBase extends EventEmitter {
227
227
  return;
228
228
 
229
229
  if (!QueryEngine.isQuery(queryEngine)) {
230
- if (Object.prototype.hasOwnProperty.call(queryEngine, 'where'))
230
+ if ('where' in queryEngine)
231
231
  queryEngine = queryEngine.where(this);
232
232
  else
233
233
  queryEngine = undefined;
@@ -260,6 +260,84 @@ class ConnectionBase extends EventEmitter {
260
260
  return this._models;
261
261
  }
262
262
 
263
+ /// Get a value from the `AsyncLocalStorage` context.
264
+ ///
265
+ /// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
266
+ /// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
267
+ /// pass a transaction connection down through the call stack. However, it can
268
+ /// also be used for any "context" level values the user wishes to store.
269
+ ///
270
+ /// Note:
271
+ /// An `AsyncLocalStorage` context might not exist when this call is made,
272
+ /// in which case this method will return `undefined`, or the `defaultValue`
273
+ /// if any was provided.
274
+ ///
275
+ /// Return: any
276
+ ///
277
+ /// Arguments:
278
+ /// key: any
279
+ /// The key for the value you wish to fetch.
280
+ /// The underlying "context" provided by `AsyncLocalStorage`
281
+ /// is a `Map`, so the "key" can be any value.
282
+ /// defaultValue?: any
283
+ /// The default value to return if there is no current `AsyncLocalStorage`
284
+ /// context, or if the requested key is not found.
285
+ getContextValue(key, defaultValue) {
286
+ return Utils.getContextValue(key, defaultValue);
287
+ }
288
+
289
+ /// Set a value onto the `AsyncLocalStorage` context.
290
+ ///
291
+ /// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
292
+ /// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
293
+ /// pass a transaction connection down through the call stack. However, it can
294
+ /// also be used for any "context" level values the user wishes to store.
295
+ ///
296
+ /// Note:
297
+ /// An `AsyncLocalStorage` context might not exist when this call is made,
298
+ /// in which case this method will do nothing, and silently return.
299
+ ///
300
+ /// Return: any
301
+ ///
302
+ /// Arguments:
303
+ /// key: any
304
+ /// The key for the value you wish to set.
305
+ /// The underlying "context" provided by `AsyncLocalStorage`
306
+ /// is a `Map`, so the "key" can be any value.
307
+ /// value: any
308
+ /// The value to set to the specified key.
309
+ setContextValue(key, value) {
310
+ return Utils.setContextValue(key, value);
311
+ }
312
+
313
+ buildConnectionContext(_connection) {
314
+ let connection = _connection || this;
315
+ let models = connection._models;
316
+ let modelNames = Object.keys(models);
317
+ let newContext = new Map();
318
+
319
+ newContext.set('connection', connection);
320
+
321
+ for (let i = 0, il = modelNames.length; i < il; i++) {
322
+ let modelName = modelNames[i];
323
+ let Model = models[modelName];
324
+ let currentModelScope = Model.getModelContext();
325
+
326
+ newContext.set(modelName, { ...currentModelScope, connection });
327
+ }
328
+
329
+ return newContext;
330
+ }
331
+
332
+ async createContext(callback, _connection, thisArg) {
333
+ let context = this.buildConnectionContext(_connection);
334
+ let connection = _connection || this;
335
+
336
+ return await Utils.runInContext(context, async () => {
337
+ return await callback.call(thisArg, connection);
338
+ });
339
+ }
340
+
263
341
  findModelField(finder) {
264
342
  let modelMap = this.getModels();
265
343
  let modelNames = Object.keys(modelMap);
@@ -1248,7 +1326,7 @@ class ConnectionBase extends EventEmitter {
1248
1326
 
1249
1327
  // eslint-disable-next-line no-unused-vars
1250
1328
  async query(sql, options) {
1251
- throw new Error(`${this.constructor.name}::transaction: This operation is not supported for this connection type.`);
1329
+ throw new Error(`${this.constructor.name}::query: This operation is not supported for this connection type.`);
1252
1330
  }
1253
1331
 
1254
1332
  // eslint-disable-next-line no-unused-vars
package/lib/field.js CHANGED
@@ -67,6 +67,7 @@
67
67
  /// Interface:
68
68
  /// interface DefaultValueContext {
69
69
  /// _initial: boolean; // If `true`, then this is the initial set of the field's value.
70
+ /// _static: boolean; // If `true`, then this will be used in a "static" context, such as the `DEFAULT` of the DB.
70
71
  /// connection: Connection; // The connection from the model.
71
72
  /// data: object | undefined; // All attributes provided to the model upon instantiation.
72
73
  /// field: Field; // The field descriptor where this `defaultValue` is defined.
@@ -113,8 +114,15 @@
113
114
  /// This has no effect client-side in JavaScript. Even if `allowNull`
114
115
  /// is `false`, the field property on your model may still have a
115
116
  /// `null` value in JavaScript.
116
- /// index: boolean = false
117
+ /// index: Array<boolean | string> | boolean = false
117
118
  /// If `true`, then this field will be indexed in the database.
119
+ /// If an array is provided, then it must be an array containing
120
+ /// other field names to combine with this field to create a combined
121
+ /// index. For example: `index: [ true, 'firstName', 'lastName', [ 'firstName', 'lastName' ] ]`
122
+ /// would create four indexes: `true` means index this field, `firstName` would create
123
+ /// the combination index `this_field_first_name`, `lastName` would create the
124
+ /// combination index `this_field_last_name`, and `[ 'firstName', 'lastName ]` would
125
+ /// create the combination index `this_field_first_name_last_name`.
118
126
  /// unique: boolean = false
119
127
  /// If `true`, then add a unique constraint to this field in
120
128
  /// the underlying database.
package/lib/model.d.ts CHANGED
@@ -15,7 +15,7 @@ export declare interface ModelOptions extends GenericObject {
15
15
  export declare type ModelClass = typeof Model;
16
16
 
17
17
  export declare interface Models {
18
- [ key: string ]: ModelClass;
18
+ [key: string]: ModelClass;
19
19
  }
20
20
 
21
21
  export declare interface HookContext {
@@ -25,11 +25,11 @@ export declare interface HookContext {
25
25
  }
26
26
 
27
27
  export declare interface DirtyChanges {
28
- [ key: string ]: { previous: any; current: any };
28
+ [key: string]: { previous: any; current: any };
29
29
  }
30
30
 
31
- export declare type LooseFields = Array<Field | FieldDefinition> | { [ key: string ]: Field | FieldDefinition } | Map<string, Field | FieldDefinition> | Set<Field | FieldDefinition>;
32
- export declare type Fields = Array<Field> | { [ key: string ]: Field } | Map<string, Field> | Set<Field>;
31
+ export declare type LooseFields = Array<Field | FieldDefinition> | { [key: string]: Field | FieldDefinition } | Map<string, Field | FieldDefinition> | Set<Field | FieldDefinition>;
32
+ export declare type Fields = Array<Field> | { [key: string]: Field } | Map<string, Field> | Set<Field>;
33
33
 
34
34
  export declare interface IterateFieldsContext {
35
35
  field: Field;
@@ -54,6 +54,18 @@ export declare class Model {
54
54
  public static isModel(value: any): boolean;
55
55
  public static toString(showFields: boolean): string;
56
56
 
57
+ static getContextValue(key: any, defaultValue?: any): any;
58
+ getContextValue(key: any, defaultValue?: any): any;
59
+
60
+ static setContextValue(key: any, value: any): void;
61
+ setContextValue(key: any, value: any): void;
62
+
63
+ public static getModelContext(): GenericObject;
64
+ getModelContext(): GenericObject;
65
+
66
+ static updateModelContext(value: GenericObject): void;
67
+ updateModelContext(value: GenericObject): void;
68
+
57
69
  public static _getConnection(connection?: ConnectionBase): ConnectionBase;
58
70
  public _getConnection(connection?: ConnectionBase): ConnectionBase;
59
71
 
package/lib/model.js CHANGED
@@ -5,6 +5,7 @@ const Inflection = require('inflection');
5
5
  const Type = require('./types/type');
6
6
  const DefaultHelpers = require('./types/helpers/default-helpers');
7
7
  const Field = require('./field');
8
+ const AsyncStore = require('./utils/async-store');
8
9
  const ModelUtils = require('./utils/model-utils');
9
10
 
10
11
  /// Used as a unique key for cache.
@@ -45,24 +46,21 @@ function bindStaticWhereToModelClass(ModelClass) {
45
46
  set: function() {},
46
47
  get: function() {
47
48
  const self = this;
49
+ let connection = self._getConnection();
50
+ if (connection)
51
+ return self.getQueryEngine(connection);
48
52
 
49
53
  function unboundWhere(_connection, _propName) {
50
54
  let connection = _connection;
51
-
52
- if (arguments.length === 0) {
53
- connection = self._getConnection();
54
- if (!connection)
55
- throw new Error(`${self.getModelName()}::where: Model has no bound connection. You need to provide a connection by calling "${self.getModelName()}.where(connection)" instead.`);
56
-
57
- return self.getQueryEngine(connection);
58
- }
59
-
60
55
  if (!connection) {
61
56
  connection = self._getConnection();
62
57
  if (!connection)
63
58
  throw new Error(`${self.getModelName()}::where: Model has no bound connection. You need to provide a connection by calling "${self.getModelName()}.where(connection)" instead.`);
64
59
  }
65
60
 
61
+ if (arguments.length === 0)
62
+ return self.getQueryEngine(connection);
63
+
66
64
  let propName = _propName;
67
65
 
68
66
  if (connection.constructor && !connection.constructor._isMythixConnection) {
@@ -94,7 +92,12 @@ function bindStaticWhereToModelClass(ModelClass) {
94
92
  // This will throw an exception
95
93
  // if no connection is available
96
94
  let queryEngine = self.getQueryEngine(connection);
97
- return queryEngine[propName];
95
+ let prop = queryEngine[propName];
96
+
97
+ if (typeof prop === 'function' && typeof prop.bind === 'function')
98
+ prop = prop.bind(queryEngine);
99
+
100
+ return prop;
98
101
  },
99
102
  });
100
103
  },
@@ -223,6 +226,146 @@ class Model {
223
226
  return this.toString((depth !== 0));
224
227
  }
225
228
 
229
+ /// Get a value from the `AsyncLocalStorage` context.
230
+ ///
231
+ /// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
232
+ /// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
233
+ /// pass a transaction connection down through the call stack. However, it can
234
+ /// also be used for any "context" level values the user wishes to store.
235
+ ///
236
+ /// Note:
237
+ /// An `AsyncLocalStorage` context might not exist when this call is made,
238
+ /// in which case this method will return `undefined`, or the `defaultValue`
239
+ /// if any was provided.
240
+ ///
241
+ /// Return: any
242
+ ///
243
+ /// Arguments:
244
+ /// key: any
245
+ /// The key for the value you wish to fetch.
246
+ /// The underlying "context" provided by `AsyncLocalStorage`
247
+ /// is a `Map`, so the "key" can be any value.
248
+ /// defaultValue?: any
249
+ /// The default value to return if there is no current `AsyncLocalStorage`
250
+ /// context, or if the requested key is not found.
251
+ static getContextValue(key, defaultValue) {
252
+ return AsyncStore.getContextValue(key, defaultValue);
253
+ }
254
+
255
+ getContextValue(key, defaultValue) {
256
+ return this.constructor.getContextValue(key, defaultValue);
257
+ }
258
+
259
+ /// Set a value onto the `AsyncLocalStorage` context.
260
+ ///
261
+ /// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
262
+ /// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
263
+ /// pass a transaction connection down through the call stack. However, it can
264
+ /// also be used for any "context" level values the user wishes to store.
265
+ ///
266
+ /// Note:
267
+ /// An `AsyncLocalStorage` context might not exist when this call is made,
268
+ /// in which case this method will do nothing, and silently return.
269
+ ///
270
+ /// Return: any
271
+ ///
272
+ /// Arguments:
273
+ /// key: any
274
+ /// The key for the value you wish to set.
275
+ /// The underlying "context" provided by `AsyncLocalStorage`
276
+ /// is a `Map`, so the "key" can be any value.
277
+ /// value: any
278
+ /// The value to set to the specified key.
279
+ static setContextValue(key, value) {
280
+ return AsyncStore.setContextValue(key, value);
281
+ }
282
+
283
+ setContextValue(key, value) {
284
+ return this.constructor.setContextValue(key, value);
285
+ }
286
+
287
+ /// Get this Model class's "model context" from the `AsyncLocalStorage` context.
288
+ ///
289
+ /// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
290
+ /// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
291
+ /// pass a transaction connection down through the call stack. However, it can
292
+ /// also be used for any "context" level values the user wishes to store.
293
+ ///
294
+ /// When a <see>Connection.createContext</see> or <see>Connection.transaction</see>
295
+ /// call is made, a "model context" (that is just an Object instance) is created for each
296
+ /// model the connection is aware of, and the `connection` instance is added to this
297
+ /// context, allowing models to lookup the "context connection" when a call to
298
+ /// <see>Model._getConnection</see> is made. By default, these "model contexts"
299
+ /// that are stored inside the `AsyncLocalStorage` context only contain a "connection"
300
+ /// property. However, these model contexts were designed as Object instances so that
301
+ /// the user may also use them, or so they can be used for future features. Each model
302
+ /// also has its own "model context" so that if the application is using more than
303
+ /// one connection, then a different connection can be specified for each model and
304
+ /// stored in the same `AsyncLocalStorage` context.
305
+ ///
306
+ /// Note:
307
+ /// An `AsyncLocalStorage` context might not exist when this call is made,
308
+ /// in which case this method will simply return an empty object.
309
+ ///
310
+ /// Return: object
311
+ /// The model class's "model context" stored in `AsyncLocalStorage`.
312
+ /// This will be an empty object if no `AsyncLocalStorage` context is
313
+ /// available.
314
+ static getModelContext() {
315
+ return AsyncStore.getContextValue(this.getModelName(), {});
316
+ }
317
+
318
+ getModelContext() {
319
+ return this.constructor.getModelContext();
320
+ }
321
+
322
+ /// Update this Model class's "model context" from the `AsyncLocalStorage` context.
323
+ ///
324
+ /// [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
325
+ /// is used primarily for two purposes: 1) to provide a connection to models, and 2) to
326
+ /// pass a transaction connection down through the call stack. However, it can
327
+ /// also be used for any "context" level values the user wishes to store.
328
+ ///
329
+ /// When a <see>Connection.createContext</see> or <see>Connection.transaction</see>
330
+ /// call is made, a "model context" (that is just an Object instance) is created for each
331
+ /// model the connection is aware of, and the `connection` instance is added to this
332
+ /// context, allowing models to lookup the "context connection" when a call to
333
+ /// <see>Model._getConnection</see> is made. By default, these "model contexts"
334
+ /// that are stored inside the `AsyncLocalStorage` context only contain a "connection"
335
+ /// property. However, these model contexts were designed as Object instances so that
336
+ /// the user may also use them, or so they can be used for future features. Each model
337
+ /// also has its own "model context" so that if the application is using more than
338
+ /// one connection, then a different connection can be specified for each model and
339
+ /// stored in the same `AsyncLocalStorage` context.
340
+ ///
341
+ /// This method allows you to update this "model context" for the model. The `value`
342
+ /// argument **must** be an Object instance. You can specify any properties you want.
343
+ /// Just be cautious if you set a `connection` property, as you might overwrite any
344
+ /// connection currently set on the context. Any properties you set here will be available
345
+ /// in any child function calls (inside this context scope), and can be accessed via a
346
+ /// call to <see>Model.getModelContext</see>.
347
+ ///
348
+ /// Note:
349
+ /// An `AsyncLocalStorage` context might not exist when this call is made,
350
+ /// in which case this method will simply return an empty object.
351
+ ///
352
+ /// Return: undefined
353
+ ///
354
+ /// Arguments:
355
+ /// value: object
356
+ /// The new properties you wish to merge with the current "model context".
357
+ static updateModelContext(value) {
358
+ let context = AsyncStore.getContextValue(this.getModelName());
359
+ if (!context)
360
+ return;
361
+
362
+ Object.assign(context, value);
363
+ }
364
+
365
+ updateModelContext(value) {
366
+ return this.constructor.updateModelContext(value);
367
+ }
368
+
226
369
  /// Get the underlying connection bound to
227
370
  /// the model's class. Connection binding is
228
371
  /// optional, so this method can also be provided
@@ -233,6 +376,32 @@ class Model {
233
376
  /// or fallback to a "bound" connection (if one can
234
377
  /// be found).
235
378
  ///
379
+ /// This method will return a connection in the following
380
+ /// order: 1) The provided `connection` argument, if supplied,
381
+ /// 2) The `connection` property on the "model context" from
382
+ /// <see>Model.getModelContext</see> from `AsyncLocalStorage`,
383
+ /// if one is available, and 3) Finally the bound connection, if
384
+ /// available, which is found by searching the `static Model._mythixBoundConnection`
385
+ /// property of the class.
386
+ ///
387
+ /// Note:
388
+ /// This is a low-level "global fallback" method. Any connection for a model
389
+ /// will first be searched by calling <see>Model.getConnection</see>,
390
+ /// which will call this static method if no connection can be found.
391
+ /// The <see>Model.getConnection</see> will also search the model instance
392
+ /// for a `connection` property, which can be supplied to any model when
393
+ /// it is instantiated.
394
+ ///
395
+ /// Note:
396
+ // A provided `connection`, either via the model instance `model.connection`,
397
+ /// or via the provided `connection` argument to the <see>Model.getConnection</see>
398
+ /// method (and eventually this method) will take priority over any `connection`
399
+ /// provided by the "model context" from `AsyncLocalStorage`. This can be an important
400
+ /// detail when using <see>Connection.transaction</see> or <see>Connection.createContext</see>,
401
+ /// because if a connection is directly specified by the user, the transaction connection
402
+ /// **will not** take priority. So if you are using transactions or a connection context,
403
+ /// be careful how you directly specify connections.
404
+ ///
236
405
  /// Return: <see>Connection</see>
237
406
  ///
238
407
  /// Arguments:
@@ -243,7 +412,18 @@ class Model {
243
412
  /// then attempt to fallback to the bound connection,
244
413
  /// if any is available.
245
414
  static _getConnection(_connection) {
246
- return _connection || this._mythixBoundConnection;
415
+ if (_connection)
416
+ return _connection;
417
+
418
+ let { connection } = this.getModelContext();
419
+ if (connection)
420
+ return connection;
421
+
422
+ connection = AsyncStore.getContextValue('connection');
423
+ if (connection)
424
+ return connection;
425
+
426
+ return this._mythixBoundConnection;
247
427
  }
248
428
 
249
429
  /// See <see>Model.static _getConnection</see>
@@ -343,17 +523,19 @@ class Model {
343
523
 
344
524
  let connectionOptions = connection.getOptions();
345
525
  if (connectionOptions && connectionOptions.bindModels === false) {
346
- let modelName = ModelClass.getModelName();
526
+ // let modelName = ModelClass.getModelName();
347
527
 
348
- ModelClass = class BoundModel extends ModelClass {
349
- static getModelName() {
350
- return modelName;
351
- }
352
- };
528
+ // ModelClass = class BoundModel extends ModelClass {
529
+ // static getModelName() {
530
+ // return modelName;
531
+ // }
532
+ // };
353
533
 
354
- ModelClass.fields = ModelClass.mergeFields();
355
- ModelClass._sortedFields = null; // Clear cache
356
- } else {
534
+ // ModelClass.fields = ModelClass.mergeFields();
535
+ // ModelClass._sortedFields = null; // Clear cache
536
+
537
+ return ModelClass;
538
+ } else if (connectionOptions.forceConnectionBinding !== true) {
357
539
  if (Object.prototype.hasOwnProperty.call(ModelClass, '_mythixBoundConnection') && ModelClass._mythixBoundConnection)
358
540
  throw new Error(`${this.getModelName()}:bindConnection: Model is already bound to a connection. You can not bind a model to a connection more than once.`);
359
541
  }
@@ -1424,6 +1606,7 @@ class Model {
1424
1606
  /// Prefix a field name with `+` to specify ASCending order, i.e.
1425
1607
  /// `[ "+User:id" ]`. Prefix the field name with `-` to specify
1426
1608
  /// DESCending order, i.e. `[ "-User:id" ]`.
1609
+ // eslint-disable-next-line no-unused-vars
1427
1610
  static defaultOrder(options) {
1428
1611
  }
1429
1612
 
@@ -27,6 +27,9 @@ const AUTO_CALL_CALLED = Symbol.for('@__autoCallCalled');
27
27
  const AUTO_CALL = Symbol.for('@__autoCall');
28
28
 
29
29
  function shouldSkipProxy(prop) {
30
+ if (prop === 'bind' || prop === 'call' || prop === 'apply')
31
+ return true;
32
+
30
33
  if (prop === PROXY || prop === TARGET || prop === SELF || prop === AUTO_CALL_CALLER || prop === AUTO_CALL_CALLED || prop === AUTO_CALL)
31
34
  return true;
32
35
 
@@ -20,6 +20,10 @@ const MiscUtils = require('../../utils/misc-utils');
20
20
  /// it is used to parse provided date strings, and when serializing the
21
21
  /// field (for example via <see>Model.toJSON</see>).
22
22
  ///
23
+ /// Note:
24
+ /// A `BigInt` timestamp is used to store the date
25
+ /// in the underlying database.
26
+ ///
23
27
  /// Example:
24
28
  /// class Dates extends Model {
25
29
  /// static fields = {
@@ -20,6 +20,10 @@ const MiscUtils = require('../../utils/misc-utils');
20
20
  /// it is used to parse provided date strings, and when serializing the
21
21
  /// field (for example via <see>Model.toJSON</see>).
22
22
  ///
23
+ /// Note:
24
+ /// A `BigInt` timestamp is used to store the date
25
+ /// and time in the underlying database.
26
+ ///
23
27
  /// Example:
24
28
  /// class DatesWithTimes extends Model {
25
29
  /// static fields = {
@@ -27,7 +27,7 @@ const Type = require('../type');
27
27
  /// then the side-effect will be that this field may be serialized
28
28
  /// and stored more often than actually needed. This system was
29
29
  /// designed this way because it is likely better to get a false
30
- /// positive and save your data, then not check at all and lose data.
30
+ /// positive and save your data than to not check at all and lose data.
31
31
  /// If needed, you can always create your own child type of
32
32
  /// `SerializedType`, and overload the `isDirty` method, or provided
33
33
  /// an `isDirty` method to the field `options` to check
@@ -5,17 +5,74 @@ const Type = require('../type');
5
5
 
6
6
  const DEFAULT_STRING_LENGTH = 256;
7
7
 
8
+ /// `STRING` type.
9
+ ///
10
+ /// This represents a "string" of any size for the
11
+ /// underlying database driver, usually a "VARCHAR" type.
12
+ ///
13
+ /// If no "length" is specified when you create the type,
14
+ /// then a default length of `256` is assumed.
15
+ ///
16
+ /// Example:
17
+ /// class Strings extends Model {
18
+ /// static fields = {
19
+ /// string1: Types.STRING(16),
20
+ /// string2: Types.STRING, // default length = 256
21
+ /// string3: new Types.StringType(64),
22
+ /// };
23
+ /// }
24
+ ///
25
+ /// See: Type
8
26
  class StringType extends Type {
27
+ /// Get the "display" name for this type.
28
+ ///
29
+ /// This method is called from <see>Model.toString</see>
30
+ /// when stringifying the model for representation.
31
+ ///
32
+ /// Note:
33
+ /// This is also an instance method that can be called from
34
+ /// an instance of the type.
35
+ ///
36
+ /// Return: string
37
+ /// Return the string value `'STRING'`
9
38
  static getDisplayName() {
10
39
  return 'STRING';
11
40
  }
12
41
 
42
+ /// Construct a new `STRING` type.
43
+ ///
44
+ /// The `length` argument specifies
45
+ /// the number of characters to use for this string
46
+ /// in the database.
47
+ ///
48
+ /// Return: StringType
49
+ ///
50
+ /// Arguments:
51
+ /// length?: number
52
+ /// How many characters to use in the underlying database to store the value.
13
53
  constructor(length) {
14
54
  super(length);
15
55
 
16
56
  this.length = length || DEFAULT_STRING_LENGTH;
17
57
  }
18
58
 
59
+ /// Cast provided value to underlying type.
60
+ ///
61
+ /// This will cast the incoming value to the
62
+ /// underlying type of this field, a `string`
63
+ /// primitive. A `null` or `undefined`
64
+ /// value will simply be returned.
65
+ ///
66
+ /// See <see>Type.castToType</see> for a more
67
+ /// detailed description.
68
+ ///
69
+ /// Return: string | null | undefined
70
+ /// Return the incoming `value`, cast to this
71
+ /// type. `null` and `undefined` are simply
72
+ /// returned without casting.
73
+ ///
74
+ /// Arguments:
75
+ /// context: <see name="CastToTypeContext">Type.castToType</see>
19
76
  castToType({ value }) {
20
77
  if (value == null)
21
78
  return value;
@@ -23,10 +80,41 @@ class StringType extends Type {
23
80
  return ('' + value);
24
81
  }
25
82
 
83
+ /// Check if the provided value is valid.
84
+ ///
85
+ /// This will check if the provided value is
86
+ /// a `string` (or `String`) instance.
87
+ /// Both an empty string, and a string that is nothing
88
+ /// except whitespace are considered "valid".
89
+ ///
90
+ /// Return: boolean
91
+ ///
92
+ /// Arguments:
93
+ /// value: any
94
+ /// The value to check.
26
95
  isValidValue(value) {
27
96
  return Nife.instanceOf(value, 'string');
28
97
  }
29
98
 
99
+ /// Stringify the type itself.
100
+ ///
101
+ /// If a `connection` argument is provided, then this
102
+ /// will go through the connection to generate the type
103
+ /// for the underlying database. If no connection is
104
+ /// provided, then a "standard" SQL type will be returned
105
+ /// for this type instead. The "standard" type returned
106
+ /// when no `connection` is provided is `'VARCHAR'`.
107
+ ///
108
+ /// Return: string
109
+ ///
110
+ /// Arguments:
111
+ /// connection?: <see>Connection</see>
112
+ /// An optional connection. If provided, send this
113
+ /// type through <see>Type.toConnectionType</see>
114
+ /// to have the connection itself generate the underlying
115
+ /// type for the database. If `connection` is not provided,
116
+ /// then this will simply return a "standard" generic matching
117
+ /// SQL type.
30
118
  toString(connection) {
31
119
  return (connection)
32
120
  ? this.toConnectionType(connection)