Skip to content

Commit

Permalink
refactor: connect({ Bone }) still necessary (#139)
Browse files Browse the repository at this point in the history
...at lest for test:unit, especially in following scenario:

```js
class Spine extends Bone {}
class Book extends Spine {}
await connect({ Bone: Spine });
```

which connects `Spine.driver` without polluting `Bone.driver`. Handy for test case at least.
  • Loading branch information
cyjake authored Jul 16, 2021
1 parent 7739011 commit 31cb3de
Show file tree
Hide file tree
Showing 20 changed files with 202 additions and 167 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"no-underscore-dangle": 0,
"no-use-before-define": [2, "nofunc"],
"no-unused-vars": [2, { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
"no-shadow": 2,
"keyword-spacing": "error",
"eol-last": "error"
}
Expand Down
20 changes: 18 additions & 2 deletions docs/contributing/guides.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,34 @@ $ brew service start postgres

```bash
$ npm install
# prepare table schema, run all tests
$ npm run test
# run unit tests
$ npm test unit
$ npm run test:unit
# run integration tests
$ npm test integration
$ npm run test:integration
```

To be more specific, we can filter test files and cases:

```bash
$ npm run test -- test/unit/test.connect.js --grep "should work"
$ npm run test:unit --grep "=> Sequelize adapter"
$ npm run test:mysql --grep "bone.toJSON()"
```

If `--grep [pattern]` isn't specific enough, we can always insert `.only`:

```js
describe('=> Spell', function() {
it.only('supports error convention with nodeify', async function() {
// asserts
});
});
```

Please remember to remove them before commit.

## Working on Documentations

The Leoric documentation is served with Github Pages, which requries Jekyll to build. See the [Jekyll on macOS](https://jekyllrb.com/docs/installation/macos/) instructions, refer to the guide [Install Ruby on Mac](https://mac.install.guide/ruby/index.html), or use Moncef Belyamani's [Ruby setup scripts](https://www.moncefbelyamani.com/ruby-script/). If you only intend to use Jekyll, you can [install Ruby with Homebrew](https://mac.install.guide/ruby/13.html) without a version manager. After Ruby is installed, follow instructions to [install Jekyll](https://jekyllrb.com/docs/installation/macos/#install-jekyll).
Expand Down
18 changes: 16 additions & 2 deletions docs/zh/contributing/guides.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,31 @@ $ npm install
# 初始化表结构,运行所有测试
$ npm run test
# 仅运行单元测试
$ npm test unit
$ npm run test:unit
# 仅运行集成测试
$ npm test integration
$ npm run test:integration
```

还可以执行单个测试文件,或者使用 `--grep` 选项进一步限定执行范围:

```bash
$ npm run test -- test/unit/test.connect.js --grep "should work"
$ npm run test:unit -- --grep "=> Sequelize adapter"
$ npm run test:mysql -- --grep "bone.toJSON()"
```

如果 `--grep [pattern]` 不够直观,也可以随时改成用 `.only` 来指定用例:

```js
describe('=> Spell', function() {
it.only('supports error convention with nodeify', async function() {
// asserts
});
});
```

提交代码之前记得将 `.only` 移除掉。

## 编写帮助文档

Leoric 的帮助文档使用 Github Pages 服务,后者依赖 Jekyll 构建。Jekyll 是一个使用 Ruby 编写的静态站点生成工具,具体安装方式参考[macOS 安装 Ruby](https://mac.install.guide/ruby/index.html),或者参考 Moncef Belyamani 的 [Ruby 安装脚本](https://www.moncefbelyamani.com/ruby-script/)。如果你只想要安装 Jekyll,也可以[使用 HomeBrew 安装 Ruby](https://mac.install.guide/ruby/13.html),然后再[安装 Jekyll](https://jekyllrb.com/docs/installation/macos/) 即可:
Expand Down
32 changes: 13 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ function initAttributes(model, columns) {
model.init(attributes);
}

async function loadModels(Bone, models, opts) {
async function loadModels(Spine, models, opts) {
const { database } = opts;
const tables = models.map(model => model.physicTable);
const schemaInfo = await Bone.driver.querySchemaInfo(database, tables);
const schemaInfo = await Spine.driver.querySchemaInfo(database, tables);

for (const model of models) {
const columns = schemaInfo[model.physicTable];
if (!model.attributes) initAttributes(model, columns);
model.load(columns);
Bone.models[model.name] = model;
Spine.models[model.name] = model;
}

for (const model of models) {
Expand All @@ -85,6 +85,7 @@ async function loadModels(Bone, models, opts) {
}

function createSpine(opts) {
if (opts.Bone && opts.Bone.prototype instanceof Bone) return opts.Bone;
if (opts.sequelize) return sequelize(Bone);
if (opts.subclass !== true) return Bone;
return class Spine extends Bone {};
Expand Down Expand Up @@ -121,25 +122,15 @@ class Realm {
}

define(name, attributes, opts = {}, descriptors = {}) {
const { Bone } = this;
const Model = class extends Bone {
// export Model: instance.Model.name
get Model() {
return Model;
}
// export Model: class.Model.name
static get Model() {
return Model;
}
const Model = class extends this.Bone {
static name = name;
};
Object.defineProperty(Model, 'name', { value: name });
Model.init(attributes, opts, descriptors);
Bone.models[name] = Model;
return Model;
}

async connect() {
const { Bone } = this;
const { models: dir } = this.options;

let models;
Expand All @@ -149,9 +140,11 @@ class Realm {
models = Object.values(this.models);
}

if (models.length > 0) await loadModels(Bone, models, this.options);
if (models.length > 0) {
await loadModels(this.Bone, models, this.options);
}
this.connected = true;
return Bone;
return this.Bone;
}

async sync() {
Expand Down Expand Up @@ -238,8 +231,9 @@ class Realm {
* @param {string|Bone[]} opts.models - an array of models
* @returns {Pool} the connection pool in case we need to perform raw query
*/
const connect = async function connect(opts = {}) {
if (Bone.driver) throw new Error('connected already');
const connect = async function connect(opts) {
opts = { Bone, ...opts };
if (opts.Bone.driver) throw new Error('connected already');
const realm = new Realm(opts);
return await realm.connect();
};
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": "./test/start.sh",
"test:local": "./test/start.sh",
"test:unit": "./test/start.sh unit",
"test:integration": "./test/start.sh integration",
"test:mysql": "./test/start.sh test/integration/mysql.test.js",
"test:mysql2": "./test/start.sh test/integration/mysql2.test.js",
"test:postgres": "./test/start.sh test/integration/postgres.test.js",
Expand Down
4 changes: 2 additions & 2 deletions src/spell.js
Original file line number Diff line number Diff line change
Expand Up @@ -937,11 +937,11 @@ class Spell {
this.$having(conditions, ...values);
const { havingConditions } = this;
const len = havingConditions.length;
const condition = havingConditions.slice(1, len - 1).reduce((result, condition) => {
const combined = havingConditions.slice(1, len - 1).reduce((result, condition) => {
return { type: 'op', name: 'and', args: [result, condition] };
}, havingConditions[0]);
this.havingConditions = [
{ type: 'op', name: 'or', args: [condition, havingConditions[len - 1]] }
{ type: 'op', name: 'or', args: [combined, havingConditions[len - 1]] }
];
return this;
}
Expand Down
21 changes: 20 additions & 1 deletion test/integration/postgres.test.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, raw } = require('../..');

before(async function() {
await connect({
Expand Down Expand Up @@ -45,3 +45,22 @@ describe('=> Date functions (postgres)', function() {
]);
});
});


describe('=> upsert', function () {
const Post = require('../models/post');

it('upsert', function() {
assert.equal(
new Post({ id: 1, title: 'New Post', createdAt: raw('CURRENT_TIMESTAMP()'), updatedAt: raw('CURRENT_TIMESTAMP()') }).upsert().toString(),
'INSERT INTO "articles" ("id", "title", "gmt_modified") VALUES (1, \'New Post\', CURRENT_TIMESTAMP()) ON CONFLICT ("id") DO UPDATE SET "id"=EXCLUDED."id", "title"=EXCLUDED."title", "gmt_modified"=EXCLUDED."gmt_modified" RETURNING "id"'
);
});

it('upsert returning multiple columns', function() {
assert.equal(
new Post({ id: 1, title: 'New Post', createdAt: raw('CURRENT_TIMESTAMP()'), updatedAt: raw('CURRENT_TIMESTAMP()') }).upsert({ returning: [ 'id', 'title' ] }).toString(),
'INSERT INTO "articles" ("id", "title", "gmt_modified") VALUES (1, \'New Post\', CURRENT_TIMESTAMP()) ON CONFLICT ("id") DO UPDATE SET "id"=EXCLUDED."id", "title"=EXCLUDED."title", "gmt_modified"=EXCLUDED."gmt_modified" RETURNING "id", "title"'
);
});
});
14 changes: 13 additions & 1 deletion test/integration/sqlite.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use strict';

const assert = require('assert').strict;
const path = require('path');
const { connect, Bone } = require('../..');
const { connect, raw, Bone } = require('../..');
const { checkDefinitions } = require('./helpers');

before(async function() {
Expand Down Expand Up @@ -53,3 +54,14 @@ describe('=> Table definitions (sqlite)', () => {
});
});
});

describe('=> upsert', function () {
const Post = require('../models/post');

it('upsert', function() {
assert.equal(
new Post({ id: 1, title: 'New Post', createdAt: raw('CURRENT_TIMESTAMP()'), updatedAt: raw('CURRENT_TIMESTAMP()') }).upsert().toString(),
'INSERT INTO "articles" ("id", "title", "gmt_modified") VALUES (1, \'New Post\', CURRENT_TIMESTAMP()) ON CONFLICT ("id") DO UPDATE SET "id"=EXCLUDED."id", "title"=EXCLUDED."title", "gmt_modified"=EXCLUDED."gmt_modified"'
);
});
});
3 changes: 1 addition & 2 deletions test/integration/suite/basics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ let stub;
before(() => {
stub = sinon.stub(logger, 'warn').callsFake((message) => {
throw new Error(message);
}
);
});
});

after(() => {
Expand Down
17 changes: 9 additions & 8 deletions test/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ args=

function run {
file=$1;
if [ "${args[0]}" = "${file}" ]; then
args=("${args[@]:1}");
fi
echo "";
echo "> DEBUG=leoric mocha --exit --timeout 5000 ${file} ${args}";
DEBUG=leoric mocha --exit --timeout 5000 ${file} ${args} || exit $?;
printf '"%s" ' "${args[@]}" | xargs echo "> DEBUG=leoric mocha --exit --timeout 5000 ${file}";
printf '"%s" ' "${args[@]}" | DEBUG=leoric xargs mocha --exit --timeout 5000 ${file} || exit $?;
}

##
# Run unit tests first in order catch bugs as soon as possible
function unit {
# recursive glob nor available in bash 3
# - https://unix.stackexchange.com/questions/49913/recursive-glob
for file in $(ls test/unit/{,drivers/,drivers/*/,adapters/}*.test.js); do
run ${file};
done
run "$(ls test/unit/{,drivers/,drivers/*/,adapters/}*.test.js)";
}

##
Expand All @@ -27,15 +28,15 @@ function integration {

case $1 in
unit)
args="${@:2}"
args=("${@:2}")
unit
;;
integration)
args="${@:2}"
args=("${@:2}")
integration
;;
*.js)
args="${@:1}"
args=("${@:1}")
run $1
;;
*)
Expand Down
1 change: 0 additions & 1 deletion test/unit/adapters/sequelize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const { Bone, connect, sequelize, DataTypes } = require('../../..');
const { Hint } = require('../../../src/hint');
const { logger } = require('../../../src/utils');


const userAttributes = {
id: DataTypes.BIGINT,
gmt_create: DataTypes.DATE,
Expand Down
1 change: 1 addition & 0 deletions test/unit/bone.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { BIGINT, STRING, DATE } = DataTypes;

describe('=> Bone', function() {
before(async function() {
Bone.driver = null;
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
Expand Down
18 changes: 17 additions & 1 deletion test/unit/connect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ describe('connect', function() {
assert.equal(Bone.driver, null);
});

it('connect with custom Bone', async function() {
class Spine extends Bone {}
class Book extends Spine {}
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
database: 'leoric',
Bone: Spine,
models: [ Book ],
});
assert.equal(Bone.driver, null);
assert.equal(Book.driver, Spine.driver);
assert.ok(Spine.driver);
assert.ok(Book.synchronized);
});

it('connect models passed in opts.models (init with primaryKey)', async function() {
const { STRING, BIGINT } = DataTypes;
class Book extends Bone {
Expand Down Expand Up @@ -78,7 +94,7 @@ describe('connect', function() {
});

it('initialize model attributes if not defined in model itself', async () => {
const Book = require('../models/book');
class Book extends Bone {}
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
Expand Down
1 change: 1 addition & 0 deletions test/unit/drivers/abstract/spellbook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('=> Spellbook', function() {
}

before(async function() {
Bone.driver = null;
await connect({
models: [ User, Post ],
database: 'leoric',
Expand Down
11 changes: 7 additions & 4 deletions test/unit/hint.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use strict';

const assert = require('assert').strict;
const path = require('path');

const { connect } = require('../..');
const Post = require('../models/post');
const { connect, Bone } = require('../..');

const { Hint, IndexHint, INDEX_HINT_TYPE, INDEX_HINT_SCOPE } = require('../../src/hint');

Expand Down Expand Up @@ -49,9 +47,14 @@ describe('IndexHint', () => {
});

describe('MySQL', async () => {
class Post extends Bone {
static table = 'articles'
}

before(async function() {
Bone.driver = null;
await connect({
models: path.resolve(__dirname, '../models'),
models: [ Post ],
database: 'leoric',
user: 'root',
port: process.env.MYSQL_PORT,
Expand Down
1 change: 1 addition & 0 deletions test/unit/hooks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe('hooks', function() {
});

beforeEach(async () => {
Bone.driver = null;
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
Expand Down
Loading

0 comments on commit 31cb3de

Please sign in to comment.