Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ezobjects

Package Overview
Dependencies
Maintainers
1
Versions
133
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ezobjects - npm Package Compare versions

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`);

@@ -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"
}
}

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc