Skip to content

Commit

Permalink
fix: opts.sequelize, opts.dialect, and opts.client (#34)
Browse files Browse the repository at this point in the history
also updated error messages a bit to better align with JavaScript convention
  • Loading branch information
cyjake authored Feb 22, 2020
1 parent 7e5188d commit ff55840
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 46 deletions.
6 changes: 6 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.5.3 / 2020-02-22
==================

* Fix: `connect({ sequelize, dialect, client })` to allow mandatory sqlite client
* Fix: prevent queries being performed unless model is correctly connected

0.5.2 / 2020-02-21
==================

Expand Down
18 changes: 11 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,24 @@ async function loadModels(Bone, models, opts) {
}
}

function createSpine(opts) {
if (opts.Bone) return opts.Bone;
if (opts.sequelize) return sequelize(Bone);
return class Spine extends Bone {};
}

class Realm {
constructor(opts = {}) {
const { client, dialect, database, Bone: Osteon, ...restOpts } = {
client: opts.dialect || 'mysql',
const { client, dialect, database, ...restOpts } = {
dialect: 'mysql',
database: opts.db || opts.storage,
Bone: class Spine extends Bone {},
...opts
};
const Spine = dialect ? sequelize(Osteon) : Osteon;
const Spine = createSpine(opts);
const models = {};

// test/integration/suite/migrations.js currently depends on this behavior
const driver = opts.driver || new (findDriver(client))({
const driver = opts.driver || new (findDriver(dialect))({
client,
database,
...restOpts
Expand Down Expand Up @@ -135,7 +140,7 @@ class Realm {
}

/**
* Connect models to database. Need to provide both the settings of the connection and the models, or the path of the models, to connect.
* Connect models to database. Need to provide both connect options and models.
* @alias module:index.connect
* @param {Object} opts
* @param {string} opts.client - client name
Expand All @@ -150,7 +155,6 @@ const connect = async function connect(opts = {}) {
return await realm.connect();
};


Object.assign(Realm.prototype, migrations, { DataTypes });
Object.assign(Realm, { connect, Bone, Collection, DataTypes, sequelize });

Expand Down
20 changes: 10 additions & 10 deletions lib/bone.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Bone {
this.rawUnset.delete(name);
return this;
} else {
if (this.rawUnset.has(name)) throw new Error(`Unset attribute "${name}"`);
if (this.rawUnset.has(name)) throw new Error(`unset attribute "${name}"`);
const value = this.raw[name];
// make sure null is returned if value is undefined
return value == null ? null : value;
Expand All @@ -142,7 +142,7 @@ class Bone {
* post.attributeWas('title') // => 'Leah'
*/
attributeWas(name) {
if (this.rawUnset.has(name)) throw new Error(`Unset attribute "${name}"`);
if (this.rawUnset.has(name)) throw new Error(`unset attribute "${name}"`);
const value = this.rawInitial[name];
return value == null ? null : value;
}
Expand Down Expand Up @@ -238,7 +238,7 @@ class Bone {
*/
async save() {
const { primaryKey } = this.constructor;
if (this.rawUnset.has(primaryKey)) throw new Error(`Unset primary key ${primaryKey}`);
if (this.rawUnset.has(primaryKey)) throw new Error(`unset primary key ${primaryKey}`);
if (this[primaryKey] == null) {
await this.create();
} else if (this.attributeChanged(primaryKey)) {
Expand Down Expand Up @@ -331,7 +331,7 @@ class Bone {

if (Object.keys(changes).length === 0) return Promise.resolve(0);
if (this[primaryKey] == null) {
throw new Error(`Unset primary key ${primaryKey}`);
throw new Error(`unset primary key ${primaryKey}`);
}

const where = { [primaryKey]: this[primaryKey] };
Expand Down Expand Up @@ -397,7 +397,7 @@ class Bone {
const { primaryKey, shardingKey } = Model;

if (this[primaryKey] == null) {
throw new Error('The instance is not persisted yet.');
throw new Error('instance is not persisted yet.');
}

const condition = { [primaryKey]: this[primaryKey] };
Expand All @@ -411,7 +411,7 @@ class Bone {
const { primaryKey, shardingKey } = Model;

if (this[primaryKey] == null) {
throw new Error('The instance is not persisted yet.');
throw new Error('instance is not persisted yet.');
}

const conditions = {
Expand Down Expand Up @@ -546,7 +546,7 @@ class Bone {
const { attributes, attributeMap } = this;

if (attributes.hasOwnProperty(newName)) {
throw new Error(`Unable to override existing attribute "${newName}"`);
throw new Error(`unable to override existing attribute "${newName}"`);
}

if (attributes.hasOwnProperty(originalName)) {
Expand Down Expand Up @@ -673,11 +673,11 @@ class Bone {
*/
static associate(name, opts = {}) {
if (name in this.associations) {
throw new Error(this.name + ' has duplicated association at: ' + name);
throw new Error(`duplicated association "${name}" on model ${this.name}`);
}
const { className } = opts;
const Model = this.models[className];
if (!Model) throw new Error(`Unable to find model "${className}"`);
if (!Model) throw new Error(`unable to find model "${className}"`);

const { deletedAt } = this.timestamps;
if (Model.attributes[deletedAt] && !opts.where) {
Expand Down Expand Up @@ -964,7 +964,7 @@ class Bone {
if (this.synchronized) return;

if (this.physicTables) {
throw new Error('Unable to sync model with custom physic tables');
throw new Error('unable to sync model with custom physic tables');
}

const { attributes, columns } = this;
Expand Down
9 changes: 4 additions & 5 deletions lib/drivers/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
'use strict';

function findDriver(client) {
switch (client) {
function findDriver(dialect) {
switch (dialect) {
case 'mysql':
case 'mysql2':
return require('./mysql');
case 'pg':
case 'postgres':
return require('./postgres');
case '@journeyapps/sqlcipher':
case 'sqlite':
case 'sqlite3':
return require('./sqlite');
default:
throw new Error(`Unsupported database ${client}`);
throw new Error(`Unsupported database ${dialect}`);
}
}

Expand Down
28 changes: 16 additions & 12 deletions lib/spell.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function parseConditions(conditions, ...values) {
return [parseExpr(conditions, ...values)];
}
else {
throw new Error(`Unsupported conditions ${conditions}`);
throw new Error(`unexpected conditions ${conditions}`);
}
}

Expand Down Expand Up @@ -123,7 +123,7 @@ function isLogicalOperator(condition) {

function parseLogicalObjectConditionValue(value) {
if (value == null || typeof value !== 'object') {
throw new Error(`Unexpected logical operator value ${value}`);
throw new Error(`unexpected logical operator value ${value}`);
}

if (Array.isArray(value)) return value;
Expand Down Expand Up @@ -218,7 +218,7 @@ function parseSelect(spell, ...names) {
const qualifier = qualifiers && qualifiers[0];
const model = qualifier && joins && (qualifier in joins) ? joins[qualifier].Model : Model;
if (!model.attributes[value]) {
throw new Error(`Unable to find attribute ${value} in model ${model.name}`);
throw new Error(`unable to find attribute ${value} in model ${model.name}`);
}
});
}
Expand Down Expand Up @@ -316,12 +316,12 @@ function joinAssociation(spell, BaseModel, baseName, refName, opts = {}) {
const { joins } = spell;
let association = BaseModel.associations[refName] || BaseModel.associations[pluralize(refName, 1)];

if (refName in joins) throw new Error(`Duplicated ${refName} in join tables`);
if (refName in joins) throw new Error(`duplicated ${refName} in join tables`);
if (!association && opts.targetAssociation) {
association = findAssociation(BaseModel.associations, { Model: opts.targetAssociation.Model });
}
if (!association) {
throw new Error(`Unable to find association ${refName} on ${BaseModel.name}`);
throw new Error(`unable to find association ${refName} on ${BaseModel.name}`);
}

const { through, Model: RefModel, includes } = association;
Expand Down Expand Up @@ -417,6 +417,10 @@ class Spell {
* @param {Object} opts - Extra attributes to be set.
*/
constructor(Model, opts) {
if (Model.synchronized == null) {
throw new Error(`model ${Model.name} is not connected yet`);
}

const scopes = [];
const { deletedAt } = Model.timestamps;
if (Model.attributes[deletedAt]) {
Expand Down Expand Up @@ -586,9 +590,9 @@ class Spell {
$increment(name, by = 1) {
const { Model } = this;

if (!Number.isFinite(by)) throw new Error(`Unexpected increment value ${by}`);
if (!Number.isFinite(by)) throw new Error(`unexpected increment value ${by}`);
if (!Model.attributes.hasOwnProperty(name)) {
throw new Error(`Undefined attribute "${name}"`);
throw new Error(`undefined attribute "${name}"`);
}

this.sets = {
Expand Down Expand Up @@ -723,7 +727,7 @@ class Spell {
*/
$offset(skip) {
skip = +skip;
if (Number.isNaN(skip)) throw new Error(`Invalid offset ${skip}`);
if (Number.isNaN(skip)) throw new Error(`invalid offset ${skip}`);
this.skip = skip;
return this;
}
Expand All @@ -735,7 +739,7 @@ class Spell {
*/
$limit(rowCount) {
rowCount = +rowCount;
if (Number.isNaN(rowCount)) throw new Error(`Invalid limit ${rowCount}`);
if (Number.isNaN(rowCount)) throw new Error(`invalid limit ${rowCount}`);
this.rowCount = rowCount;
return this;
}
Expand Down Expand Up @@ -824,7 +828,7 @@ class Spell {
const { joins } = this;

if (qualifier in joins) {
throw new Error(`Invalid join target. ${qualifier} already defined.`);
throw new Error(`invalid join target. ${qualifier} already defined.`);
}
joins[qualifier] = { Model, on: parseConditions(onConditions, ...values)[0] };
return this;
Expand Down Expand Up @@ -858,7 +862,7 @@ class Spell {
*/
async * batch(size = 1000) {
const limit = parseInt(size, 10);
if (!(limit > 0)) throw new Error(`Invalid batch limit ${size}`);
if (!(limit > 0)) throw new Error(`invalid batch limit ${size}`);
// Duplicate the spell because spell.skip gets updated while iterating over the batch.
const spell = this.limit(limit);
let results = await spell;
Expand Down Expand Up @@ -897,7 +901,7 @@ for (const aggregator in AGGREGATOR_MAP) {
writable: true,
value: function Spell_aggreator(name = '*') {
if (name != '*' && parseExpr(name).type != 'id') {
throw new Error(`Unexpected operand ${name} for ${func.toUpperCase()}()`);
throw new Error(`unexpected operand ${name} for ${func.toUpperCase()}()`);
}
this.$select(`${func}(${name}) as ${aggregator}`);
return this;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "leoric",
"version": "0.5.2",
"version": "0.5.3",
"description": "JavaScript Object-relational mapping alchemy",
"main": "index.js",
"types": "index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion test/integration/test.postgres.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { connect } = require('../..');

before(async function() {
await connect({
client: 'postgres',
dialect: 'postgres',
host: process.env.POSTGRES_HOST || '127.0.0.1',
port: process.env.POSTGRES_PORT,
user: process.env.POSTGRES_USER || '',
Expand Down
2 changes: 1 addition & 1 deletion test/integration/test.sqlite.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { checkDefinitions } = require('./helpers');

before(async function() {
await connect({
client: 'sqlite3',
dialect: 'sqlite',
database: '/tmp/leoric.sqlite3',
models: path.resolve(__dirname, '../models'),
});
Expand Down
2 changes: 1 addition & 1 deletion test/unit/adapters/test.sequelize.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('=> Sequelize adapter', () => {
before(async () => {
await connect({
Model: Spine,
client: 'sqlite',
dialect: 'sqlite',
database: '/tmp/leoric.sqlite3',
models: [ Book, Post ],
});
Expand Down
8 changes: 4 additions & 4 deletions test/unit/test.connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('connect', function() {

it('rejects unsupported database', async function() {
await assert.rejects(async () => {
await connect({ client: 'mongodb', models: `${__dirname}/models` });
await connect({ dialect: 'mongodb', models: `${__dirname}/models` });
}, /unsupported database/i);
});

Expand All @@ -23,9 +23,9 @@ describe('connect', function() {
});

it('connect with specified Bone', async function() {
const Osteon = await connect({ Bone: class extends Bone {} });
assert.ok(Osteon.prototype instanceof Bone);
assert.ok(Osteon.models);
const Spine = await connect({ Bone: class extends Bone {} });
assert.ok(Spine.prototype instanceof Bone);
assert.ok(Spine.models);
});

it('connect models passed in opts.models', async function() {
Expand Down
6 changes: 3 additions & 3 deletions test/unit/test.realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ describe('=> Realm', () => {
assert.equal(arda.Bone, Bone);
});

it('should subclass with sequelize api if exists opts.dialect', async () => {
const realm = new Realm({ dialect: 'mysql' });
it('should subclass with sequelize api if opts.sequelize', async () => {
const realm = new Realm({ sequelize: true });
const { Bone: Spine } = realm;
assert.ok(typeof Spine.prototype.setDataValue === 'function');
});
Expand All @@ -31,7 +31,7 @@ describe('=> Realm', () => {
});

it('should rename opts.storage to opts.database', async () => {
const realm = new Realm({ client: 'sqlite3', storage: '/tmp/leoric.sqlite3' });
const realm = new Realm({ dialect: 'sqlite', storage: '/tmp/leoric.sqlite3' });
assert.equal(realm.options.database, '/tmp/leoric.sqlite3');
});

Expand Down
9 changes: 8 additions & 1 deletion test/unit/test.spell.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const assert = require('assert').strict;
const path = require('path');

const { connect } = require('../..');
const { connect, Bone } = require('../..');
const Post = require('../models/post');
const TagMap = require('../models/tagMap');
const Comment = require('../models/comment');
Expand All @@ -17,6 +17,13 @@ before(async function() {
});
});

describe('=> Spell', function() {
it('rejects query if not connected yet', async () => {
class Note extends Bone {};
await assert.rejects(async () => await Note.all);
});
});

describe('=> Insert', function() {
it('insert', function() {
const date = new Date(2017, 11, 12);
Expand Down

0 comments on commit ff55840

Please sign in to comment.