ezobjects
Advanced tools
Comparing version 2.11.6 to 3.0.0
202
example.js
@@ -1,135 +0,101 @@ | ||
const ezobjects = require(`./index`); | ||
const ezobjects = require('./index'); | ||
/** Create a customized object on the global (node) or window (browser) namespace */ | ||
ezobjects.createObject({ | ||
className: `DatabaseRecord`, | ||
/** Configure a basic EZ Object example */ | ||
const configBasicExample = { | ||
className: 'BasicExample', | ||
properties: [ | ||
{ name: `id`, type: `number`, setTransform: x => parseInt(x) } | ||
{ name: 'name', type: 'string' } | ||
] | ||
}); | ||
}; | ||
/** Example of the object newly instansiated */ | ||
const a = new DatabaseRecord(); | ||
/** Create the `BasicExample` class */ | ||
ezobjects.createClass(configBasicExample); | ||
console.log(a); | ||
/** Create a new instance of the `BasicExample` class */ | ||
const basicExample1 = new BasicExample(); | ||
/** Create another customized object that extends the first one */ | ||
ezobjects.createObject({ | ||
className: `Person`, | ||
extends: DatabaseRecord, | ||
properties: [ | ||
{ name: `firstName`, type: `string` }, | ||
{ name: `lastName`, type: `string` }, | ||
{ name: `checkingBalance`, type: `number`, setTransform: x => parseFloat(x) }, | ||
{ name: `permissions`, type: `Array` }, | ||
{ name: `favoriteDay`, type: `Date` } | ||
] | ||
}); | ||
/** Give it a name using the auto-generated setter */ | ||
basicExample1.name('Basic Example 1'); | ||
/** Example of the extended object newly instansiated */ | ||
const b = new Person(); | ||
/** Output the instance to console */ | ||
console.log(basicExample1); | ||
console.log(b); | ||
/** Example of the extended object instansiated and initialized using object passed to constructor */ | ||
const c = new Person({ | ||
id: 1, | ||
firstName: `Rich`, | ||
lastName: `Lowe`, | ||
checkingBalance: 4.87, | ||
permissions: [1, 2, 3], | ||
favoriteDay: new Date(`01-01-2018`) | ||
/** | ||
* Create another instance of the `BasicExample` class, but this time | ||
* initialize it with a plain object passed to the constructor. | ||
*/ | ||
const basicExample2 = new BasicExample({ | ||
name: 'Basic Example 2' | ||
}); | ||
console.log(c); | ||
/** | ||
* Configure a full EZ Object example demonstrating all supported types, | ||
* including the ability to extend other objects. | ||
*/ | ||
const configFullExample = { | ||
className: 'FullExample', | ||
extends: BasicExample, | ||
properties: [ | ||
{ name: 'exampleInt', type: 'int' }, | ||
{ name: 'exampleFloat', type: 'float' }, | ||
{ name: 'exampleString', type: 'string' }, | ||
{ name: 'exampleBoolean', type: 'boolean' }, | ||
{ name: 'exampleFunction', type: 'function' }, | ||
{ name: 'exampleDate', type: 'date' }, | ||
{ name: 'exampleBuffer', type: 'buffer' }, | ||
{ name: 'exampleOtherObj', type: 'BasicExample' }, | ||
{ name: 'exampleIntArray', type: 'array', arrayOf: { type: 'int' } }, | ||
{ name: 'exampleFloatArray', type: 'array', arrayOf: { type: 'float' } }, | ||
{ name: 'exampleStringArray', type: 'array', arrayOf: { type: 'string' } }, | ||
{ name: 'exampleBooleanArray', type: 'array', arrayOf: { type: 'boolean' } }, | ||
{ name: 'exampleFunctionArray', type: 'array', arrayOf: { type: 'function' } }, | ||
{ name: 'exampleDateArray', type: 'array', arrayOf: { type: 'date' } }, | ||
{ name: 'exampleBufferArray', type: 'array', arrayOf: { type: 'buffer' } }, | ||
{ name: 'exampleOtherObjArray', type: 'array', arrayOf: { type: 'BasicExample' } } | ||
] | ||
}; | ||
/** Example of the extended object instansiated, then loaded with data using setter methods */ | ||
const d = new Person(); | ||
/** Create the `FullExample` class */ | ||
ezobjects.createClass(configFullExample); | ||
d.id(2); | ||
d.firstName(`Bert`); | ||
d.lastName(`Reynolds`); | ||
d.checkingBalance(91425518.32); | ||
d.permissions([1, 4]); | ||
d.favoriteDay(new Date(`06-01-2017`)); | ||
console.log(d); | ||
/** Example of the extended object`s properties being accessed using getter methods */ | ||
console.log(`ID: ${d.id()}`); | ||
console.log(`First Name: ${d.firstName()}`); | ||
console.log(`Last Name: ${d.lastName()}`); | ||
console.log(`Checking Balance: $${d.checkingBalance()}`); | ||
console.log(`Permissions: ${d.permissions().join(`, `)}`); | ||
console.log(`Favorite Day: ${d.favoriteDay().toString()}`); | ||
/** Adding property to the generated object`s prototype */ | ||
DatabaseRecord.prototype.table = function (arg) { | ||
/** Getter */ | ||
if ( arg === undefined ) | ||
return this._table; | ||
/** Create a new instance of the `FullExample` class */ | ||
const fullExample = new FullExample({ | ||
name: `Full Example`, | ||
exampleInt: 293, | ||
exampleFloat: 194.13489, | ||
exampleString: `What's up, doc?`, | ||
exampleBoolean: true, | ||
exampleFunction: arg => `Hello, ${arg}!`, | ||
exampleDate: new Date('1776-07-04'), | ||
exampleBuffer: Buffer.from([0x04, 0x7F, 0x93, 0x38]), | ||
exampleOtherObj: basicExample1, | ||
/** Setter */ | ||
else if ( typeof arg == `string` ) | ||
this._table = arg; | ||
/** Handle type errors */ | ||
else | ||
throw new TypeError(`${this.constructor.name}.table(${typeof arg}): Invalid signature.`); | ||
/** Return this object for set call chaining */ | ||
return this; | ||
}; | ||
exampleIntArray: [293, -178, 492], | ||
exampleFloatArray: [194.13489, -2890.25, -0.04281], | ||
exampleStringArray: [`What's up, doc?`, `Shiver me timbers`], | ||
exampleBooleanArray: [true, false, true], | ||
exampleFunctionArray: [arg => `Hello, ${arg}!`, arg => `Farewell, ${arg}!`], | ||
exampleDateArray: [new Date('1776-07-04'), new Date('1945-12-07')], | ||
exampleBufferArray: [Buffer.from([0x04, 0x7F, 0x93, 0x38]), Buffer.from('A string instead')], | ||
exampleOtherObjArray: [basicExample1, basicExample2] | ||
}); | ||
/** Yuck, now I have to manually override the init() call if I want it initialized */ | ||
DatabaseRecord.prototype.init = function (data = {}) { | ||
this.id(data.id || 0); | ||
this.table(data.table || ``); | ||
}; | ||
/** Output the instance to console */ | ||
console.log(fullExample); | ||
const e = new DatabaseRecord(); | ||
/** Output one of the function responses to console */ | ||
console.log(fullExample.exampleFunctionArray()[1]('Rich')); | ||
console.log(e); | ||
/** Adding arbitrary capability other than property to the generated object`s prototype */ | ||
DatabaseRecord.prototype.hello = function () { | ||
return `Hello, World!`; | ||
}; | ||
const f = new DatabaseRecord(); | ||
console.log(f.hello()); | ||
/** These objects can be extended instead to accomplish the same things if preferred */ | ||
class DatabaseRecord2 extends DatabaseRecord { | ||
constructor(data = {}) { | ||
super(data); | ||
} | ||
init(data = {}) { | ||
super.init(data); | ||
this.test(`Test`); | ||
} | ||
test(arg) { | ||
/** Getter */ | ||
if ( arg === undefined ) | ||
return this._test; | ||
/** Setter */ | ||
else if ( typeof arg == `string` ) | ||
this._test = arg; | ||
/** Handle type errors */ | ||
else | ||
throw new TypeError(`${this.constructor.name}.test(${typeof arg}): Invalid signature.`); | ||
/** Return this object for set call chaining */ | ||
return this; | ||
} | ||
/** Try to set a `float` property with a `string` */ | ||
try { | ||
/** | ||
* This throws a TypeError since string given, not float; the same behavior | ||
* can be expected for all other types, including arrays of types. | ||
*/ | ||
fullExample.exampleFloat('test'); | ||
} catch ( err ) { | ||
/** Output error message to console */ | ||
console.log(err.message); | ||
} | ||
const g = new DatabaseRecord2(); | ||
console.log(g); | ||
console.log(g.hello()); | ||
const docket = require(`docket-parser`); | ||
docket.title(`EZ Objects v2.11.6`); | ||
docket.title(`EZ Objects v3.0.0`); | ||
docket.linkClass(`text-success`); | ||
docket.parseFiles([`index.js`, `mysql-connection.js`]); | ||
docket.parseFiles([`index.js`]); | ||
docket.generateDocs(`docs`); |
855
index.js
@@ -1,7 +0,1 @@ | ||
/** Require external modules */ | ||
const url = require(`url`); | ||
/** Require local modules */ | ||
const mysqlConnection = require(`./mysql-connection`); | ||
/** | ||
@@ -12,198 +6,205 @@ * @module ezobjects | ||
* @description Easy automatic class creation using simple configuration objects. Capable | ||
* of automatically creating a matching MySQL table and generating delete(), insert(), load(), | ||
* and update() methods in addition to the constructor, initializer, and getters/setters for | ||
* all configured properties. | ||
* | ||
* @signature ezobjects.createTable(db, obj) | ||
* @param db MySQLConnection | ||
* @param obj Object Configuration object | ||
* @description A function for automatically generating a MySQL table, if it doesn't already | ||
* exist, based on the values in the provided configuration object. | ||
* of automatically creating ES6 classes with constructor, initializer, and getters/setters | ||
* for all configured properties. | ||
*/ | ||
module.exports.createTable = async (db, obj) => { | ||
if ( typeof db != `object` || db.constructor.name != `MySQLConnection` ) | ||
throw new Error(`ezobjects.createTable(): Invalid database argument.`); | ||
else if ( typeof obj != `object` ) | ||
throw new Error(`ezobjects.createTable(): Invalid table configuration argument.`); | ||
/** Create some helpful arrays for identifying MySQL types that have certain features */ | ||
const mysqlTypesAllowed = [`BIT`, `TINYINT`, `SMALLINT`, `MEDIUMINT`, `INT`, `INTEGER`, `BIGINT`, `REAL`, `DOUBLE`, `FLOAT`, | ||
`DECIMAL`, `NUMERIC`, `DATE`, `TIME`, `TIMESTAMP`, `DATETIME`, `YEAR`, `CHAR`, `VARCHAR`, `BINARY`, | ||
`VARBINARY`, `TINYBLOB`, `BLOB`, `MEDIUMBLOB`, `LONGBLOB`, `TINYTEXT`, `TEXT`, `MEDIUMTEXT`, | ||
`LONGTEXT`, `ENUM`, `SET`, `JSON`]; | ||
const mysqlTypesWithLength = [`BIT`, `TINYINT`, `SMALLINT`, `MEDIUMINT`, `INT`, `INTEGER`, `BIGINT`, `REAL`, `DOUBLE`, `FLOAT`, | ||
`DECIMAL`, `NUMERIC`, `CHAR`, `VARCHAR`, `BINARY`, `VARBINARY`, `BLOB`, `TEXT`]; | ||
/** Require external modules */ | ||
const util = require(`util`); | ||
const mysqlTypesWithDecimals = [`REAL`, `DOUBLE`, `FLOAT`, `DECIMAL`, `NUMERIC`]; | ||
/** Figure out proper parent scope between node (global) and browser (window) */ | ||
let parent; | ||
const mysqlTypesWithLengthRequiringDecimals = [`REAL`, `DOUBLE`, `FLOAT`]; | ||
if ( typeof window !== `undefined` ) | ||
parent = window; | ||
else | ||
parent = global; | ||
const mysqlTypesWithUnsignedAndZerofill = [`TINYINT`, `SMALLINT`, `MEDIUMINT`, `INT`, `INTEGER`, `BIGINT`, `REAL`, `DOUBLE`, `FLOAT`, | ||
`DECIMAL`, `NUMERIC`]; | ||
const mysqlTypesWithCharacterSetAndCollate = [`CHAR`, `VARCHAR`, `TINYTEXT`, `TEXT`, `MEDIUMTEXT`, `LONGTEXT`, `ENUM`, `SET`]; | ||
/** Define the EZ Object types, their associated JavaScript and MySQL types, defaults, quirks, transforms, etc... */ | ||
const ezobjectTypes = [ | ||
{ type: `int`, javascriptType: 'number', default: 0, setTransform: x => parseInt(x) }, | ||
{ type: `float`, javascriptType: 'number', default: 0, setTransform: x => parseFloat(x) }, | ||
{ type: `string`, javascriptType: 'string', default: '' }, | ||
{ type: `boolean`, javascriptType: 'boolean', default: false, setTransform: x => x ? true : false }, | ||
{ type: `function`, javascriptType: 'function', default: () => {} }, | ||
{ type: `date`, javascriptType: 'Date', default: new Date(0) }, | ||
{ type: `buffer`, javascriptType: 'Buffer', default: Buffer.from([]) }, | ||
{ type: `other`, javascriptType: 'object', default: null }, | ||
/** Helper method that can be recursively called to add all properties to the create query */ | ||
const addPropertiesToCreateQuery = (obj) => { | ||
/** If this object extends another, recursively add properties from the extended object */ | ||
if ( obj.extendsConfig ) | ||
addPropertiesToCreateQuery(obj.extendsConfig); | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `int`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => isNaN(y)) ) | ||
throw new Error(`${property.name}.setTransform(): Non-numeric value passed to Array[int] setter.`); | ||
return x.map(y => parseInt(y)); | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `float`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => isNaN(y)) ) | ||
throw new Error(`${property.name}.setTransform(): Non-numeric value passed to Array[float] setter.`); | ||
return x.map(y => parseFloat(y)); | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `string`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => typeof y !== 'string') ) | ||
throw new Error(`${property.name}.setTransform(): Non-string value passed to Array[string] setter.`); | ||
return x; | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `boolean`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => typeof y !== 'boolean') ) | ||
throw new Error(`${property.name}.setTransform(): Non-boolean value passed to Array[boolean] setter.`); | ||
return x; | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `function`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => typeof y !== 'function') ) | ||
throw new Error(`${property.name}.setTransform(): Non-function value passed to Array[function] setter.`); | ||
return x; | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `date`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => typeof y !== 'object' || y.constructor.name != 'Date' ) ) | ||
throw new Error(`${property.name}.setTransform(): Non-Date value passed to Array[Date] setter.`); | ||
return x; | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `buffer`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => typeof y !== 'object' || y.constructor.name != 'Buffer' ) ) | ||
throw new Error(`${property.name}.setTransform(): Non-Buffer value passed to Array[Buffer] setter.`); | ||
return x; | ||
} | ||
}, | ||
{ type: `array`, javascriptType: `Array`, default: [], arrayOfType: `other`, | ||
setTransform: (x, property) => { | ||
if ( x.some(y => typeof y !== 'object' || ( typeof property.type == 'string' && y.constructor.name != property.originalType ) || ( typeof property.instanceOf === 'string' && y.constructor.name != property.instanceOf )) ) | ||
throw new Error(`${property.name}.setTransform(): Invalid value passed to Array[${typeof property.type === 'string' ? property.originalType : property.instanceOf}] setter.`); | ||
return x; | ||
} | ||
}, | ||
]; | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Validate configuration for a single property */ | ||
function validatePropertyConfig(property) { | ||
/** If name is missing or not a string, throw error */ | ||
if ( typeof property.name !== 'string' ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property configured with missing or invalid 'name'.`); | ||
/** Convert the type to upper case for reliable string comparison */ | ||
property.mysqlType = property.mysqlType.toUpperCase(); | ||
/** If name is not a valid MySQL column name, throw error */ | ||
if ( !property.name.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/) ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property 'name' not valid MySQL column name, must start with 'a-zA-Z_' and contain only 'a-zA-Z0-9_'.`); | ||
/** If type is missing or not a string, throw error */ | ||
if ( typeof property.type !== 'string' && typeof property.instanceOf !== 'string' ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property ${property.name} configured with missing or invalid 'type' and/or 'instanceOf', one of them is required.`); | ||
/** If type is invalid, throw error */ | ||
if ( property.type && typeof property.type !== 'string' ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property ${property.name} configured with invalid 'type'.`); | ||
/** If instanceOf is invalid, throw error */ | ||
if ( property.instanceOf && typeof property.instanceOf !== 'string' ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property ${property.name} configured with invalid 'instanceOf'.`); | ||
/** Add property name and type to query */ | ||
createQuery += `${property.name} ${property.mysqlType}`; | ||
/** If the original type has not yet been recorded */ | ||
if ( property.type && typeof property.originalType !== 'string' ) { | ||
/** Store original type with preserved case */ | ||
property.originalType = property.type; | ||
/** Convert type to lower-case for comparison to ezobjectsTypes */ | ||
property.type = property.type.toLowerCase(); | ||
} | ||
/** If type is 'ARRAY' with no 'arrayOf', throw error */ | ||
if ( property.type == 'array' && ( typeof property.arrayOf !== 'object' || property.arrayOf.constructor.name != 'Object' ) ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property '${property.name}' of type ${property.type} with missing or invalid 'arrayOf'.`); | ||
/** If type is 'ARRAY' with 'arrayOf' containing bad or missing type, throw error */ | ||
if ( property.type == 'array' && typeof property.arrayOf.type != 'string' && typeof property.arrayOf.instanceOf != 'string' ) | ||
throw new Error(`ezobjects.validatePropertyConfig(): Property '${property.name}' of type ${property.type} with missing or invalid 'arrayOf.type' and/or 'arrayOf.instanceOf', one of them is required.`); | ||
/** Attach arrayOf 'ezobjectType' if property type is 'array' */ | ||
if ( property.type == 'array' ) { | ||
/** If it's a standard EZ Object type, attach 'ezobjectType' to property for later use */ | ||
property.ezobjectType = ezobjectTypes.find(x => x.type == property.type && x.arrayOfType == property.arrayOf.type ); | ||
/** Types where length is required, throw error if missing */ | ||
if ( property.mysqlType == `VARCHAR` && isNaN(property.length) ) | ||
throw new Error(`Property of type VARCHAR used without required length.`); | ||
else if ( property.mysqlType == `VARBINARY` && isNaN(property.length) ) | ||
throw new Error(`Property of type VARBINARY used without required length.`); | ||
else if ( mysqlTypesWithLengthRequiringDecimals.includes(property.mysqlType) && !isNaN(property.length) && isNaN(property.decimals) ) | ||
throw new Error(`Property of type REAL, DOUBLE, or FLOAT used with length, but without decimals.`); | ||
/** If no standard type was found, use 'other' type for other objects */ | ||
if ( !property.ezobjectType ) | ||
property.ezobjectType = ezobjectTypes.find(x => x.type == 'array' && x.arrayOfType == 'other' ); | ||
/** If it's a standard EZ Object type, attach 'ezobjectType' to property arrayOf type for later use */ | ||
property.arrayOf.ezobjectType = ezobjectTypes.find(x => x.type == property.arrayOf.type); | ||
/** Properties with length and/or decimals */ | ||
if ( !isNaN(property.length) && mysqlTypesWithLength.includes(property.mysqlType) && ( !mysqlTypesWithDecimals.includes(property.mysqlType) || isNaN(property.decimals) ) ) | ||
createQuery += `(${property.length})`; | ||
else if ( !isNaN(property.length) && !isNaN(property.decimals) && mysqlTypesWithLength.includes(property.mysqlType) && mysqlTypesWithDecimals.includes(property.mysqlType) ) | ||
createQuery += `(${property.length}, ${property.decimals})`; | ||
/** If no standard type was found, use 'other' type for other objects */ | ||
if ( !property.arrayOf.ezobjectType ) | ||
property.arrayOf.ezobjectType = ezobjectTypes.find(x => x.type == 'other'); | ||
} else { | ||
/** If it's a standard EZ Object type, attach 'ezobjectType' to property for later use */ | ||
property.ezobjectType = ezobjectTypes.find(x => x.type == property.type); | ||
/** Properties with UNSIGNED */ | ||
if ( property.unsigned && mysqlTypesWithUnsignedAndZerofill.includes(property.mysqlType) ) | ||
createQuery += ` UNSIGNED`; | ||
/** If no standard type was found, use 'other' type for other objects */ | ||
if ( !property.ezobjectType ) | ||
property.ezobjectType = ezobjectTypes.find(x => x.type == 'other'); | ||
} | ||
/** Create default transform function that doesn't change the input */ | ||
const defaultTransform = x => x; | ||
/** If there is no init transform, set to default */ | ||
if ( typeof property.initTransform !== `function` ) | ||
property.initTransform = typeof property.ezobjectType == 'object' && typeof property.ezobjectType.initTransform == 'function' ? property.ezobjectType.initTransform : defaultTransform; | ||
/** Properties with ZEROFILL */ | ||
if ( property.zerofill && mysqlTypesWithUnsignedAndZerofill.includes(property.mysqlType) ) | ||
createQuery += ` ZEROFILL`; | ||
/** If there is no getter transform, set to default */ | ||
if ( typeof property.getTransform !== `function` ) | ||
property.getTransform = typeof property.ezobjectType == 'object' && typeof property.ezobjectType.getTransform == 'function' ? property.ezobjectType.getTransform : defaultTransform; | ||
/** Properties with CHARACTER SET */ | ||
if ( property.charsetName && mysqlTypesWithCharacterSetAndCollate.includes(property.mysqlType) ) | ||
createQuery += ` CHARACTER SET ${property.charsetName}`; | ||
/** If there is no setter transform, set to default */ | ||
if ( typeof property.setTransform !== `function` ) | ||
property.setTransform = typeof property.ezobjectType == 'object' && typeof property.ezobjectType.setTransform == 'function' ? property.ezobjectType.setTransform : defaultTransform; | ||
/** Properties with COLLATE */ | ||
if ( property.collationName && mysqlTypesWithCharacterSetAndCollate.includes(property.mysqlType) ) | ||
createQuery += ` COLLATE ${property.collationName}`; | ||
/** If there is no save transform, set to default */ | ||
if ( typeof property.saveTransform !== `function` ) | ||
property.saveTransform = typeof property.ezobjectType == 'object' && typeof property.ezobjectType.saveTransform == 'function' ? property.ezobjectType.saveTransform : defaultTransform; | ||
/** Properties with NULL */ | ||
if ( property.null ) | ||
createQuery += ` NULL`; | ||
else | ||
createQuery += ` NOT NULL`; | ||
/** If there is no load transform, set to default */ | ||
if ( typeof property.loadTransform !== `function` ) | ||
property.loadTransform = typeof property.ezobjectType == 'object' && typeof property.ezobjectType.loadTransform == 'function' ? property.ezobjectType.loadTransform : defaultTransform; | ||
/** Fully determine whether to allow nulls for this property */ | ||
if ( typeof property.allowNull !== `boolean` && property.ezobjectType.type != 'other' ) | ||
property.allowNull = false; | ||
else if ( typeof property.allowNull !== `boolean` ) | ||
property.allowNull = true; | ||
} | ||
/** Properties with DEFAULT */ | ||
if ( property.mysqlDefault ) | ||
createQuery += ` DEFAULT ${property.mysqlDefault}`; | ||
/** Validate configuration for a class */ | ||
function validateClassConfig(obj) { | ||
/** If configuration is not plain object, throw error */ | ||
if ( typeof obj != `object` || obj.constructor.name != `Object` ) | ||
throw new Error(`ezobjects.validateClassConfig(): Invalid table configuration argument, must be plain object.`); | ||
/** If configuration has missing or invalid 'className' configuration, throw error */ | ||
if ( typeof obj.className !== 'string' || !obj.className.match(/[A-Za-z_0-9$]+/) ) | ||
throw new Error(`ezobjects.validateClassConfig(): Configuration has missing or invalid 'className', must be string containing characters 'A-Za-z_0-9$'.`); | ||
/** Properties with AUTO_INCREMENT */ | ||
if ( property.autoIncrement ) | ||
createQuery += ` AUTO_INCREMENT`; | ||
/** Add properties array if one wasn't set */ | ||
if ( !obj.properties ) | ||
obj.properties = []; | ||
/** Properties with UNIQUE KEY */ | ||
if ( property.unique ) | ||
createQuery += ` UNIQUE`; | ||
/** Make sure properties is array */ | ||
if ( obj.properties && ( typeof obj.properties != 'object' || obj.properties.constructor.name != 'Array' ) ) | ||
throw new Error(`ezobjects.validateClassConfig(): Invalid properties configuration, properties not array.`); | ||
/** Loop through any properties and validate them */ | ||
obj.properties.forEach((property) => { | ||
validatePropertyConfig(property); | ||
}); | ||
} | ||
/** Properties with PRIMARY KEY */ | ||
if ( property.primary ) | ||
createQuery += ` PRIMARY`; | ||
if ( property.unique || property.primary ) | ||
createQuery += ` KEY`; | ||
/** Properties with COMMENT */ | ||
if ( property.comment && typeof property.comment == `string` ) | ||
createQuery += ` COMMENT '${property.comment.replace(`'`, ``)}'`; | ||
createQuery += `, `; | ||
}); | ||
}; | ||
/** Helper method that can be recursively called to add all indexes to the create query */ | ||
const addIndexesToCreateQuery = (obj) => { | ||
/** If this object extends another, recursively add indexes from the extended object */ | ||
if ( obj.extendsConfig ) | ||
addIndexesToCreateQuery(obj.extendsConfig); | ||
/** If there are any indexes defined */ | ||
if ( obj.indexes ) { | ||
/** Loop through each index */ | ||
obj.indexes.forEach((index) => { | ||
/** Convert the type to upper case for reliable string comparison */ | ||
index.type = index.type.toUpperCase(); | ||
/** If type is not defined, default to BTREE */ | ||
if ( typeof index.type !== `string` ) | ||
index.type = `BTREE`; | ||
/** Validate index settings */ | ||
if ( index.type != `BTREE` && index.type != `HASH` ) | ||
throw new Error(`Invalid index type '${index.type}'.`); | ||
else if ( index.visible && index.invisible ) | ||
throw new Error(`Index cannot have both VISIBLE and INVISIBLE options set.`); | ||
/** Add index name and type to query */ | ||
createQuery += `INDEX ${index.name} USING ${index.type} (`; | ||
/** Loop through each indexed column and append to query */ | ||
index.columns.forEach((column) => { | ||
createQuery += `${column}, `; | ||
}); | ||
/** Trim off extra ', ' from columns */ | ||
createQuery = createQuery.substr(0, createQuery.length - 2); | ||
/** Close column list */ | ||
createQuery += `)`; | ||
/** Indexes with KEY_BLOCK_SIZE */ | ||
if ( typeof index.keyBlockSize === `number` ) | ||
createQuery += ` KEY_BLOCK_SIZE ${index.keyBlockSize}`; | ||
/** Indexes with WITH PARSER */ | ||
if ( typeof index.parserName === `string` ) | ||
createQuery += ` WITH PARSER ${index.parserName}`; | ||
/** Indexes with COMMENT */ | ||
if ( typeof index.comment === `string` ) | ||
createQuery += ` COMMENT '${index.comment.replace(`'`, ``)}'`; | ||
/** Indexes with VISIBLE */ | ||
if ( typeof index.visible === `boolean` && index.visible ) | ||
createQuery += ` VISIBLE`; | ||
/** Indexes with INVISIBLE */ | ||
if ( typeof index.visible === `boolean` && index.invisible ) | ||
createQuery += ` INVISIBLE`; | ||
createQuery += `, `; | ||
}); | ||
} | ||
}; | ||
/** Begin create table query */ | ||
let createQuery = `CREATE TABLE IF NOT EXISTS ${obj.tableName} (`; | ||
/** Call recursive methods to add properties and indexes to query */ | ||
addPropertiesToCreateQuery(obj); | ||
addIndexesToCreateQuery(obj); | ||
/** Trim extra ', ' from property and/or index list */ | ||
createQuery = createQuery.substr(0, createQuery.length - 2); | ||
/** Close property and/or index list */ | ||
createQuery += `)`; | ||
/** Await query execution and return result */ | ||
return await db.query(createQuery); | ||
}; | ||
/** | ||
* @signature ezobjects.instanceof(obj, constructorName) | ||
* @signature ezobjects.instanceOf(obj, constructorName) | ||
* @param obj Object | ||
@@ -217,6 +218,9 @@ * @param constructorName string | ||
/** Recursive function for determining if ancestral prototype is an instance of the given `constructorName` */ | ||
const isInstance = (obj) => { | ||
/** If it is an instance of `constructorName`, set found to true */ | ||
if ( obj && obj.constructor && obj.constructor.name == constructorName ) | ||
found = true; | ||
/** If this is an extension of a more fundamental prototype, recursively check it too */ | ||
if ( obj && obj.__proto__ ) | ||
@@ -226,4 +230,6 @@ isInstance(obj.__proto__); | ||
/** See if `obj` is an instance of `constructorName` */ | ||
isInstance(obj); | ||
/** Return the result */ | ||
return found; | ||
@@ -233,3 +239,3 @@ }; | ||
/** | ||
* @signature ezobjects.createObject(obj) | ||
* @signature ezobjects.createClass(obj) | ||
* @param obj Object Configuration object | ||
@@ -239,17 +245,5 @@ * @description A function for automatically generating a class object based on | ||
*/ | ||
module.exports.createObject = (obj) => { | ||
/** Add properties array if one wasn't set */ | ||
if ( !obj.properties ) | ||
obj.properties = []; | ||
/** Create default transform function that doesn't change the input */ | ||
const defaultTransform = x => x; | ||
let parent; | ||
/** Figure out proper global scope between node (global) and browser (window) */ | ||
if ( typeof window !== `undefined` ) | ||
parent = window; | ||
else | ||
parent = global; | ||
module.exports.createClass = (obj) => { | ||
/** Validate class configuration */ | ||
validateClassConfig(obj); | ||
@@ -260,4 +254,6 @@ /** Create new class on global scope */ | ||
constructor(data = {}) { | ||
/** Initialize super */ | ||
super(data); | ||
/** Initialize object to values in `data` or defaults */ | ||
this.init(data); | ||
@@ -272,5 +268,10 @@ } | ||
/** If data is a string, assume it's JSON encoded and parse */ | ||
if ( typeof data == `string` ) | ||
data = JSON.parse(data); | ||
/** If data is a string, assume it's JSON encoded and try and parse */ | ||
if ( typeof data == `string` ) { | ||
try { | ||
data = JSON.parse(data); | ||
} catch ( err ) { | ||
throw new Error(`${this.constructor.name}.init(${typeof data}): Initialization string is not valid JSON.`); | ||
} | ||
} | ||
@@ -288,35 +289,7 @@ /** Loop through each key/val pair in data */ | ||
}); | ||
const emptyFunction = function () { | ||
}; | ||
/** Loop through each property in the obj */ | ||
obj.properties.forEach((property) => { | ||
/** If there is no init transform, set to default */ | ||
if ( typeof property.initTransform !== `function` ) | ||
property.initTransform = defaultTransform; | ||
/** Initialize 'number' types to zero */ | ||
if ( property.type && property.type.split(`|`).includes(`number`) ) | ||
this[property.name](property.initTransform(data[property.name]) || property.default || 0); | ||
/** Initialize 'boolean' types to false */ | ||
else if ( property.type && property.type.split(`|`).includes(`boolean`) ) | ||
this[property.name](property.initTransform(data[property.name]) || property.default || false); | ||
/** Initialize 'string' types to empty */ | ||
else if ( property.type && property.type.split(`|`).includes(`string`) ) | ||
this[property.name](property.initTransform(data[property.name]) || property.default || ``); | ||
/** Initialize 'function' types to empty function */ | ||
else if ( property.type && property.type.split(`|`).includes(`function`) ) | ||
this[property.name](property.initTransform(data[property.name]) || property.default || emptyFunction); | ||
/** Initialize 'Array' types to empty */ | ||
else if ( property.type && property.type.split(`|`).includes(`Array`) ) | ||
this[property.name](property.initTransform(data[property.name]) || property.default || []); | ||
/** Initialize all other types to null */ | ||
else | ||
this[property.name](property.initTransform(data[property.name]) || property.default || null); | ||
/** Initialize types to defaults */ | ||
this[property.name](property.initTransform(data[property.name]) || property.default || property.ezobjectType.default); | ||
}); | ||
@@ -327,407 +300,40 @@ } | ||
/** Loop through each property in the obj */ | ||
obj.properties.forEach((property) => { | ||
/** If there is no getter transform, set to default */ | ||
if ( typeof property.getTransform !== `function` ) | ||
property.getTransform = defaultTransform; | ||
/** If there is no setter transform, set to default */ | ||
if ( typeof property.setTransform !== `function` ) | ||
property.setTransform = defaultTransform; | ||
/** If there is no save transform, set to default */ | ||
if ( typeof property.saveTransform !== `function` ) | ||
property.saveTransform = defaultTransform; | ||
/** If there is no load transform, set to default */ | ||
if ( typeof property.loadTransform !== `function` ) | ||
property.loadTransform = defaultTransform; | ||
obj.properties.forEach((property) => { | ||
/** Create class method on prototype */ | ||
parent[obj.className].prototype[property.name] = function (arg) { | ||
parent[obj.className].prototype[property.name] = function (arg) { | ||
/** Getter */ | ||
if ( arg === undefined ) | ||
return property.getTransform(this[`_${property.name}`]); | ||
/** Setters */ | ||
/** Setter for non-nullable standard property types */ | ||
else if ( !property.allowNull && ![`object`, `Buffer`, `Array`, `Date`].includes(property.ezobjectType.javascriptType) && typeof arg == property.ezobjectType.javascriptType ) | ||
this[`_${property.name}`] = property.setTransform(arg, property.instanceOf ? property.instanceOf : property.originalType); | ||
/** For `number` type properties */ | ||
else if ( property.type && property.type.split(`|`).includes(`number`) && typeof arg == `number` ) | ||
this[`_${property.name}`] = property.setTransform(arg); | ||
/** Setter for nullable standard property types */ | ||
else if ( property.allowNull && ![`object`, `Buffer`, `Array`, `Date`].includes(property.ezobjectType.javascriptType) ) | ||
this[`_${property.name}`] = property.setTransform(arg, property.instanceOf ? property.instanceOf : property.originalType); | ||
/** For `boolean` type properties */ | ||
else if ( property.type && property.type.split(`|`).includes(`boolean`) && typeof arg == `boolean` ) | ||
this[`_${property.name}`] = property.setTransform(arg); | ||
/** For `string` type properties */ | ||
else if ( property.type && property.type.split(`|`).includes(`string`) && typeof arg == `string` ) | ||
this[`_${property.name}`] = property.setTransform(arg); | ||
/** Setter for non-nullable Array property types */ | ||
else if ( !property.allowNull && typeof arg == `object` && arg && property.ezobjectType.javascriptType == `Array` && ( arg.constructor.name == property.ezobjectType.javascriptType || module.exports.instanceOf(arg, property.ezobjectType.javascriptType) ) ) | ||
this[`_${property.name}`] = property.setTransform(arg, property.arrayOf.instanceOf ? property.arrayOf.instanceOf : property.arrayOf.type); | ||
/** For `function` type properties */ | ||
else if ( property.type && property.type.split(`|`).includes(`function`) && typeof arg == `function` ) | ||
this[`_${property.name}`] = property.setTransform(arg); | ||
/** Setter for nullable Array property types */ | ||
else if ( property.allowNull && property.ezobjectType.javascriptType == `Array` && typeof arg == `object` && arg === null ) | ||
this[`_${property.name}`] = property.setTransform(arg, property.arrayOf.instanceOf ? property.arrayOf.instanceOf : property.arrayOf.type); | ||
/** For `Array` type propeties */ | ||
else if ( property.type && property.type.split(`|`).includes(`Array`) && typeof arg == `object` && arg.constructor.name == `Array` ) | ||
this[`_${property.name}`] = property.setTransform(arg); | ||
/** For all other property types */ | ||
else if ( arg === null || ( typeof arg == `object` && property.type && property.type.split(`|`).includes(arg.constructor.name) ) || ( typeof property.instanceOf == `string` && property.instanceOf.split(`|`).some(x => module.exports.instanceOf(arg, x)) ) ) | ||
this[`_${property.name}`] = property.setTransform(arg); | ||
/** Setter for non-nullable Buffer and Date types */ | ||
else if ( !property.allowNull && typeof arg == `object` && arg && [`Buffer`, `Date`].includes(property.ezobjectType.javascriptType) && ( arg.constructor.name == property.ezobjectType.javascriptType || module.exports.instanceOf(arg, property.ezobjectType.javascriptType) ) ) | ||
this[`_${property.name}`] = property.setTransform(arg, property.instanceOf ? property.instanceOf : property.originalType); | ||
/** Setter for nullable or custom property types */ | ||
else if ( arg === null || typeof arg === `object` && ( property.originalType && property.originalType == arg.constructor.name ) || ( typeof arg == `object` && typeof property.instanceOf == `string` && module.exports.instanceOf(arg, property.instanceOf) ) ) | ||
this[`_${property.name}`] = property.setTransform(arg, property.instanceOf ? property.instanceOf : property.originalType); | ||
/** Handle type errors */ | ||
else | ||
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature.`); | ||
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature, requires '${typeof property.type === 'string' ? property.originalType : property.instanceOf}'.`); | ||
/** Return this object for set call chaining */ | ||
return this; | ||
}; | ||
if ( typeof obj.tableName == `string` ) { | ||
/** Create MySQL delete method on prototype */ | ||
parent[obj.className].prototype.delete = async function (db) { | ||
/** If the argument is a valid database, delete the record */ | ||
if ( typeof db == `object` && db.constructor.name == `MySQLConnection` ) { | ||
/** Execute query to delete record from database */ | ||
await db.query(`DELETE FROM ${obj.tableName} WHERE id = ?`, [this.id()]); | ||
} | ||
/** Otherwise throw TypeError */ | ||
else { | ||
throw new TypeError(`${this.constructor.name}.delete(${typeof db}): Invalid signature.`); | ||
} | ||
/** Allow for call chaining */ | ||
return this; | ||
}; | ||
/** Create MySQL insert method on prototype */ | ||
parent[obj.className].prototype.insert = async function (arg1) { | ||
/** Provide option for inserting record from browser if developer implements ajax backend */ | ||
if ( typeof window !== `undefined` && typeof arg1 == `string` ) { | ||
const url = new URL(arg1); | ||
const result = await $.get({ | ||
url: url.href, | ||
data: JSON.stringify(this), | ||
dataType: `json` | ||
}); | ||
if ( result && result.insertId ) | ||
this.id(result.insertId); | ||
else | ||
throw new Error(`${obj.className}.insert(): Unable to insert record, invalid response from remote host.`); | ||
} | ||
/** If the argument is a valid database, insert record into database and capture ID */ | ||
else if ( typeof arg1 == `object` && arg1.constructor.name == `MySQLConnection` ) { | ||
/** Create array for storing values to insert */ | ||
const params = []; | ||
/** Create helper method for recursively adding properties to params array */ | ||
const propertyValues = (obj) => { | ||
/** If this object extends another, recursively add properties from the extended object */ | ||
if ( obj.extendsConfig ) | ||
propertyValues(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore ID since we`ll get that from the insert */ | ||
if ( property.name == `id` ) | ||
return; | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Add property to params array after performing the save transform */ | ||
params.push(property.saveTransform(this[property.name]())); | ||
}); | ||
}; | ||
/** Recursively add properties to params array */ | ||
propertyValues(obj); | ||
/** Begin INSERT query */ | ||
let query = `INSERT INTO ${obj.tableName} (`; | ||
/** Create helper method for recursively adding property names to query */ | ||
const propertyNames = (obj) => { | ||
/** If this object extends another, recursively add property names from the extended object */ | ||
if ( obj.extendsConfig ) | ||
propertyNames(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore ID since we`ll get that from the insert */ | ||
if ( property.name == `id` ) | ||
return; | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Append property name to query */ | ||
query += `${property.name}, `; | ||
}); | ||
}; | ||
/** Add property names to query */ | ||
propertyNames(obj); | ||
/** Trim extra `, ` from property list */ | ||
query = query.substr(0, query.length - 2); | ||
/** Continue query */ | ||
query += `) VALUES (`; | ||
/** Create helper method for recursively adding property value placeholders to query */ | ||
const propertyPlaceholders = (obj) => { | ||
/** If this object extends another, recursively add property placeholders from the extended object */ | ||
if ( obj.extendsConfig ) | ||
propertyPlaceholders(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore ID since we`ll get that from the insert */ | ||
if ( property.name == `id` ) | ||
return; | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Append placeholder to query */ | ||
query += `?, `; | ||
}); | ||
}; | ||
/** Add property placeholders to query */ | ||
propertyPlaceholders(obj); | ||
/** Trim extra `, ` from placeholder list */ | ||
query = query.substr(0, query.length - 2); | ||
/** Finish query */ | ||
query += `)`; | ||
/** Execute query to add record to database */ | ||
const result = await arg1.query(query, params); | ||
/** Store the resulting insert ID */ | ||
this.id(result.insertId); | ||
} | ||
/** Otherwise throw TypeError */ | ||
else { | ||
throw new TypeError(`${this.constructor.name}.insert(${typeof arg1}): Invalid signature.`); | ||
} | ||
/** Allow for call chaining */ | ||
return this; | ||
}; | ||
/** Create MySQL load method on prototype */ | ||
parent[obj.className].prototype.load = async function (arg1, arg2) { | ||
/** Provide option for loading record from browser if developer implements ajax backend */ | ||
if ( typeof window !== `undefined` && typeof arg1 == `string` ) { | ||
const url = new URL(arg1); | ||
const result = await $.get({ | ||
url: url.href, | ||
dataType: `json` | ||
}); | ||
if ( result ) | ||
this.init(result); | ||
else | ||
throw new Error(`${obj.className}.load(): Unable to load record, invalid response from remote host.`); | ||
} | ||
/** If the first argument is a valid database and the second is a number, load record from database by ID */ | ||
else if ( typeof arg1 == `object` && arg1.constructor.name == `MySQLConnection` && ( typeof arg2 == `number` || ( typeof arg2 == `string` && typeof obj.stringSearchField == `string` ) ) ) { | ||
/** Begin SELECT query */ | ||
let query = `SELECT `; | ||
/** Create helper method for recursively adding property names to query */ | ||
const propertyNames = (obj) => { | ||
/** If this object extends another, recursively add property names from the extended object */ | ||
if ( obj.extendsConfig ) | ||
propertyNames(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Append property name to query */ | ||
query += `${property.name}, `; | ||
}); | ||
}; | ||
/** Add property names to query */ | ||
propertyNames(obj); | ||
/** Trim extra `, ` from property list */ | ||
query = query.substr(0, query.length - 2); | ||
/** Finish query */ | ||
query += ` FROM ${obj.tableName} `; | ||
if ( typeof arg2 === `string` && typeof obj.stringSearchField === `string` ) | ||
query += `WHERE ${obj.stringSearchField} = ?`; | ||
else | ||
query += `WHERE id = ?`; | ||
/** Execute query to load record properties from the database */ | ||
const result = await arg1.query(query, [arg2]); | ||
/** If a record with that ID doesn`t exist, throw error */ | ||
if ( !result[0] ) | ||
throw new ReferenceError(`${this.constructor.name}.load(): Record ${arg2} in ${obj.tableName} does not exist.`); | ||
/** Create helper method for recursively loading property values into object */ | ||
const loadProperties = (obj) => { | ||
/** If this object extends another, recursively add extended property values into objecct */ | ||
if ( obj.extendsConfig ) | ||
loadProperties(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Append property in object */ | ||
this[property.name](property.loadTransform(result[0][property.name])); | ||
}); | ||
}; | ||
/** Store loaded record properties into object */ | ||
loadProperties(obj); | ||
} | ||
/** If the first argument is a MySQL RowDataPacket, load from row data */ | ||
else if ( typeof arg1 == `object` && ( arg1.constructor.name == `RowDataPacket` || arg1.constructor.name == `Array` ) ) { | ||
/** Create helper method for recursively loading property values into object */ | ||
const loadProperties = (obj) => { | ||
/** If this object extends another, recursively add extended property values into objecct */ | ||
if ( obj.extendsConfig ) | ||
loadProperties(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Append property in object */ | ||
if ( typeof arg1[property.name] !== `undefined` ) | ||
this[property.name](property.loadTransform(arg1[property.name])); | ||
}); | ||
}; | ||
/** Store loaded record properties into object */ | ||
loadProperties(obj); | ||
} | ||
/** Otherwise throw TypeError */ | ||
else { | ||
throw new TypeError(`${this.constructor.name}.load(${typeof arg1}, ${typeof arg2}): Invalid signature.`); | ||
} | ||
/** Allow for call chaining */ | ||
return this; | ||
}; | ||
/** Create MySQL update method on prototype */ | ||
parent[obj.className].prototype.update = async function (arg1) { | ||
/** Provide option for inserting record from browser if developer implements ajax backend */ | ||
if ( typeof window !== `undefined` && typeof arg1 == `string` ) { | ||
const url = new URL(arg1); | ||
const result = await $.get({ | ||
url: url.href, | ||
data: JSON.stringify(this), | ||
dataType: `json` | ||
}); | ||
if ( !result ) | ||
throw new Error(`${obj.className}.update(): Unable to update record, invalid response from remote host.`); | ||
} | ||
/** If the argument is a valid database, update database record */ | ||
else if ( typeof arg1 == `object` && arg1.constructor.name == `MySQLConnection` ) { | ||
/** Create array for storing values to update */ | ||
const params = []; | ||
/** Create helper method for recursively adding properties to params array */ | ||
const propertyValues = (obj) => { | ||
/** If this object extends another, recursively add properties from the extended object */ | ||
if ( obj.extendsConfig ) | ||
propertyValues(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore ID since we will use that to locate the record, and will never update it */ | ||
if ( property.name == `id` ) | ||
return; | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Add property to params array after performing the save transform */ | ||
params.push(property.saveTransform(this[property.name]())); | ||
}); | ||
}; | ||
/** Recursively add properties to params array */ | ||
propertyValues(obj); | ||
/** Add ID to params array at the end so we can locate the record to update */ | ||
params.push(this.id()); | ||
/** Begin UPDATE query */ | ||
let query = `UPDATE ${obj.tableName} SET `; | ||
/** Create helper method for recursively adding property updates to query */ | ||
const propertyUpdates = (obj) => { | ||
/** If this object extends another, recursively add properties from the extended object */ | ||
if ( obj.extendsConfig ) | ||
propertyUpdates(obj.extendsConfig); | ||
/** Loop through each property */ | ||
obj.properties.forEach((property) => { | ||
/** Ignore ID since we will use that to locate the record, and will never update it */ | ||
if ( property.name == `id` ) | ||
return; | ||
/** Ignore properties that don`t have MySQL types */ | ||
if ( typeof property.mysqlType == `undefined` ) | ||
return; | ||
/** Append property update to query */ | ||
query += `${property.name} = ?, `; | ||
}); | ||
}; | ||
/** Add property updates to query */ | ||
propertyUpdates(obj); | ||
/** Trim extra `, ` from property list */ | ||
query = query.substr(0, query.length - 2); | ||
/** Finish query */ | ||
query += ` WHERE id = ?`; | ||
/** Execute query to update record in database */ | ||
await arg1.query(query, params); | ||
} | ||
/** Otherwise throw TypeError */ | ||
else { | ||
throw new TypeError(`${this.constructor.name}.update(${typeof arg1}): Invalid signature.`); | ||
} | ||
/** Allow for call chaining */ | ||
return this; | ||
}; | ||
} | ||
}); | ||
@@ -741,4 +347,1 @@ | ||
}; | ||
/** Re-export MySQLConnection */ | ||
module.exports.MySQLConnection = mysqlConnection.MySQLConnection; |
{ | ||
"name": "ezobjects", | ||
"version": "2.11.6", | ||
"description": "Easy dynamic object generation with optional MySQL table linking", | ||
"version": "3.0.0", | ||
"description": "Easy Auto-Generated Strictly-Typed JavaScript Class Objects", | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "node example-mysql.js", | ||
"test": "eslint example.js example-mysql.js index.js mysql-connection.js" | ||
"start": "node example.js", | ||
"test": "eslint example.js index.js" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/om-mani-padme-hum/ezobjects.git" | ||
"url": "git+https://github.com/om-mani-padme-hum/ezobjects.git" | ||
}, | ||
"keywords": [ | ||
"easy", | ||
"strict", | ||
"strictly", | ||
"typed", | ||
"auto", | ||
@@ -28,16 +31,17 @@ "automatic", | ||
"generate", | ||
"generation", | ||
"mysql", | ||
"linked", | ||
"database" | ||
"generation" | ||
], | ||
"author": "Rich Lowe", | ||
"license": "MIT", | ||
"dependencies": { | ||
"moment": "^2.22.1", | ||
"mysql": "^2.15.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"docket-parser": "^0.6.7" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/om-mani-padme-hum/ezobjects/issues" | ||
}, | ||
"homepage": "https://github.com/om-mani-padme-hum/ezobjects#readme", | ||
"directories": { | ||
"doc": "docs" | ||
} | ||
} |
459
README.md
@@ -1,14 +0,10 @@ | ||
# EZ Objects v2.11.6 | ||
# EZ Objects v3.0.0 | ||
EZ Objects is a Node.js module (that can also be usefully browserify'd) that aims to save you lots of time | ||
writing class objects that are strictly typed in JavaScript, and can be tied directly to MySQL database tables | ||
by way of a mix of insert/update/load/delete class method signatures. All you have to do is create simple | ||
configurations for each of your objects and then create them using the createObject() function. | ||
writing class objects that are strictly typed in JavaScript. All you have to do is create simple | ||
configurations for each of your objects and then create them using the createClass() function. | ||
* [Installation](#installation) | ||
* [Basic Example](#basic-example) | ||
* [Extending the Basic Example](#extending-the-basic-example) | ||
* [MySQL Example with Extended Object](#mysql-example-with-extended-object) | ||
* [Basic EZ Object Method Signatures](#basic-ez-object-method-signatures) | ||
* [MySQL EZ Object Method Signatures](#mysql-ez-object-method-signatures) | ||
* [Examples](#examples) | ||
* [EZ Object Method Signatures](#ez-object-method-signatures) | ||
* [Module Exports](#module-exports) | ||
@@ -23,3 +19,3 @@ * [Configuration Specifications](#configuration-specifications) | ||
## Basic Example | ||
## Examples | ||
@@ -29,227 +25,100 @@ ```javascript | ||
/** | ||
* Create a customized object called DatabaseRecord on the | ||
* global (node) or window (browser) namespace with a single | ||
* property called `id`. | ||
*/ | ||
ezobjects.createObject({ | ||
className: `DatabaseRecord`, | ||
/** Configure a basic EZ Object example */ | ||
const configBasicExample = { | ||
className: 'BasicExample', | ||
properties: [ | ||
{ name: `id`, type: `number`, setTransform: x => parseInt(x) } | ||
{ name: 'name', type: 'string' } | ||
] | ||
}); | ||
const record = new DatabaseRecord(); | ||
}; | ||
console.log(record); | ||
``` | ||
/** Create the `BasicExample` class */ | ||
ezobjects.createClass(configBasicExample); | ||
### Expected Output | ||
/** Create a new instance of the `BasicExample` class */ | ||
const basicExample1 = new BasicExample(); | ||
``` | ||
DatabaseRecord { _id: 0 } | ||
``` | ||
/** Give it a name using the auto-generated setter */ | ||
basicExample1.name('Basic Example 1'); | ||
## Extending the Basic Example | ||
/** Output the instance to console */ | ||
console.log(basicExample1); | ||
```javascript | ||
ezobjects.createObject({ | ||
className: `User`, | ||
extends: DatabaseRecord, | ||
properties: [ | ||
{ name: `username`, type: `string` }, | ||
{ name: `firstName`, type: `string` }, | ||
{ name: `lastName`, type: `string` }, | ||
{ name: `checkingBalance`, type: `number`, setTransform: x => parseFloat(x) }, | ||
{ name: `permissions`, type: `Array` }, | ||
{ name: `favoriteDay`, type: `Date` } | ||
] | ||
}); | ||
const user = new User(); | ||
console.log(user); | ||
``` | ||
### Expected Output | ||
``` | ||
User { | ||
_id: 0, | ||
_username: ``, | ||
_firstName: ``, | ||
_lastName: ``, | ||
_checkingBalance: 0, | ||
_permissions: [], | ||
_favoriteDay: null } | ||
``` | ||
## MySQL Example with Extended Object | ||
**Important Notes:** Your object must have a unique integer property named `id` to be able to use the MySQL | ||
functionality of EZ Objects. You must also use EZ Object's MySQLConnection class for your database connection. | ||
```javascript | ||
const ezobjects = require(`ezobjects`); | ||
const fs = require(`fs`); | ||
const moment = require(`moment`); | ||
/** | ||
* Load external MySQL configuration which uses the following JSON | ||
* format: | ||
* { | ||
* "host" : "localhost", | ||
* "user" : "ezobjects", | ||
* "password" : "myPassword", | ||
* "database" : "ezobjects" | ||
* } | ||
* Create another instance of the `BasicExample` class, but this time | ||
* initialize it with a plain object passed to the constructor. | ||
*/ | ||
const configMySQL = JSON.parse(fs.readFileSync(`mysql-config.json`)); | ||
const basicExample2 = new BasicExample({ | ||
name: 'Basic Example 2' | ||
}); | ||
/** | ||
* Create a connection object for the MySQL database using our MySQL | ||
* module async/await wrapper. | ||
* Configure a full EZ Object example demonstrating all supported types, | ||
* including the ability to extend other objects. | ||
*/ | ||
const db = new ezobjects.MySQLConnection(configMySQL); | ||
/** | ||
* Configure a new EZ Object called DatabaseRecord with one `id` | ||
* property that contains additional MySQL configuration settings. | ||
*/ | ||
const configDatabaseRecord = { | ||
className: `DatabaseRecord`, | ||
const configFullExample = { | ||
className: 'FullExample', | ||
extends: BasicExample, | ||
properties: [ | ||
{ | ||
name: `id`, | ||
type: `number`, | ||
mysqlType: `int`, | ||
autoIncrement: true, | ||
primary: true, | ||
setTransform: x => parseInt(x) | ||
} | ||
{ name: 'exampleInt', type: 'int' }, | ||
{ name: 'exampleFloat', type: 'float' }, | ||
{ name: 'exampleString', type: 'string' }, | ||
{ name: 'exampleBoolean', type: 'boolean' }, | ||
{ name: 'exampleFunction', type: 'function' }, | ||
{ name: 'exampleDate', type: 'date' }, | ||
{ name: 'exampleBuffer', type: 'buffer' }, | ||
{ name: 'exampleOtherObj', type: 'BasicExample' }, | ||
{ name: 'exampleIntArray', type: 'array', arrayOf: { type: 'int' } }, | ||
{ name: 'exampleFloatArray', type: 'array', arrayOf: { type: 'float' } }, | ||
{ name: 'exampleStringArray', type: 'array', arrayOf: { type: 'string' } }, | ||
{ name: 'exampleBooleanArray', type: 'array', arrayOf: { type: 'boolean' } }, | ||
{ name: 'exampleFunctionArray', type: 'array', arrayOf: { type: 'function' } }, | ||
{ name: 'exampleDateArray', type: 'array', arrayOf: { type: 'date' } }, | ||
{ name: 'exampleBufferArray', type: 'array', arrayOf: { type: 'buffer' } }, | ||
{ name: 'exampleOtherObjArray', type: 'array', arrayOf: { type: 'BasicExample' } } | ||
] | ||
}; | ||
/** | ||
* Create the DatabaseRecord object -- Note: This object is not | ||
* linked to a MySQL table directory, as therefore has no `tableName` | ||
* property, but it has the MySQL configuration properties on `id` | ||
* because it will be extended by another object that is linked to | ||
* a MySQL table and therefore it will need the MySQL configuration | ||
* of the `id` property. | ||
*/ | ||
ezobjects.createObject(configDatabaseRecord); | ||
/** Create the `FullExample` class */ | ||
ezobjects.createClass(configFullExample); | ||
/** | ||
* Configure a new EZ Object called User that extends from the | ||
* DatabaseRecord object and adds several additional properties and | ||
* a MySQL index. | ||
*/ | ||
const configUser = { | ||
tableName: `users`, | ||
className: `User`, | ||
extends: DatabaseRecord, | ||
extendsConfig: configDatabaseRecord, | ||
properties: [ | ||
{ | ||
name: `username`, | ||
type: `string`, | ||
mysqlType: `varchar`, | ||
length: 20 | ||
}, | ||
{ | ||
name: `firstName`, | ||
type: `string`, | ||
mysqlType: `varchar`, | ||
length: 20 | ||
}, | ||
{ | ||
name: `lastName`, | ||
type: `string`, | ||
mysqlType: `varchar`, | ||
length: 20 | ||
}, | ||
{ | ||
name: `checkingBalance`, | ||
type: `number`, | ||
mysqlType: `double`, | ||
setTransform: x => parseFloat(x) | ||
}, | ||
{ | ||
name: `permissions`, | ||
type: `Array`, | ||
mysqlType: `text`, | ||
saveTransform: x => x.join(`,`), | ||
loadTransform: x => x.split(`,`).map(x => parseInt(x)) | ||
}, | ||
{ | ||
name: `favoriteDay`, | ||
type: `Date`, | ||
mysqlType: `datetime`, | ||
saveTransform: x => moment(x).format(`Y-MM-DD HH:mm:ss`), | ||
loadTransform: x => new Date(x) | ||
} | ||
], | ||
indexes: [ | ||
{ name: `lastName`, type: `BTREE`, columns: [ `lastName` ] } | ||
] | ||
}; | ||
/** Create the User object */ | ||
ezobjects.createObject(configUser); | ||
/** Create new user, initializing with object passed to constructor */ | ||
const user = new User({ | ||
username: `richlowe`, | ||
firstName: `Rich`, | ||
lastName: `Lowe`, | ||
checkingBalance: 4.32, | ||
permissions: [1, 3, 5], | ||
favoriteDay: new Date(`01-01-2018`) | ||
/** Create a new instance of the `FullExample` class */ | ||
const fullExample = new FullExample({ | ||
name: `Full Example`, | ||
exampleInt: 293, | ||
exampleFloat: 194.13489, | ||
exampleString: `What's up, doc?`, | ||
exampleBoolean: true, | ||
exampleFunction: arg => `Hello, ${arg}!`, | ||
exampleDate: new Date('1776-07-04'), | ||
exampleBuffer: Buffer.from([0x04, 0x7F, 0x93, 0x38]), | ||
exampleOtherObj: basicExample1, | ||
exampleIntArray: [293, -178, 492], | ||
exampleFloatArray: [194.13489, -2890.25, -0.04281], | ||
exampleStringArray: [`What's up, doc?`, `Shiver me timbers`], | ||
exampleBooleanArray: [true, false, true], | ||
exampleFunctionArray: [arg => `Hello, ${arg}!`, arg => `Farewell, ${arg}!`], | ||
exampleDateArray: [new Date('1776-07-04'), new Date('1945-12-07')], | ||
exampleBufferArray: [Buffer.from([0x04, 0x7F, 0x93, 0x38]), Buffer.from('A string instead')], | ||
exampleOtherObjArray: [basicExample1, basicExample2] | ||
}); | ||
/** Test if user is an instance of DatabaseRecord */ | ||
console.log(ezobjects.instanceOf(user, `DatabaseRecord`)); | ||
/** Output the instance to console */ | ||
console.log(fullExample); | ||
/** Self-executing async wrapper so we can await results */ | ||
(async () => { | ||
try { | ||
/** Create table if it doesn`t already exist */ | ||
await ezobjects.createTable(db, configUser); | ||
/** Output one of the function responses to console */ | ||
console.log(fullExample.exampleFunctionArray()[1]('Rich')); | ||
/** Insert user into the database */ | ||
await user.insert(db); | ||
/** Log user (should have automatically incremented ID now) */ | ||
console.log(user); | ||
/** Change the property values a bit */ | ||
user.checkingBalance(50.27); | ||
user.firstName(`Richard`); | ||
user.favoriteDay(new Date(`09-01-2019`)); | ||
/** Update user in the database */ | ||
await user.update(db); | ||
/** Log user (should have `checkingBalance` of 50.27) */ | ||
console.log(user); | ||
/** Create another user */ | ||
const anotherUser = new User(); | ||
/** Assuming ID of last user was 1, load record from database */ | ||
await anotherUser.load(db, 1); | ||
/** Log anotherUser */ | ||
console.log(anotherUser); | ||
/** Delete the user from the database */ | ||
await anotherUser.delete(db); | ||
} catch ( err ) { | ||
console.log(err.message); | ||
} finally { | ||
/** Close database connection */ | ||
db.close(); | ||
} | ||
})(); | ||
/** Try to set a `float` property with a `string` */ | ||
try { | ||
/** | ||
* This throws a TypeError since string given, not float; the same behavior | ||
* can be expected for all other types, including arrays of types. | ||
*/ | ||
fullExample.exampleFloat('test'); | ||
} catch ( err ) { | ||
/** Output error message to console */ | ||
console.log(err.message); | ||
} | ||
``` | ||
@@ -260,32 +129,32 @@ | ||
``` | ||
true | ||
User { | ||
_id: 1, | ||
_username: `richlowe`, | ||
_firstName: `Rich`, | ||
_lastName: `Lowe`, | ||
_checkingBalance: 4.32, | ||
_permissions: [ 1, 3, 5 ], | ||
_favoriteDay: 2018-01-01T06:00:00.000Z } | ||
User { | ||
_id: 1, | ||
_username: `richlowe`, | ||
_firstName: `Richard`, | ||
_lastName: `Lowe`, | ||
_checkingBalance: 50.27, | ||
_permissions: [ 1, 3, 5 ], | ||
_favoriteDay: 2019-09-01T05:00:00.000Z } | ||
User { | ||
_id: 1, | ||
_username: `richlowe`, | ||
_firstName: `Richard`, | ||
_lastName: `Lowe`, | ||
_checkingBalance: 50.27, | ||
_permissions: [ 1, 3, 5 ], | ||
_favoriteDay: 2019-09-01T05:00:00.000Z } | ||
BasicExample { _name: 'Basic Example 1' } | ||
FullExample { | ||
_name: 'Full Example', | ||
_exampleInt: 293, | ||
_exampleFloat: 194.13489, | ||
_exampleString: 'What\'s up, doc?', | ||
_exampleBoolean: true, | ||
_exampleFunction: [Function: exampleFunction], | ||
_exampleDate: 1776-07-04T00:00:00.000Z, | ||
_exampleBuffer: <Buffer 04 7f 93 38>, | ||
_exampleOtherObj: BasicExample { _name: 'Basic Example 1' }, | ||
_exampleIntArray: [ 293, -178, 492 ], | ||
_exampleFloatArray: [ 194.13489, -2890.25, -0.04281 ], | ||
_exampleStringArray: [ 'What\'s up, doc?', 'Shiver me timbers' ], | ||
_exampleBooleanArray: [ true, false, true ], | ||
_exampleFunctionArray: [ [Function], [Function] ], | ||
_exampleDateArray: [ 1776-07-04T00:00:00.000Z, 1945-12-07T00:00:00.000Z ], | ||
_exampleBufferArray: | ||
[ <Buffer 04 7f 93 38>, | ||
<Buffer 41 20 73 74 72 69 6e 67 20 69 6e 73 74 65 61 64> ], | ||
_exampleOtherObjArray: | ||
[ BasicExample { _name: 'Basic Example 1' }, | ||
BasicExample { _name: 'Basic Example 2' } ] } | ||
Farewell, Rich! | ||
FullExample.exampleFloat(string): Invalid signature, requires 'float'. | ||
``` | ||
## Basic EZ Object Method Signatures | ||
## EZ Object Method Signatures | ||
These are the object method signatures even the most basic of EZ Objects will have: | ||
These are the object method signatures that all of your EZ Objects will have, though note that you can always add other functionality by adding to the object prototype: | ||
@@ -315,49 +184,14 @@ ### new MyObject([data]) | ||
* **Returns:** this | ||
* **Description:** Set the value of the property, throwing an error if the javascript data type does not match the configuration, this is how the strict typing is implemented. This signature returns `this` to allow for set call chaining. | ||
* **Description:** Set the value of the property, throwing a `TypeError` if the javascript data type does not match the configuration, this is how the strict typing is implemented. This signature returns `this` to allow for set call chaining. | ||
## MySQL EZ Object Method Signatures | ||
These are the object method signatures that will additionally be provided if your configuration contains a `tableName`, | ||
meaning it's intended to be linked to a MySQL table: | ||
### MyObject.delete(db) | ||
* **Parameter:** db - `MySQLConnection` | ||
* **Description:** Delete the record in database `db`, table `tableName`, that has its `id` field equal to the `id` property of this object. | ||
### MyObject.insert(db) | ||
* **Parameter:** db - `MySQLConnection` | ||
* **Description:** Insert this object's property values into the database `db`, table `tableName`, and store the resulting insertId in the `id` property of this object. | ||
### MyObject.load(db, id) | ||
* **Parameter:** db - `MySQLConnection` | ||
* **Parameter:** id number The value of the `id` property of the record you wish to load | ||
* **Description:** Load the record in database `db`, table `tableName`, that has its `id` field equal to provided `id` parameter. | ||
### MyObject.load(db, fieldValue) | ||
* **Parameter:** db - `MySQLConnection` | ||
* **Parameter:** fieldValue - `mixed` - The value of the `stringSearchField` property of the record you wish to load | ||
* **Description:** Load the record in database `db`, table `tableName`, that has its `stringSearchField` field equal to provided `fieldValue` parameter. Here, the actual field name of `stringSearchField` is provided in the object configuration, see the configuration section below. | ||
### MyObject.load(url) | ||
* **Parameter:** url - `string` - The URL of a back-end that provides JSON data compatible with this object's initializer | ||
* **Description:** Load the JSON-encoded data obtained from `url` using this object's initializer. | ||
* **Note:** This signature is useful only when your classes are standalone browserify'd and requires you to implement a backend at `url` that will output the JSON. This signature also requires you have jQuery loaded prior to use. | ||
### MyObject.update(db) | ||
* **Parameter:** db - `MySQLConnection` | ||
* **Description:** Update the record in database `db`, table `tableName`, with its `id` field equal to the `id` property of this object, using this object's property values. | ||
## Module Exports | ||
The EZ Objects module exports two functions and a MySQL class object: | ||
The EZ Objects module exports two functions: | ||
### ezobjects.createTable(db, objectConfig) | ||
A function that creates a MySQL table corresponding to the configuration outlined in `objectConfig`, if it doesn't already exist | ||
### ezobjects.createClass(objectConfig) | ||
* **Description:** A function that creates an ES6 class corresponding to the configuration outlined in `objectConfig`, with constructor, initializer, getters, setters. | ||
### ezobjects.createObject(objectConfig) | ||
A function that creates an ES6 class corresponding to the configuration outlined in `objectConfig`, with constructor, initializer, getters, setters, and also delete, insert, load, and update if `tableName` is configured | ||
### ezobjects.MySQLConnection(mysqlConfig) | ||
A MySQL database connection class that wraps the standard mysql object and provides it with async/await functionality and transaction helpers | ||
### ezobjects.instanceOf(obj, constructorName) | ||
* **Description:** A helper function for testing whether `obj` is an descendant of a constructor `constructorName`. | ||
## Configuration Specifications | ||
@@ -367,59 +201,28 @@ | ||
### A basic object configuration can have the following: | ||
### An object configuration can have the following: | ||
* **className** - `string` - (required) Name of the class | ||
* **properties** - `Array` - (required) An array of property configurations that the object (and MySQL table, if applicable) should have corresponding properties for | ||
* **properties** - `Array` - (required) An array of property configurations that the object should have corresponding properties for | ||
* **extends** - `mixed` - (optional) The object that the new object should be extended from \[required to extend object] | ||
### A MySQL object configuration can also have the following: | ||
### A property configuration can have the following: | ||
* **extendsConfig** - `object` - (optional) The EZ Object configuration for the object that is being extended from \[required to extend object for use with MySQL table link] | ||
* **tableName** - `string` - (optional) Provide if object should be linked with MySQL database table | ||
* **stringSearchField** - `string` - (optional) The name of a unique property of type `string` that you want to be able to load with as an alternative to `id` | ||
* **indexes** - `Array` - (optional) An array of MySQL index configurations that should be created in the MySQL table | ||
### A basic property configuration can have the following: | ||
* **name** - `string` - (required) Name of the property, must conform to both JavaScript and MySQL rules | ||
* **type** - `string` - (optional) JavaScript data type, or types if separated by the pipe `|` character, that the property must be equal to -- types can be `string`, `number`, `boolean`, `function`, `Array`, or any valid object constructor name \[either **type** and/or **instanceOf** is required] | ||
* **name** - `string` - (required) Name of the property, must conform to JavaScript rules | ||
* **type** - `string` - (optional) JavaScript data type, or types if separated by the pipe `|` character, that the property must be equal to -- types can be `int`, `float`, `string`, `boolean`, `date`, `buffer`, `function`, any other valid object constructor name, or `Array` where `arrayOf` is provided with information about the array element types. \[either **type** and/or **instanceOf** is required] | ||
* **instanceOf** - `string` - (optional) JavaScript class constructor name, or names if separated by the pipe `|` character, that the property must be an instance of \[either **type** and/or **instanceOf** is required] | ||
* **default** - `mixed` - (optional) Sets the default value for the property in the class object | ||
* **initTransform** - `function` - (optional) Function that transforms and returns the property value prior to initializing (does not affect ezobjects defaults or custom defaults) | ||
* **getTransform** - `function` - (optional) Function that transforms and returns the property value prior to getting | ||
* **setTransform** - `function` - (optional) Function that transforms and returns the property value prior to setting | ||
* **arrayOf** - `string` - (required for type `array`) A plain object containing he EZ Object `type` or `instanceOf` of the elements of the array -- types can be `int`, `float`, `string`, `boolean`, `date`, `buffer`, `function`, any other valid object constructor name | ||
* **initTransform(x)** - `function` - (optional) Function that transforms and returns the property value prior to initializing (does not affect ezobjects defaults or custom defaults) | ||
* **getTransform(x)** - `function` - (optional) Function that transforms and returns the property value prior to getting | ||
* **setTransform(x)** - `function` - (optional) Function that transforms and returns the property value prior to setting | ||
### A MySQL property configuration can also have the following: | ||
### Default intiailizations for different EZ Object types | ||
* **mysqlType** - `string` - (optional) MySQL data type for the property \[required for MySQL table association] | ||
* **length** - `number` - (optional) MySQL data length for the property \[required for MySQL table association on some data types like VARCHAR] | ||
* **decimals** - `number` - (optional) Number of decimals that should be provided for certain data types when SELECT'ed from the MySQL table | ||
* **primary** - `boolean` - (optional) Indicates the property is a PRIMARY KEY in the MySQL table \[required for MySQL table association on at least one property in the table] | ||
* **unique** - `boolean` - (optional) Indicates the property is a UNIQUE KEY in the MySQL table | ||
* **null** - `boolean` - (optional) Indicates the property can be NULL \[default is properties must be NOT NULL] | ||
* **mysqlDefault** - `mixed` - (optional) Sets the default value for the property in the MySQL table, assuming its of the correct type | ||
* **unsigned** - `boolean` - (optional) Indicates the property should be unsigned in the MySQL table | ||
* **zerofill** - `boolean` - (optional) Indicates the property should be zero-filled in the MySQL table | ||
* **comment** - `string` - (optional) Indicates the property should note the provided comment in the MySQL table | ||
* **charsetName** - `string` - (optional) Indicates the property should use the provided charset in the MySQL table | ||
* **collationName** - `string` - (optional) Indicates the property should use the provided collation in the MySQL table | ||
* **autoIncrement** - `boolean` - (optional) Indicates the property should be auto-incremented in the MySQL table | ||
* **saveTransform** - `function` - (optional) Function that transforms and returns the property value prior to saving in the database | ||
* **loadTransform** - `function` - (optional) Function that transforms and returns the property value after loading from the database | ||
### A MySQL index configuration can have the following (for MySQL table association only): | ||
* **name** - `string` - (required) Name of the index, can be arbitrary, but must be unique and not PRIMARY | ||
* **columns** - `Array` - (required) An array of strings containing property names to be indexed | ||
* **type** - `string` - (optional) Index type, can be BTREE or HASH, defaults to BTREE | ||
* **keyBlockSize** - `number` - (optional) Indicates the index should use the provided key block size | ||
* **withParser** - `string` - (optional) Indicates the index should use the provided parser | ||
* **visible** - `boolean` - (optional) Indicates the index should be visible | ||
* **invisible** - `boolean` - (optional) Indicates the index should be invisible | ||
### Default intiailizations for different JavaScript types | ||
* `number` - 0 | ||
* `int` - 0 | ||
* `double` - 0 | ||
* `string` - `` | ||
* `boolean` - false | ||
* `function` - function () { } | ||
* `Date` - new Date(0) | ||
* `Buffer` - Buffer.from([]) | ||
* `Array` - [] | ||
@@ -426,0 +229,0 @@ * Everything else - null |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
0
2
335614
12
1567
234
- Removedmoment@^2.22.1
- Removedmysql@^2.15.0
- Removedbignumber.js@9.0.0(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removedinherits@2.0.4(transitive)
- Removedisarray@1.0.0(transitive)
- Removedmoment@2.30.1(transitive)
- Removedmysql@2.18.1(transitive)
- Removedprocess-nextick-args@2.0.1(transitive)
- Removedreadable-stream@2.3.7(transitive)
- Removedsafe-buffer@5.1.2(transitive)
- Removedsqlstring@2.3.1(transitive)
- Removedstring_decoder@1.1.1(transitive)
- Removedutil-deprecate@1.0.2(transitive)