Permalink
Browse files

feat: add execute function to adapters

  • Loading branch information...
XadillaX committed Aug 9, 2016
1 parent 7240c0b commit f0494a0edeaca2b897c9fd298bf31c7ca79f6924
Showing with 218 additions and 75 deletions.
  1. +5 −0 lib/adapters/base.js
  2. +44 −3 lib/adapters/mysql.js
  3. +36 −0 lib/toshihiko.js
  4. +26 −7 test/adapters/base.js
  5. +107 −65 test/adapters/mysql.js
@@ -31,6 +31,11 @@ class Adapter extends EventEmitter {

debug("created.", this);
}

execute(param, callback) {
debug("this adapter instance's execute is not implemented.");
process.nextTick(callback);
}
}

module.exports = Adapter;
@@ -15,6 +15,26 @@ function onConnection() {
this.emit("log", "A new MySQL connection from Toshihiko is set. ⁽⁽ଘ( ˙꒳˙ )ଓ⁾⁾");
}

function _loadOrigin(name) {
const mysql = require(name);

Object.defineProperty(this, "package", {
configurable: false,
writable: false,
enumerable: true,
value: name
});

Object.defineProperty(this, "format", {
configurable: false,
writable: false,
enumerable: true,
value: mysql.format.bind(mysql)
});

return mysql;
}

