Comparing version 1.5.2 to 1.6.0
@@ -0,1 +1,8 @@ | ||
1.6.0 / 2021-07-06 | ||
================== | ||
* feat: support class static attributes and hooks (#131) | ||
* fix: names defined in Bone.attributes should be always enumerable (#128) | ||
* chore: add quality badge to readme (#129) | ||
1.5.2 / 2021-07-02 | ||
@@ -2,0 +9,0 @@ ================== |
@@ -83,2 +83,3 @@ 'use strict'; | ||
model.describe(); | ||
model.initialize(); | ||
} | ||
@@ -85,0 +86,0 @@ } |
{ | ||
"name": "leoric", | ||
"version": "1.5.2", | ||
"version": "1.6.0", | ||
"description": "JavaScript Object-relational mapping alchemy", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
# Leoric | ||
[![Package Quality](https://packagequality.com/shield/leoric.svg)](https://packagequality.com/#?package=leoric) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/leoric.svg?style=flat)](https://www.npmjs.com/package/leoric) | ||
@@ -4,0 +5,0 @@ [![NPM Version](http://img.shields.io/npm/v/leoric.svg?style=flat)](https://www.npmjs.com/package/leoric) |
125
src/bone.js
@@ -15,3 +15,3 @@ 'use strict'; | ||
const { capitalize, camelCase, snakeCase } = require('./utils/string'); | ||
const { setupHooks } = require('./setup_hooks'); | ||
const { hookNames, setupSingleHook } = require('./setup_hooks'); | ||
const { logger } = require('./utils/index'); | ||
@@ -770,3 +770,2 @@ | ||
if (this[primaryKey] == null) { | ||
@@ -892,2 +891,23 @@ throw new Error('instance is not persisted yet.'); | ||
static normalize(attributes) { | ||
for (const name in LEGACY_TIMESTAMP_MAP) { | ||
const newName = LEGACY_TIMESTAMP_MAP[name]; | ||
if (attributes.hasOwnProperty(name) && !attributes.hasOwnProperty(newName)) { | ||
attributes[newName] = attributes[name]; | ||
delete attributes[name]; | ||
} | ||
} | ||
// if there is no primaryKey added, add it to attributes automatically | ||
if (Object.values(attributes).every(attribute => !attribute.primaryKey)) { | ||
attributes[this.primaryKey] = { | ||
type: new DataTypes.BIGINT(), | ||
allowNull: false, | ||
autoIncrement: true, | ||
primaryKey: true, | ||
columnName: snakeCase(this.primaryKey), | ||
}; | ||
} | ||
} | ||
/** | ||
@@ -902,3 +922,6 @@ * Generate attributes from column definitions. | ||
const attributeMap = {}; | ||
const table = this.table || snakeCase(pluralize(this.name)); | ||
const tableAlias = camelCase(pluralize(this.name || table)); | ||
this.normalize(attributes); | ||
for (const name of Object.keys(attributes)) { | ||
@@ -910,2 +933,11 @@ const attribute = new Attribute(name, attributes[name], options.define); | ||
const primaryKey = Object.keys(attributes).find(key => attributes[key].primaryKey); | ||
const timestamps = {}; | ||
for (const name of [ 'createdAt', 'updatedAt', 'deletedAt' ]) { | ||
if (attributes.hasOwnProperty(name)) timestamps[name] = name; | ||
if (attributes.hasOwnProperty(snakeCase(name))) { | ||
timestamps[name] = snakeCase(name); | ||
} | ||
} | ||
const descriptors = {}; | ||
@@ -921,19 +953,31 @@ for (const name in attributes) { | ||
}, | ||
enumerable: true, | ||
configurable: true, | ||
}, Object.keys(descriptor || {}).reduce((result, key) => { | ||
if (descriptor[key] != null) result[key] = descriptor[key]; | ||
return result; | ||
}, {})); | ||
}, {}), { | ||
enumerable: true, | ||
configurable: true, | ||
}); | ||
} | ||
Object.defineProperties(this.prototype, descriptors); | ||
Object.defineProperties(this, looseReadonly({ | ||
timestamps, | ||
table, | ||
primaryKey, | ||
columns, | ||
attributeMap, | ||
associations: [], | ||
tableAlias, | ||
synchronized: Object.keys(compare(attributes, columns)).length === 0, | ||
})); | ||
for (const hookName of hookNames) { | ||
if (this[hookName]) setupSingleHook(this, hookName, this[hookName]); | ||
} | ||
} | ||
/** | ||
* Placeholder static method. Sub-classes of Bone can override this method to setup model informations such as associations, attribute renamings, etc. | ||
* Override this method to setup associations, rename attributes, etc. | ||
* @deprecated use {@link Bone.initialize} instead | ||
* @example | ||
@@ -950,2 +994,14 @@ * class Post extends Bone { | ||
/** | ||
* Override this method to setup associations, rename attributes, etc. | ||
* @example | ||
* class Post extends Bone { | ||
* static didLoad() { | ||
* this.belongsTo('author', { className: 'User' }) | ||
* this.renameAttribute('content', 'body') | ||
* } | ||
* } | ||
*/ | ||
static initialize() {} | ||
/** | ||
* The primary key of the model, in camelCase. | ||
@@ -1427,58 +1483,19 @@ * @type {string} | ||
static init(attributes = {}, opts = {}, overrides = {}) { | ||
opts = { | ||
const { hooks, tableName: table } = { | ||
underscored: true, | ||
table: this.table || opts.tableName, | ||
tableName: this.table, | ||
hooks: {}, | ||
...(this.options && this.options.define), | ||
...opts, | ||
}; | ||
const table = opts.table || snakeCase(pluralize(this.name)); | ||
const aliasName = camelCase(pluralize(this.name || table)); | ||
for (const name in LEGACY_TIMESTAMP_MAP) { | ||
const newName = LEGACY_TIMESTAMP_MAP[name]; | ||
if (attributes.hasOwnProperty(name) && !attributes.hasOwnProperty(newName)) { | ||
attributes[newName] = attributes[name]; | ||
delete attributes[name]; | ||
} | ||
} | ||
const timestamps = {}; | ||
for (const name of [ 'createdAt', 'updatedAt', 'deletedAt' ]) { | ||
if (attributes.hasOwnProperty(name)) timestamps[name] = name; | ||
if (attributes.hasOwnProperty(snakeCase(name))) { | ||
timestamps[name] = snakeCase(name); | ||
} | ||
} | ||
const primaryKey = Object.keys(attributes).find(key => attributes[key].primaryKey); | ||
if (primaryKey && primaryKey !== this.primaryKey) { | ||
// redefine primaryKey getter | ||
Object.defineProperty(this, 'primaryKey', { | ||
value: primaryKey, | ||
writable: false, | ||
configurable: false, | ||
}); | ||
} | ||
// if there is no primaryKey added, add it to attributes automatically | ||
if (!attributes[this.primaryKey]) { | ||
attributes[this.primaryKey] = { | ||
type: new DataTypes.BIGINT(), | ||
allowNull: false, | ||
autoIncrement: true, | ||
primaryKey: true, | ||
columnName: snakeCase(this.primaryKey), | ||
}; | ||
} | ||
const customDescriptors = Object.getOwnPropertyDescriptors(overrides); | ||
Object.defineProperties(this.prototype, customDescriptors); | ||
Object.defineProperties(this, looseReadonly({ | ||
attributes, | ||
table, | ||
aliasName, | ||
associations: [], | ||
timestamps, | ||
})); | ||
setupHooks(this, opts.hooks); | ||
const hookMethods = hookNames.reduce(function(result, key) { | ||
const method = hooks[key]; | ||
if (typeof method === 'function') result[key] = method; | ||
return result; | ||
}, {}); | ||
Object.defineProperties(this, looseReadonly({ ...hookMethods, attributes, table })); | ||
} | ||
@@ -1485,0 +1502,0 @@ |
@@ -93,3 +93,3 @@ 'use strict'; | ||
const results = new Collection(); | ||
const { aliasName, table, primaryColumn, primaryKey } = Model; | ||
const { tableAlias, table, primaryColumn, primaryKey } = Model; | ||
@@ -100,3 +100,3 @@ for (const row of rows) { | ||
// Hence we need to fallback to original table name here. | ||
const main = row[aliasName] || row[table]; | ||
const main = row[tableAlias] || row[table]; | ||
let current = results.find(result => result[primaryKey] == main[primaryColumn]); | ||
@@ -140,3 +140,3 @@ | ||
const { joins } = spell; | ||
const { table, aliasName } = spell.Model; | ||
const { table, tableAlias } = spell.Model; | ||
@@ -157,3 +157,3 @@ // await Post.count() | ||
// mysql2 sometimes nests rows with table name instead of table alias | ||
const qualifier = prop == table ? aliasName : prop; | ||
const qualifier = prop == table ? tableAlias : prop; | ||
const obj = result[qualifier] || (result[qualifier] = {}); | ||
@@ -163,3 +163,3 @@ if (qualifier == '') { | ||
} | ||
else if (qualifier in joins || qualifier == aliasName) { | ||
else if (qualifier in joins || qualifier == tableAlias) { | ||
const { Model } = joins[qualifier] || spell; | ||
@@ -166,0 +166,0 @@ for (const columnName in data) { |
@@ -18,3 +18,3 @@ 'use strict'; | ||
const qualifier = qualifiers && qualifiers[0]; | ||
const Model = qualifier && qualifier != spell.Model.aliasName | ||
const Model = qualifier && qualifier != spell.Model.tableAlias | ||
? (spell.joins.hasOwnProperty(qualifier) ? spell.joins[qualifier].Model : null) | ||
@@ -366,3 +366,3 @@ : spell.Model; | ||
const { Model, columns, joins, whereConditions, orders } = spell; | ||
const baseName = Model.aliasName; | ||
const baseName = Model.tableAlias; | ||
const subspell = spell.dup; | ||
@@ -429,3 +429,3 @@ | ||
const { Model, columns, groups, whereConditions, havingConditions, orders } = spell; | ||
const baseName = Model.aliasName; | ||
const baseName = Model.tableAlias; | ||
const clarify = node => { | ||
@@ -453,3 +453,3 @@ if (node.type === 'id' && !node.qualifiers) { | ||
const { escapeId } = Model.driver; | ||
const baseName = Model.aliasName; | ||
const baseName = Model.tableAlias; | ||
const selects = new Set(); | ||
@@ -492,3 +492,3 @@ const map = {}; | ||
const { escapeId } = Model.driver; | ||
const baseName = Model.aliasName; | ||
const baseName = Model.tableAlias; | ||
@@ -649,3 +649,3 @@ const chunks = ['SELECT']; | ||
} | ||
columns = attributes.map(entry => entry.columnName); | ||
@@ -681,4 +681,4 @@ | ||
} | ||
const chunks = ['INSERT']; | ||
@@ -810,3 +810,3 @@ | ||
/** | ||
* @param {Spell} spell | ||
* @param {Spell} spell | ||
* @returns returning sql string | ||
@@ -813,0 +813,0 @@ */ |
@@ -61,3 +61,3 @@ 'use strict'; | ||
const results = []; | ||
const qualifiers = [ spell.Model.aliasName, ...Object.keys(spell.joins) ]; | ||
const qualifiers = [ spell.Model.tableAlias, ...Object.keys(spell.joins) ]; | ||
let defaultTableIndex = 0; | ||
@@ -64,0 +64,0 @@ |
@@ -16,3 +16,3 @@ 'use strict'; | ||
const { Model } = spell; | ||
const { aliasName } = Model; | ||
const { tableAlias } = Model; | ||
const results = []; | ||
@@ -27,3 +27,3 @@ | ||
? (parts.length > 1 ? parts : ['', key]) | ||
: [Model.attributeMap.hasOwnProperty(key) ? aliasName : '', key]; | ||
: [Model.attributeMap.hasOwnProperty(key) ? tableAlias : '', key]; | ||
const obj = result[qualifier] || (result[qualifier] = {}); | ||
@@ -30,0 +30,0 @@ obj[column] = row[key]; |
@@ -12,3 +12,3 @@ 'use strict'; | ||
if (!token.qualifiers && Model.attributes[token.value]) { | ||
token.qualifiers = [Model.aliasName]; | ||
token.qualifiers = [Model.tableAlias]; | ||
} | ||
@@ -19,5 +19,5 @@ whitelist.add(token.qualifiers[0]); | ||
for (const qualifier of [Model.aliasName].concat(Object.keys(joins))) { | ||
for (const qualifier of [Model.tableAlias].concat(Object.keys(joins))) { | ||
if (!whitelist.has(qualifier) && !groups.length > 0) { | ||
const model = qualifier == Model.aliasName ? Model : joins[qualifier].Model; | ||
const model = qualifier == Model.tableAlias ? Model : joins[qualifier].Model; | ||
for (const definition of model.columns) { | ||
@@ -24,0 +24,0 @@ const value = definition.columnName; |
@@ -120,2 +120,9 @@ 'use strict'; | ||
const hookNames = hookableMethods.reduce(function(result, method) { | ||
for (const prefix of Object.values(hookType)) { | ||
result.push(prefix + method[0].toUpperCase() + method.slice(1)); | ||
} | ||
return result; | ||
}, []); | ||
function addHook(target, hookName, func) { | ||
@@ -193,2 +200,3 @@ const { type, method } = getFnType(hookName); | ||
setupSingleHook, | ||
hookNames, | ||
}; |
@@ -394,3 +394,3 @@ 'use strict'; | ||
* @param {Model} BaseModel - A subclass of Bone | ||
* @param {string} baseName - Might be Model.aliasName, Model.table, or other names given by users | ||
* @param {string} baseName - Might be Model.tableAlias, Model.table, or other names given by users | ||
* @param {string} refName - The name of the join target | ||
@@ -963,6 +963,6 @@ * @param {Object} opts - Extra options such as { select, throughAssociation } | ||
for (const key in qualifier) { | ||
joinAssociation(this, this.Model, this.Model.aliasName, key, qualifier[key]); | ||
joinAssociation(this, this.Model, this.Model.tableAlias, key, qualifier[key]); | ||
} | ||
} else { | ||
joinAssociation(this, this.Model, this.Model.aliasName, qualifier); | ||
joinAssociation(this, this.Model, this.Model.tableAlias, qualifier); | ||
} | ||
@@ -988,3 +988,3 @@ } | ||
} | ||
const qualifier = Model.aliasName; | ||
const qualifier = Model.tableAlias; | ||
const { joins } = this; | ||
@@ -991,0 +991,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
250143
7298
95