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 1.1.5 to 2.0.0

example-mysql.js

12

example.js
const ezobjects = require('./index');
/** Create a customized object on the global (node) or window (browser) namespace */
ezobjects({
name: 'DatabaseRecord',
ezobjects.createObject({
className: 'DatabaseRecord',
properties: [
{ name: 'id', type: 'int' }
{ name: 'id', type: 'number', setTransform: x => parseInt(x) }
]

@@ -17,4 +17,4 @@ });

/** Create another customized object that extends the first one */
ezobjects({
name: 'Person',
ezobjects.createObject({
className: 'Person',
extends: DatabaseRecord,

@@ -24,3 +24,3 @@ properties: [

{ name: 'lastName', type: 'string' },
{ name: 'checkingBalance', type: 'float' },
{ name: 'checkingBalance', type: 'number', setTransform: x => parseFloat(x) },
{ name: 'permissions', type: 'Array' },

@@ -27,0 +27,0 @@ { name: 'favoriteDay', type: 'Date' }

@@ -0,1 +1,3 @@

const mysqlConnection = require('./mysql-connection');
/**

@@ -5,5 +7,198 @@ * @module ezobjects

* @license MIT
* @description Easy automatic class creation using simple configuration objects. Capable
* of automatically creating a matching MySQL table and generating insert(), load(), and
* update() methods in addition to the constructor, initializer, and getters/setters for
* all configured properties.
*
* @signature createTable(db, obj)
* @module ezobjects
* @param db MySQLConnection
* @param obj Object
* @descrtiption Create a MySQL table with the specifications outlined in `obj`, if it doesn't already exist.
*/
module.exports.createTable = async (db, obj) => {
/** 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`];
const mysqlTypesWithDecimals = [`REAL`, `DOUBLE`, `FLOAT`, `DECIMAL`, `NUMERIC`];
const mysqlTypesWithLengthRequiringDecimals = [`REAL`, `DOUBLE`, `FLOAT`];
const mysqlTypesWithUnsignedAndZerofill = [`TINYINT`, `SMALLINT`, `MEDIUMINT`, `INT`, `INTEGER`, `BIGINT`, `REAL`, `DOUBLE`, `FLOAT`,
`DECIMAL`, `NUMERIC`];
const mysqlTypesWithCharacterSetAndCollate = [`CHAR`, `VARCHAR`, `TINYTEXT`, `TEXT`, `MEDIUMTEXT`, `LONGTEXT`, `ENUM`, `SET`];
/** 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);
/** Loop through each property */
obj.properties.forEach((property) => {
/** Convert the type to upper case for reliable string comparison */
property.mysqlType = property.mysqlType.toUpperCase();
/** Add property name and type to query */
createQuery += `${property.name} ${property.mysqlType}`;
/** 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.type) && !isNaN(property.length) && isNaN(property.decimals) )
throw new Error(`Property of type REAL, DOUBLE, or FLOAT used with length, but without decimals.`);
/** 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})`;
/** Properties with UNSIGNED */
if ( property.unsigned && mysqlTypesWithUnsignedAndZerofill.includes(property.mysqlType) )
createQuery += ` UNSIGNED`;
/** Properties with ZEROFILL */
if ( property.zerofill && mysqlTypesWithUnsignedAndZerofill.includes(property.mysqlType) )
createQuery += ` ZEROFILL`;
/** Properties with CHARACTER SET */
if ( property.charsetName && mysqlTypesWithCharacterSetAndCollate.includes(property.mysqlType) )
createQuery += ` CHARACTER SET ${property.charsetName}`;
/** Properties with COLLATE */
if ( property.collationName && mysqlTypesWithCharacterSetAndCollate.includes(property.mysqlType) )
createQuery += ` COLLATE ${property.collationName}`;
/** Properties with NULL */
if ( property.null )
createQuery += ` NULL`;
else
createQuery += ` NOT NULL`;
/** Properties with DEFAULT */
if ( property.default )
createQuery += ` DEFAULT ${property.default}`;
/** Properties with AUTO_INCREMENT */
if ( property.autoIncrement )
createQuery += ` AUTO_INCREMENT`;
/** Properties with UNIQUE KEY */
if ( property.unique )
createQuery += ` UNIQUE`;
/** 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 createObject(obj)
* @module ezobjects
* @param obj Object Configuration object
* @description Easy, automatic object creation from simple templates with strict typing
*/
module.exports = (obj) => {
module.exports.createObject = (obj) => {
/** Create default transform function that doesn't change the input */
const defaultTransform = x => x;
let parent;

@@ -16,6 +211,6 @@

parent = global;
/** Create new class on global scope */
parent[obj.name] = class extends (obj.extends || Object) {
/** Constructor for new object. */
parent[obj.className] = class extends (obj.extends || Object) {
/** Create constructor */
constructor(data = {}) {

@@ -27,38 +222,45 @@ super(data);

/** Initializer */
/** Create initializer */
init(data = {}) {
/** If there is an 'init' function on super, call it */
if ( typeof super.init === 'function' )
super.init(data);
/** If data is a string, assume it's JSON encoded and parse */
if ( typeof data == 'string' )
data = JSON.parse(data);
/** Loop through each key/val pair in data */
Object.keys(data).forEach((key) => {
/** If key begins with '_' */
if ( key.match(/^_/) ) {
/** Create a new key with the '_' character stripped from the beginning */
Object.defineProperty(data, key.replace(/^_/, ''), Object.getOwnPropertyDescriptor(data, key));
/** Delete the old key that has '_' */
delete data[key];
}
});
/** Loop through each property in the obj */
obj.properties.forEach((col) => {
/** Initialize 'int' and 'float' types to zero */
if ( col.type == 'int' || col.type == 'float' )
this[col.name](data[col.name] || col.default || 0);
obj.properties.forEach((property) => {
/** Initialize 'number' types to zero */
if ( property.type == 'number' )
this[property.name](data[property.name] || property.default || 0);
/** Initialize 'boolean' types to false */
else if ( col.type == 'boolean' )
this[col.name](data[col.name] || col.default || false);
else if ( property.type == 'boolean' )
this[property.name](data[property.name] || property.default || false);
/** Initialize 'string' types to empty */
else if ( col.type == 'string' )
this[col.name](data[col.name] || col.default || '');
else if ( property.type == 'string' )
this[property.name](data[property.name] || property.default || '');
/** Initialize 'Array' types to empty */
else if ( col.type == 'Array' )
this[col.name](data[col.name] || col.default || []);
else if ( property.type == 'Array' )
this[property.name](data[property.name] || property.default || []);
/** Initialize all other types to null */
else
this[col.name](data[col.name] || col.default || null);
this[property.name](data[property.name] || property.default || null);
});

@@ -69,18 +271,34 @@ }

/** Loop through each property in the obj */
obj.properties.forEach((col) => {
/** For 'int' type properties */
if ( col.type == 'int' ) {
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;
/** For 'number' type properties */
if ( property.type == 'number' ) {
/** Create class method on prototype */
parent[obj.name].prototype[col.name] = function (arg) {
parent[obj.className].prototype[property.name] = function (arg) {
/** Getter */
if ( arg === undefined )
return this[`_${col.name}`];
return property.getTransform(this[`_${property.name}`]);
/** Setter */
else if ( typeof arg == 'number' )
this[`_${col.name}`] = parseInt(arg);
this[`_${property.name}`] = property.setTransform(arg);
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.${col.name}(${typeof arg}): Invalid signature.`);
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature.`);

@@ -90,40 +308,19 @@ /** Return this object for set call chaining */

};
}
}
/** For 'float' type properties */
else if ( col.type == 'float' ) {
/** Create class method on prototype */
parent[obj.name].prototype[col.name] = function (arg) {
/** Getter */
if ( arg === undefined )
return this[`_${col.name}`];
/** Setter */
else if ( typeof arg == 'number' )
this[`_${col.name}`] = parseFloat(arg);
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.${col.name}(${typeof arg}): Invalid signature.`);
/** Return this object for set call chaining */
return this;
};
}
/** For 'boolean' type properties */
else if ( col.type == 'boolean' ) {
else if ( property.type == 'boolean' ) {
/** Create class method on prototype */
parent[obj.name].prototype[col.name] = function (arg) {
parent[obj.className].prototype[property.name] = function (arg) {
/** Getter */
if ( arg === undefined )
return this[`_${col.name}`];
return property.getTransform(this[`_${property.name}`]);
/** Setter */
else if ( typeof arg == 'boolean' )
this[`_${col.name}`] = arg;
this[`_${property.name}`] = property.setTransform(arg);
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.${col.name}(${typeof arg}): Invalid signature.`);
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature.`);

@@ -136,16 +333,16 @@ /** Return this object for set call chaining */

/** For 'string' type properties */
else if ( col.type == 'string' ) {
else if ( property.type == 'string' ) {
/** Create class method on prototype */
parent[obj.name].prototype[col.name] = function (arg) {
parent[obj.className].prototype[property.name] = function (arg) {
/** Getter */
if ( arg === undefined )
return this[`_${col.name}`];
return property.getTransform(this[`_${property.name}`]);
/** Setter */
else if ( typeof arg == 'string' )
this[`_${col.name}`] = arg;
this[`_${property.name}`] = property.setTransform(arg);
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.${col.name}(${typeof arg}): Invalid signature.`);
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature.`);

@@ -158,16 +355,16 @@ /** Return this object for set call chaining */

/** For 'Array' type properties */
else if ( col.type == 'Array' ) {
else if ( property.type == 'Array' ) {
/** Create class method on prototype */
parent[obj.name].prototype[col.name] = function (arg) {
parent[obj.className].prototype[property.name] = function (arg) {
/** Getter */
if ( arg === undefined )
return this[`_${col.name}`];
return property.getTransform(this[`_${property.name}`]);
/** Setter */
else if ( typeof arg == 'object' && arg.constructor.name == col.type )
this[`_${col.name}`] = arg;
else if ( typeof arg == 'object' && arg.constructor.name == property.type )
this[`_${property.name}`] = property.setTransform(arg);
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.${col.name}(${typeof arg}): Invalid signature.`);
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature.`);

@@ -182,14 +379,14 @@ /** Return this object for set call chaining */

/** Create class method on prototype */
parent[obj.name].prototype[col.name] = function (arg) {
parent[obj.className].prototype[property.name] = function (arg) {
/** Getter */
if ( arg === undefined )
return this[`_${col.name}`];
return property.getTransform(this[`_${property.name}`]);
/** Setter */
else if ( arg === null || ( typeof arg == 'object' && arg.constructor.name == col.type ) )
this[`_${col.name}`] = arg;
else if ( arg === null || ( typeof arg == 'object' && arg.constructor.name == property.type ) )
this[`_${property.name}`] = property.setTransform(arg);
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.${col.name}(${typeof arg}): Invalid signature.`);
throw new TypeError(`${this.constructor.name}.${property.name}(${typeof arg}): Invalid signature.`);

@@ -200,2 +397,246 @@ /** Return this object for set call chaining */

}
if ( typeof obj.tableName == 'string' ) {
/** Create MySQL insert method on prototype */
parent[obj.className].prototype.insert = async function (db) {
/** If the argument is a valid database, insert record into database and capture ID */
if ( typeof db == 'object' && db.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;
/** 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;
/** 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;
/** 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 db.query(query, params);
/** Store the resulting insert ID */
this.id(result.insertId);
}
/** Otherwise throw TypeError */
else {
throw new TypeError(`${this.constructor.name}.insert(${typeof db}): Invalid signature.`);
}
/** Allow for call chaining */
return this;
};
/** Create MySQL load method on prototype */
parent[obj.className].prototype.load = async function (arg1, arg2) {
/** If the first argument is a valid database and the second is a number, load record from database by ID */
if ( typeof arg1 == 'object' && arg1.constructor.name == 'MySQLConnection' && typeof arg2 == 'number' ) {
/** 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) => {
/** 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} `;
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) => {
/** 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' ) ) {
/** Loop through each property */
obj.properties.forEach((property) => {
/** Append property in object */
this[property.name](arg1[property.name]);
});
}
/** 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 (db) {
/** If the argument is a valid database, update database record */
if ( typeof db == 'object' && db.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;
/** 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;
/** 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 db.query(query, params);
}
/** Otherwise throw TypeError */
else {
throw new TypeError(`${this.constructor.name}.update(${typeof db}): Invalid signature.`);
}
/** Allow for call chaining */
return this;
};
}
});

@@ -207,3 +648,6 @@

*/
Object.defineProperty(parent[obj.name], 'name', { value: obj.name });
Object.defineProperty(parent[obj.className], 'name', { value: obj.className });
}
/** Re-export MySQLConnection */
module.exports.MySQLConnection = mysqlConnection.MySQLConnection;
{
"name": "ezobjects",
"version": "1.1.5",
"version": "2.0.0",
"description": "Easy dynamic object generation with strict typing and set chaining",
"main": "index.js",
"scripts": {
"start": "node example.js",
"test": "eslint example.js index.js"
"start": "node example-mysql.js",
"test": "eslint example.js example-mysql.js index.js mysql-connection.js"
},

@@ -24,3 +24,7 @@ "repository": {

"author": "Rich Lowe",
"license": "MIT"
"license": "MIT",
"dependencies": {
"moment": "^2.22.1",
"mysql": "^2.15.0"
}
}

@@ -1,106 +0,175 @@

# EZ Objects v1.1.5
# EZ Objects v2.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 the initializers, property getters, and property setters for your data model objects. The library
takes the form of a single function that can be passed a generic object containing a few configuration keys,
defined below, that will allow it to automatically generate a new ES6 class object with the following features:
writing class objects. All you have to do is create simple configurations for each of your objects and then call
the library function(s). Let's start by showing a basic example:
Auto-initializes all properties (including parent object properties, if extended)
* Default default values for different data types are listed further below
* You can specify the default value for a property in the config when you create the EZ Object
* You can pass a value that the property should have when instansiating the EZ Object
* If the object has been JSON encoded and parsed, the properties will have the internal property
underscores `_`, fortunately the module will strip the `_` from the object if you wish to initialize
a new object using a JSON encoded/parsed version of the same object
## Basic Example
Automatically creates methods that perform getter/setter functionality with strict data typing
* Methods use myMethod() for getter and myMethod(val) for setter
* Methods throw TypeError if type does not match that specified
* Methods return 'this' when setting so set calls can be chained
```javascript
const ezobjects = require('ezobjects');
See the examples below to witness them used in a variety of situations.
/** Create a customized object on the global (node) or window (browser) namespace */
ezobjects.createObject({
className: 'DatabaseRecord',
properties: [
{ name: 'id', type: 'number', setTransform: x => parseInt(x) }
]
});
## Status
const record = new DatabaseRecord();
```
Fully operational! Please open an issue for any bug reports or feature requests.
In this short snippet, we have effectively created a new class named `DatabaseRecord`. The class
has a constructor, an initializer, and a getter/setter method for `id`. The constructor calls the
initializer (named `init`), which sets the value of `id` to the default for type **number**, being
zero in this case. You could also explicitly pass a default by adding a `default` key to the property,
should you so desire. We've also added a transform function that will take any value passed to the `id`
setter and apply parseInt() to it.
## Principles of Operation
## MySQL Example w/ Extended Object
This module, when required, is a function that takes a single object argument. At present, that object can have the
following keys:
```javascript
const ezobjects = require('ezobjects');
const fs = require('fs');
const moment = require('moment');
* name - A string containing the name of the desired class object (required)
* extends - An object that you wish the class to extend from (optional, note this is the class itself, not the name)
* properties - An array of properties for which the class will have getters/setters/initialization implemented (optional)
Each property in the properties array is an object that can have the following keys:
/** Load external MySQL configuration */
const configMySQL = JSON.parse(fs.readFileSync('mysql-config.json'));
* name - The name of the property (required)
* type - The type of the property (required, can be string, int, float, boolean, Array, or any other object name)
* default - The default initialized value (optional)
/** Connect to the MySQL database using our MySQL module async/await wrapper */
const db = new ezobjects.MySQLConnection(configMySQL);
Default defaults are:
/**
* Configure a new EZ Object called DatabaseRecord with one 'id' property that
* contains extended MySQL configuration settings.
*/
const configDatabaseRecord = {
className: 'DatabaseRecord',
properties: [
{
name: 'id',
type: 'number',
mysqlType: 'int',
autoIncrement: true,
primary: true,
setTransform: x => parseInt(x)
}
]
};
* string - ''
* int - 0
* float - 0
* boolean - false
* Array - []
* Any others - null
/** Create the DatabaseRecord object */
ezobjects.createObject(configDatabaseRecord);
Note that the created objects are added to the global space, being `global` (node) or `window` (browser), though you'll
have to browserify or equivalent to use in browser. Like normal classes, they can have other properties/methods added
externally using the prototype, though note that if you want external prototype-added properties to be initialized, you'll
have to rewrite the init() function manually. Alternatively, you can just extend the class and init the parent with
`super`, see examples below.
/**
* Configure a new EZ Object called Person that extends from the DatabaseRecord
* object and adds several additional properties and a MySQL index.
*/
const configPerson = {
tableName: 'people',
className: 'Person',
extends: DatabaseRecord,
extendsConfig: configDatabaseRecord,
properties: [
{
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(',')
},
{
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' ] }
]
};
## Examples
/** Create the Person object */
ezobjects.createObject(configPerson);
#### Creating a class
```javascript
const ezobjects = require('ezobjects');
/** Create a customized object on the global (node) or window (browser) namespace */
ezobjects({
name: 'DatabaseRecord',
properties: [
{ name: 'id', type: 'int' }
]
/** Create new person, initializing with object passed to constructor */
const person = new Person({
firstName: 'Rich',
lastName: 'Lowe',
checkingBalance: 4.32,
permissions: [1, 3, 5],
favoriteDay: new Date('01-01-2018')
});
/** Example of the object newly instansiated */
const a = new DatabaseRecord();
console.log(a);
/** Self-executing async wrapper so we can await results */
(async () => {
/** Create table if it doesn't already exist */
await ezobjects.createTable(db, configPerson);
/** Insert person into the database */
await person.insert(db);
/** Log person (should have automatically incremented ID now) */
console.log(person);
/** Close database connection */
db.close();
})();
```
#### Output
### Expected Output
```
DatabaseRecord { _id: 0 }
Person {
_id: 8,
_firstName: 'Rich',
_lastName: 'Lowe',
_checkingBalance: 4.32,
_permissions: [ 1, 3, 5 ],
_favoriteDay: 2018-01-01T06:00:00.000Z }
```
#### Creating a class that's extended from another class
In this snippet, we've created two classes, DatabaseRecord and Person. Person extends DatabaseRecord and is also associated with
a MySQL table called `people`. Each property is given a JavaScript `type` and a MySQL `mysqlType`. Additional MySQL property
configuration options can be provided, which are outlined in more detail below. A BTREE index is also added on the lastName column
for faster searching. While not required, the moment library is used to help translate date formats between MySQL and JavaScript.
Since the Person class configuration provided a `tableName` property, it will automatically have additional methods created that are
not present in a basic EZ Object. The additional methods are insert(db), load(db, id), and update(db). These methods can be used to
insert the object properties as a new MySQL record, load a MySQL record into the object properties, or update an existing MySQL record
with the object properties. Transforms can be used to adjust the values properties when they are get or set in the object, or when they
are saved or loaded from the database.
## Various Uses of EZ Objects
### Constructor Default
```javascript
/** Create another customized object that extends the first one */
ezobjects({
name: 'Person',
extends: DatabaseRecord,
properties: [
{ name: 'firstName', type: 'string' },
{ name: 'lastName', type: 'string' },
{ name: 'checkingBalance', type: 'float' },
{ name: 'permissions', type: 'Array' },
{ name: 'favoriteDay', type: 'Date' }
]
});
const a = new Person();
/** Example of the extended object newly instansiated */
const b = new Person();
console.log(b);
console.log(a);
```
#### Output
### Expected Output

@@ -117,19 +186,18 @@ ```

#### Using an intializer object passed to constructor
### Using Initializer Object
```javascript
/** Example of the extended object instansiated and initialized using object passed to constructor */
const c = new Person({
const b = new Person({
id: 1,
firstName: 'Rich',
lastName: 'Lowe',
checkingBalance: 4.87,
permissions: [1, 2, 3],
checkingBalance: 4.32,
permissions: [1, 3, 5],
favoriteDay: new Date('01-01-2018')
});
console.log(c);
console.log(b);
```
#### Output
### Expected Output

@@ -141,24 +209,23 @@ ```

_lastName: 'Lowe',
_checkingBalance: 4.87,
_permissions: [ 1, 2, 3 ],
_checkingBalance: 4.32,
_permissions: [ 1, 4, 5 ],
_favoriteDay: 2018-01-01T06:00:00.000Z }
```
#### Using the auto-created setters
### Using Auto-generated Setters
```javascript
/** Example of the extended object instansiated, then loaded with data using setter methods */
const d = new Person();
const c = new Person();
d.id(2);
d.firstName('Bert');
d.lastName('Reynolds');
d.checkingBalance(91425518.32);
d.permissions([1, 4]);
d.favoriteDay(new Date('06-01-2017'));
c.id(2);
c.firstName('Bert');
c.lastName('Reynolds');
c.checkingBalance(91425518.32);
c.permissions([1, 4]);
c.favoriteDay(new Date('06-01-2017'));
console.log(d);
console.log(c);
```
#### Output
### Expected Output

@@ -175,15 +242,14 @@ ```

#### Using the auto-created getters
### Using Auto-generated Getters
```javascript
/** 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()}`);
console.log(`ID: ${c.id()}`);
console.log(`First Name: ${c.firstName()}`);
console.log(`Last Name: ${c.lastName()}`);
console.log(`Checking Balance: $${c.checkingBalance()}`);
console.log(`Permissions: ${c.permissions().join(`, `)}`);
console.log(`Favorite Day: ${c.favoriteDay().toString()}`);
```
#### Output
### Expected Output

@@ -199,102 +265,53 @@ ```

#### Adding properties by using the class prototype
## Module Specification
```javascript
/** Adding property to the generated object's prototype */
DatabaseRecord.prototype.table = function (arg) {
/** Getter */
if ( arg === undefined )
return this._table;
/** 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;
};
### The module has three exports:
/** 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 || '');
};
**ezobjects.createTable(db, obj)**
* Creates a MySQL table corresponding to the configuration outlined in `obj`, if it doesn't already exist
const e = new DatabaseRecord();
**ezobjects.createObject(obj)**
* Creates an ES6 class corresponding to the configuration outlined in `obj`, with constructor/init/getters/setters, and insert/load/update if `tableName` is configured
console.log(e);
```
**ezobjects.MySQLConnection(config)**
* A MySQL database connection wrapper that uses the standard mysql package and wraps it with async/await and transaction helpers
#### Output
### An object configuration can have the following:
```
DatabaseRecord { _id: 0, _table: '' }
```
* tableName - string - (optional) Provide if object should be linked with MySQL database table
* className - string - (required) Name of the class
* extends - object - (optional) The object that the new object should be extended from [required to extend object]
* extendsConfig - object - (optional) The EZ Object configuration for the object that is being extended from [required to extend object]
* properties - Array - (required) An array of properties that the object (and MySQL table, if applicable) should contain
* indexes - Array - (optional) An array of indexes that should be created in the MySQL table, if applicable
#### Adding capability other than properties by using the class prototype
### A property configuration can have the following:
```javascript
/** Adding arbitrary capability other than property to the generated object's prototype */
DatabaseRecord.prototype.hello = function () {
return "Hello, World!";
};
* name - string - (required) Name of the property, must conform to both JavaScript and MySQL rules
* type - string - (required) JavaScript data type for the property
* 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]
* default - 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
* 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
* 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
const f = new DatabaseRecord();
### An index configuration can have the following (for MySQL table association only):
console.log(f.hello());
```
#### Output
```
Hello, World!
```
#### Adding properties and/or capability by extending the class
```javascript
/** 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.toString();
/** Handle type errors */
else
throw new TypeError(`${this.constructor.name}.test(${typeof arg}): Invalid signature.`);
/** Return this object for set call chaining */
return this;
}
}
const g = new DatabaseRecord2();
console.log(g);
console.log(g.hello());
```
#### Output
```
DatabaseRecord2 { _id: 0, _table: '', _test: 'Test' }
Hello, World!
```
* name - string - (required) Name of the index, can be arbitrary, but must be unique and not PRIMARY
* 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
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