Merge pull request #39 from dgsan/master

Support for Enum type in MySQL adapter. Requires patch to mainline JDB.
This commit is contained in:
dgsan 2013-06-18 11:07:56 -07:00
commit f2412bc170
3 changed files with 196 additions and 0 deletions

49
lib/enumFactory.js Normal file
View File

@ -0,0 +1,49 @@
var EnumFactory = function() {
if(arguments.length > 0){
var Enum = function Enum(arg){
if(typeof arg === 'number' && arg % 1 == 0) {
return Enum._values[arg];
} else if(Enum[arg]){
return Enum[arg]
} else if (Enum._values.indexOf(arg) !== -1 ) {
return arg;
} else if (arg === null) {
return null;
} else {
return '';
}
};
var dxList = [];
dxList.push(''); // Want empty value to be at index 0 to match MySQL Enum values and MySQL non-strict behavior.
for(var arg in arguments){
arg = String(arguments[arg]);
Object.defineProperty(Enum, arg.toUpperCase(), {configurable: false, enumerable: true, value: arg, writable: false});
dxList.push(arg);
}
Object.defineProperty(Enum, '_values', {configurable: false, enumerable: false, value: dxList, writable: false});
Object.defineProperty(Enum, '_string', {configurable: false, enumerable: false, value: stringified(Enum), writable: false});
Object.freeze(Enum);
return Enum;
} else {
throw "No arguments - could not create Enum.";
}
};
function stringified(anEnum) {
var s = [];
for(var i in anEnum._values){
if(anEnum._values[i] != ''){
s.push("'" + anEnum._values[i] + "'");
}
}
return s.join(',');
}
exports.EnumFactory = EnumFactory;

View File

@ -3,6 +3,8 @@
*/
var mysql = require('mysql');
var jdb = require('jugglingdb');
var EnumFactory = require('./enumFactory').EnumFactory;
exports.initialize = function initializeSchema(schema, callback) {
if (!mysql) return;
@ -58,6 +60,10 @@ exports.initialize = function initializeSchema(schema, callback) {
// MySQL specific column types
schema.constructor.registerType(function Point() {});
schema.EnumFactory = EnumFactory; // factory for Enums. Note that currently Enums can not be registered.
};
/**
@ -205,6 +211,7 @@ MySQL.prototype.toDatabase = function (prop, val) {
return '"' + dateToMysql(val) + '"';
}
if (prop.type.name == "Boolean") return val ? 1 : 0;
if (typeof prop.type === 'function') return this.client.escape(prop.type(val));
return this.client.escape(val.toString());
};
@ -644,6 +651,10 @@ function datatype(p) {
case 'Point':
dt = 'POINT';
break;
case 'Enum':
dt = 'ENUM(' + p.type._string + ')';
dt = stringOptions(p, dt); // Enum columns can have charset/collation.
break;
}
return dt;
}

136
test/datatypes.test.js Normal file
View File

@ -0,0 +1,136 @@
var should = require('./init.js');
var assert = require('assert');
var Schema = require('jugglingdb').Schema;
var db, settings, adapter, EnumModel, ANIMAL_ENUM;
describe('MySQL specific datatypes', function() {
before(setup);
it('should run migration', function(done) {
db.automigrate(function(){
done();
});
});
it('An enum should parse itself', function(done) {
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM('cat'));
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM('CAT'));
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM(2));
assert.equal(ANIMAL_ENUM.CAT, 'cat');
assert.equal(ANIMAL_ENUM(null), null);
assert.equal(ANIMAL_ENUM(''), '');
assert.equal(ANIMAL_ENUM(0), '');
done();
});
it('should create a model instance with Enums', function(done) {
var em = EnumModel.create({animal: ANIMAL_ENUM.CAT, condition: 'sleepy', mood: 'happy'}, function(err, obj) {
assert.ok(!err);
assert.equal(obj.condition, 'sleepy');
EnumModel.findOne({where: {animal: ANIMAL_ENUM.CAT}}, function(err, found){
assert.ok(!err);
assert.equal(found.mood, 'happy');
assert.equal(found.animal, ANIMAL_ENUM.CAT);
done();
});
});
});
it('should fail spectacularly with invalid enum values', function(done) {
var em = EnumModel.create({animal: 'horse', condition: 'sleepy', mood: 'happy'}, function(err, obj) {
assert.ok(!err);
EnumModel.find(obj.id, function(err, found){
assert.ok(!err);
assert.equal(found.animal, ''); // MySQL fun.
assert.equal(found.animal, 0);
done();
});
});
});
it('should disconnect when done', function(done) {
db.disconnect();
done()
});
});
function setup(done) {
require('./init.js');
db = getSchema();
ANIMAL_ENUM = db.EnumFactory('dog', 'cat', 'mouse');
EnumModel = db.define('EnumModel', {
animal: { type: ANIMAL_ENUM, null: false },
condition: { type: db.EnumFactory('hungry', 'sleepy', 'thirsty') },
mood: { type: db.EnumFactory('angry', 'happy', 'sad') }
});
blankDatabase(db, done);
}
var query = function (sql, cb) {
db.adapter.query(sql, cb);
};
var blankDatabase = function (db, cb) {
var dbn = db.settings.database;
var cs = db.settings.charset;
var co = db.settings.collation;
query('DROP DATABASE IF EXISTS ' + dbn, function(err) {
var q = 'CREATE DATABASE ' + dbn;
if(cs){
q += ' CHARACTER SET ' + cs;
}
if(co){
q += ' COLLATE ' + co;
}
query(q, function(err) {
query('USE '+ dbn, cb);
});
});
};
getFields = function (model, cb) {
query('SHOW FIELDS FROM ' + model, function(err, res) {
if (err) {
cb(err);
} else {
var fields = {};
res.forEach(function(field){
fields[field.Field] = field;
});
cb(err, fields);
}
});
}
getIndexes = function (model, cb) {
query('SHOW INDEXES FROM ' + model, function(err, res) {
if (err) {
console.log(err);
cb(err);
} else {
var indexes = {};
// Note: this will only show the first key of compound keys
res.forEach(function(index) {
if (parseInt(index.Seq_in_index, 10) == 1) {
indexes[index.Key_name] = index
}
});
cb(err, indexes);
}
});
};