From d865b77aea7a9c06f20602423ea84e5a33d14d54 Mon Sep 17 00:00:00 2001 From: Anatoliy Chakkaev Date: Sat, 1 Oct 2011 23:40:22 +0400 Subject: [PATCH] Sequelize adapter --- index.js | 23 ++++-- lib/sequelize.js | 178 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 6 +- 3 files changed, 199 insertions(+), 8 deletions(-) create mode 100644 lib/sequelize.js diff --git a/index.js b/index.js index e31ff6bf..997b90c7 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,14 @@ function Text() { } Schema.Text = Text; +Schema.prototype.automigrate = function (cb) { + if (this.adapter.automigrate) { + this.adapter.automigrate(cb); + } else { + cb && cb(); + } +}; + /** * Define class * @param className @@ -103,12 +111,14 @@ function AbstractClass(name, properties, settings, data) { var self = this; data = data || {}; - Object.defineProperty(this, 'id', { - writable: false, - enumerable: true, - configurable: true, - value: data.id - }); + if (data.id) { + Object.defineProperty(this, 'id', { + writable: false, + enumerable: true, + configurable: true, + value: data.id + }); + } Object.keys(properties).forEach(function (attr) { var _attr = '_' + attr, @@ -158,6 +168,7 @@ AbstractClass.create = function (data) { if (arguments.length == 0 || data === callback) { data = {}; } + if (typeof callback !== 'function') { callback = function () {}; } diff --git a/lib/sequelize.js b/lib/sequelize.js new file mode 100644 index 00000000..2d97fa4c --- /dev/null +++ b/lib/sequelize.js @@ -0,0 +1,178 @@ +var Sequelize = require('sequelize'); + +exports.initialize = function initializeSchema(schema) { + schema.adapter = new SequelizeAdapter(schema); +}; + +function SequelizeAdapter(schema) { + this.schema = schema; + this._models = {}; + this._modelDefinitions = {}; + this.client = new Sequelize( + schema.settings.database, + schema.settings.username, + schema.settings.password, { + host: schema.settings.host, + port: schema.settings.port, + logging: schema.settings.port, + maxConcurrentQueries: schema.settings.maxConcurrentQueries + } + ); +} + +SequelizeAdapter.prototype.define = function (d) { + var model = d.model; + var settings = d.settings; + var properties = d.properties; + var translate = { + 'String': Sequelize.STRING, + 'Text': Sequelize.TEXT, + 'Number': Sequelize.INTEGER, + 'Boolean': Sequelize.BOOLEAN, + 'Date': Sequelize.DATE + }; + + var props = {}; + + Object.keys(properties).forEach(function (property) { + props[property] = translate[properties[property].type.name]; + }); + + this._modelDefinitions[model.modelName] = props; + this._models[model.modelName] = this.client.define(model.modelName, props, settings); + +}; + +SequelizeAdapter.prototype.model = function getModel(name) { + return this._models[name]; +}; + +SequelizeAdapter.prototype.cleanup = function (model, obj) { + if (!obj) { + return null; + } + var def = this._modelDefinitions[model]; + var data = {}; + Object.keys(def).concat(['id']).forEach(function (key) { + data[key] = obj[key]; + }); + return data; +}; + +SequelizeAdapter.prototype.save = function (model, data, callback) { + this.model(model).find(data.id) + .on('success', function (record) { + record.updateAttributes(data) + .on('success', callback.bind(data, null)) + .on('failure', callback); + }) + .on('failure', callback); + +}; + +SequelizeAdapter.prototype.updateAttributes = function (model, id, data, callback) { + data.id = id; + this.save(model, data, callback); +}; + +SequelizeAdapter.prototype.create = function (model, data, callback) { + this.model(model) + .build(data) + .save() + .on('success', function (obj) { + callback(null, obj.id); + }) + .on('failure', callback); +}; + +SequelizeAdapter.prototype.automigrate = function (cb) { + this.client.sync({force: true}) + .on('success', cb.bind(this, null)) + .on('failure', cb); +}; + +SequelizeAdapter.prototype.exists = function (model, id, callback) { + this.model(model) + .find(id) + .on('success', function (data) { + if (callback) { + callback.calledOnce = true; + callback(null, data); + } + }) + .on('failure', co(callback)); +}; + +function co(cb, args) { + + return function () { + if (!cb.calledOnce) { + cb.calledOnce = true; + cb.call.apply(cb, args || []); + } + } +} + +SequelizeAdapter.prototype.find = function find(model, id, callback) { + this.model(model) + .find(id) + .on('success', function (data) { + callback(null, this.cleanup(model, data)); + }.bind(this)) + .on('failure', callback); +}; + +SequelizeAdapter.prototype.destroy = function destroy(model, id, callback) { + this.model(model) + .find(id) + .on('success', function (data) { + data.destroy() + .on('success', callback.bind(null, null)) + .on('failure', callback); + }.bind(this)) + .on('failure', callback); +}; + +SequelizeAdapter.prototype.all = function all(model, filter, callback) { + this.model(model).all.on('success', function (data) { + // TODO: filter + callback(null, data); + }).on('failure', callback); +}; + +function applyFilter(filter) { + if (typeof filter === 'function') { + return filter; + } + var keys = Object.keys(filter); + return function (obj) { + var pass = true; + keys.forEach(function (key) { + if (!test(filter[key], obj[key])) { + pass = false; + } + }); + return pass; + } + + function test(example, value) { + if (typeof value === 'string' && example && example.constructor.name === 'RegExp') { + return value.match(example); + } + // not strict equality + return example == value; + } +} + +SequelizeAdapter.prototype.destroyAll = function destroyAll(model, callback) { + callback(); +}; + +SequelizeAdapter.prototype.count = function count(model, callback) { + this.model(model).count() + .on('success', function (c) { + if (callback) callback(null, c); + }) + .on('failure', callback); +}; + diff --git a/package.json b/package.json index bdcfc457..44e83bd9 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "test": "nodeunit test/*_test.js" }, "engines": { - "node": "~v0.4.9" + "node": ">= v0.4.0" + }, + "dependencies": { + "redis": "=0.6.7" }, - "dependencies": {}, "devDependencies": {} }