diff --git a/lib/enumFactory.js b/lib/enumFactory.js new file mode 100644 index 0000000..baf5371 --- /dev/null +++ b/lib/enumFactory.js @@ -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; + + + + + + + diff --git a/lib/mysql.js b/lib/mysql.js index 9d300dc..c0272a5 100644 --- a/lib/mysql.js +++ b/lib/mysql.js @@ -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; } diff --git a/test/datatypes.test.js b/test/datatypes.test.js new file mode 100644 index 0000000..882b39a --- /dev/null +++ b/test/datatypes.test.js @@ -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); + } + }); +}; + + + + + +