class MySQLAdapter extends Adapter {
constructor(parent, options) {
super(parent, options);
@@ -51,16 +71,17 @@ class MySQLAdapter extends Adapter {

// try to require mysql adapter
let mysql;
const loadOrigin = _loadOrigin.bind(this);
if(!this.options.package) {
try {
mysql = require("mysql2");
mysql = loadOrigin("mysql2");
} catch(e) {
debug("fallback to use mysql.");
mysql = require("mysql");
mysql = loadOrigin("mysql");
}
} else {
debug(`use package "${this.options.package}" as adapter`);
mysql = require(this.options.package);
mysql = loadOrigin(this.options.package);
}

Object.defineProperty(this, "mysql", {
@@ -83,6 +104,26 @@ class MySQLAdapter extends Adapter {

debug("created.", this);
}

execute(sql, params, callback) {
if(typeof params === "function") {
callback = params;
params = undefined;
}

this.mysql.getConnection((err, conn) => {
if(err) return callback(err);

if(params && params.length) {
sql = this.format(sql, params);
}

conn.query(sql, function(err, rows) {
conn.release();
callback(err, rows);
});
});
}
}

module.exports = MySQLAdapter;
@@ -9,6 +9,8 @@
const _ = require("lodash");
const EventEmitter = require("eventemitter2").EventEmitter2;

const common = require("../util/common");

class Toshihiko extends EventEmitter {
constructor(Adapter, options) {
super();
@@ -26,6 +28,40 @@ class Toshihiko extends EventEmitter {
this.cache = Toshihiko.createCache(opt.cache);
}
}

execute() {
this.adapter.execute.apply(this.adapter, arguments);
}

define(collectionName, schema, options) {
const Model = require("./model");
const model = new Model(collectionName, this, schema, options);
return model;
}
}

Toshihiko.createCache = function(param) {
// if param itself is a cache instance
if(typeof param.deleteData === "function" && typeof param.deleteKeys === "function" &&
typeof param.setData === "function" && typeof param.getData === "function") {
return param;
}

let path;
if(param.path) {
path = param.path;
} else if(param.name) {
path = `toshihiko-${param.name}`;
} else {
return null;
}

const m = require(path);
const func = m.create;
const keys = common.getParamNames(func);

// fill param.?? to Cache.create
return func.apply(undefined, keys.map(key => param[key]));
};

module.exports = Toshihiko;
@@ -11,14 +11,33 @@ require("should");
const Adapter = require("../../lib/adapters/base");

describe("🐣 adapters/base", function() {
it("should create a base adapter", function() {
const par = {};
const options = { foo: "bar" };
const adapter = new Adapter(par, options);
const adapter = new Adapter({}, {});

adapter.parent.should.be.eql(par);
describe("create", function() {
it("should create a base adapter", function() {
const par = {};
const options = { foo: "bar" };
const adapter = new Adapter(par, options);

adapter.options.should.deepEqual(options);
adapter.options.should.not.equal(options);
adapter.parent.should.be.eql(par);

adapter.options.should.deepEqual(options);
adapter.options.should.not.equal(options);
});
});

describe("execute", function() {
it("should be async", function(done) {
let flag = false;
let ok = false;

adapter.execute({}, function() {
flag = true;
if(ok) done();
});

flag.should.equal(false);
ok = true;
});
});
});
@@ -9,94 +9,136 @@
const path = require("path");

const decache = require("decache");
const otrans = require("otrans");
const runSync = require("sync-runner");
const should = require("should");

const Adapter = require("../../lib/adapters/base");
const MySQLAdapter = require("../../lib/adapters/mysql");

describe("🐣 adapters/mysql", function() {
it("should be instanceof Adapter", function(done) {
const par = {};
const options = {};
const adapter = new MySQLAdapter(par, options);
describe("create", function() {
it("should be instanceof Adapter", function(done) {
const par = {};
const options = {};
const adapter = new MySQLAdapter(par, options);

adapter.should.be.instanceof(Adapter);
adapter.mysql.end(done);
});

it("should have correct position of username, database, password", function(done) {
const par = {};
const options = {
username: "username",
password: "pwd",
database: "test",

host: "127.0.0.1"
};
const adapter = new MySQLAdapter(par, options);

adapter.username.should.equal(options.username);
adapter.database.should.equal(options.database);
adapter.options.host.should.equal(options.host);
adapter.options.port.should.equal(3306);
adapter.mysql.config.connectionConfig.password.should.equal(options.password);
adapter.should.be.instanceof(Adapter);
adapter.mysql.end(done);
});

should.not.exists(adapter.options.username);
should.not.exists(adapter.options.password);
should.not.exists(adapter.options.database);
it("should have correct position of username, database, password", function(done) {
const par = {};
const options = {
username: "username",
password: "pwd",
database: "test",

adapter.mysql.end(done);
});
host: "127.0.0.1"
};
const adapter = new MySQLAdapter(par, options);

it("should hijack parent's pool to be compatible", function(done) {
const par = {};
const adapter = new MySQLAdapter(par);
par.pool.should.equal(adapter.mysql);
par.pool.end(done);
});
adapter.username.should.equal(options.username);
adapter.database.should.equal(options.database);
adapter.options.host.should.equal(options.host);
adapter.options.port.should.equal(3306);
adapter.mysql.config.connectionConfig.password.should.equal(options.password);

describe("should use mysql2", function() {
it("when default", function(done) {
const Pool = require("mysql2/lib/pool");
const adapter = new MySQLAdapter({}, {});
should.not.exists(adapter.options.username);
should.not.exists(adapter.options.password);
should.not.exists(adapter.options.database);

adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
});

it("when use config", function(done) {
const Pool = require("mysql2/lib/pool");
const options = { package: "mysql2" };
const adapter = new MySQLAdapter({}, options);

adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
it("should hijack parent's pool to be compatible", function(done) {
const par = {};
const adapter = new MySQLAdapter(par);
par.pool.should.equal(adapter.mysql);
par.pool.end(done);
});
});

describe("should use mysql", function() {
it("when default with no mysql2", function(done) {
decache("mysql2");
decache("../../lib/adapters/mysql");
const MySQLAdapter_ = require("../../lib/adapters/mysql");
runSync("mv node_modules/mysql2 node_modules/mysql2.bak", path.resolve(__dirname, "../../"));
describe("should use mysql2", function() {
it("when default", function(done) {
const Pool = require("mysql2/lib/pool");
const adapter = new MySQLAdapter({}, {});

const adapter = new MySQLAdapter_({}, {});
runSync("mv node_modules/mysql2.bak node_modules/mysql2", path.resolve(__dirname, "../../"));
adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
});

const Pool = require("mysql/lib/Pool");
adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
it("when use config", function(done) {
const Pool = require("mysql2/lib/pool");
const options = { package: "mysql2" };
const adapter = new MySQLAdapter({}, options);

adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
});
});

it("when use config", function(done) {
const Pool = require("mysql/lib/Pool");
const options = { package: "mysql" };
const adapter = new MySQLAdapter({}, options);
describe("should use mysql", function() {
it("when default with no mysql2", function(done) {
decache("mysql2");
decache("../../lib/adapters/mysql");
const MySQLAdapter_ = require("../../lib/adapters/mysql");
runSync("mv node_modules/mysql2 node_modules/mysql2.bak", path.resolve(__dirname, "../../"));

const adapter = new MySQLAdapter_({}, {});
runSync("mv node_modules/mysql2.bak node_modules/mysql2", path.resolve(__dirname, "../../"));

const Pool = require("mysql/lib/Pool");
adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
});

it("when use config", function(done) {
const Pool = require("mysql/lib/Pool");
const options = { package: "mysql" };
const adapter = new MySQLAdapter({}, options);

adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
});
});
});

adapter.mysql.should.be.instanceof(Pool);
adapter.mysql.end(done);
[ "mysql", "mysql2" ].forEach(name => {
describe(`${name} execute`, function() {
const adapter = new MySQLAdapter({}, {
username: "root",
password: "",
database: "toshihiko",
charset: "utf8mb4_general_ci"
});

after(function() {
adapter.mysql.end();
});

it("should execute `create table`", function(done) {
adapter.execute("create table ??(id int(?) not null)", [ "test", 11 ], function(err, rows) {
should.ifError(err);
rows.serverStatus.should.equal(2);
done();
});
});

it("should execute `show tables`", function(done) {
adapter.execute("show tables;", function(err, rows) {
should.ifError(err);
otrans.toCamel(rows).should.deepEqual([ { tablesInToshihiko: "test" } ]);
done();
});
});

it("should execute `drop table`", function(done) {
adapter.execute("drop table test", [], function(err, rows) {
should.ifError(err);
rows.serverStatus.should.equal(2);
done();
});
});
});
});
});

0 comments on commit f0494a0

Please sign in to comment.