Skip to content
Browse files

First version of a cradle adapter

  • Loading branch information...
1 parent 9e2b9e1 commit d333fe1d306a6ea2904df8854c3c173280505ee1 @athieriot athieriot committed Jun 10, 2012
Showing with 325 additions and 3 deletions.
  1. +319 −0 lib/adapters/cradle.js
  2. +2 −1 package.json
  3. +3 −2 test/common_test.js
  4. +1 −0 test/performance.coffee
View
319 lib/adapters/cradle.js
@@ -0,0 +1,319 @@
+var safeRequire = require('../utils').safeRequire;
+
+/**
+ * Module dependencies
+ */
+var cradle = safeRequire('cradle');
+
+/**
+ * Private functions for internal use
+ */
+function CradleAdapter(client) {
+ this._models = {};
+ this.client = client;
+}
+
+function createdbif(client, callback) {
+ client.exists(function (err, exists) {
+ if(err) callback(err);
+ if (!exists) { client.create(function() { callback(); }); }
+ else { callback(); }
+ });
+}
+
+function naturalize(data, model) {
+ data.nature = model;
+ //TODO: maybe this is not a really good idea
+ if(data.date) data.date = data.date.toString();
+ return data;
+}
+function idealize(data) {
+ data.id = data._id;
+ return data;
+}
+function stringify(data) {
+ return data ? data.toString() : data
+}
+
+function errorHandler(callback, func) {
+ return function(err, res) {
+ if(err) {
+ callback(err);
+ } else {
+ if(func) {
+ func(res, function(res) {
+ callback(null, res);
+ });
+ } else {
+ callback(null, res);
+ }
+ }
+ }
+};
+
+function synchronize(functions, args, callback) {
+ if(functions.length === 0) callback();
+ if(functions.length > 0 && args.length === functions.length) {
+ functions[0](args[0][0], args[0][1], function(err, res) {
+ if(err) callback(err);
+ functions.splice(0, 1);
+ args.splice(0, 1);
+ synchronize(functions, args, callback);
+ });
+ }
+};
+
+function applyFilter(filter) {
+ if (typeof filter.where === 'function') {
+ return filter.where;
+ }
+ var keys = Object.keys(filter.where);
+ return function (obj) {
+ var pass = true;
+ keys.forEach(function (key) {
+ if (!test(filter.where[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;
+ }
+}
+
+function numerically(a, b) {
+ return a[this[0]] - b[this[0]];
+}
+
+function literally(a, b) {
+ return a[this[0]] > b[this[0]];
+}
+
+function filtering(res, model, filter, instance) {
+
+ if(model) {
+ if(filter == null) filter = {};
+ if(filter.where == null) filter.where = {};
+ filter.where.nature = model;
+ }
+ // do we need some filtration?
+ if (filter.where) {
+ res = res ? res.filter(applyFilter(filter)) : res;
+ }
+
+ // do we need some sorting?
+ if (filter.order) {
+ var props = instance[model].properties;
+ var allNumeric = true;
+ var orders = filter.order;
+ var reverse = false;
+ if (typeof filter.order === "string") {
+ orders = [filter.order];
+ }
+
+ orders.forEach(function (key, i) {
+ var m = key.match(/\s+(A|DE)SC$/i);
+ if (m) {
+ key = key.replace(/\s+(A|DE)SC/i, '');
+ if (m[1] === 'DE') reverse = true;
+ }
+ orders[i] = key;
+ if (props[key].type.name !== 'Number') {
+ allNumeric = false;
+ }
+ });
+ if (allNumeric) {
+ res = res.sort(numerically.bind(orders));
+ } else {
+ res = res.sort(literally.bind(orders));
+ }
+ if (reverse) res = res.reverse();
+ }
+ return res;
+}
+
+/**
+ * Connection/Disconnection
+ */
+exports.initialize = function(schema, callback) {
+ if (!cradle) return;
+
+ if (!schema.settings.url) {
+ var host = schema.settings.host || 'localhost';
+ var port = schema.settings.port || '5984';
+ var options = schema.settings.options || {
+ cache: true,
+ raw: false
+ };
+ if (schema.settings.username) {
+ options.auth = {};
+ options.auth.username = schema.settings.username;
+ if (schema.settings.password) {
+ options.auth.password = schema.settings.password;
+ }
+ }
+ var database = schema.settings.database || 'jugglingdb';
+
+ schema.settings.host = host;
+ schema.settings.port = port;
+ schema.settings.database = database;
+ schema.settings.options = options;
+ }
+ schema.client = new(cradle.Connection)(schema.settings.host, schema.settings.port,schema.settings.options).database(schema.settings.database);
+
+ createdbif(
+ schema.client,
+ errorHandler(callback, function() {
+ schema.adapter = new CradleAdapter(schema.client);
+ process.nextTick(callback);
+ }));
+};
+
+CradleAdapter.prototype.disconnect = function() {
+};
+
+/**
+ * Write methods
+ */
+CradleAdapter.prototype.define = function(descr) {
+ this._models[descr.model.modelName] = descr;
+};
+
+CradleAdapter.prototype.create = function(model, data, callback) {
+ this.client.save(
+ stringify(data.id),
+ naturalize(data, model),
+ errorHandler(callback, function(res, cb) {
+ cb(res.id);
+ })
+ );
+};
+
+CradleAdapter.prototype.save = function(model, data, callback) {
+ this.client.save(
+ stringify(data.id),
+ naturalize(data, model),
+ errorHandler(callback)
+ )
+};
+
+CradleAdapter.prototype.updateAttributes = function(model, id, data, callback) {
+ this.client.merge(
+ stringify(id),
+ data,
+ errorHandler(callback, function(doc, cb) {
+ cb(idealize(doc));
+ })
+ );
+};
+
+CradleAdapter.prototype.updateOrCreate = function(model, data, callback) {
+ this.client.get(
+ stringify(data.id),
+ function (err, doc) {
+ if(err) {
+ this.create(model, data, callback);
+ } else {
+ this.updateAttributes(mode, data.id, data, callback);
+ }
+ }.bind(this)
+ )
+};
+
+/**
+ * Read methods
+ */
+CradleAdapter.prototype.exists = function(model, id, callback) {
+ this.client.get(
+ stringify(id),
+ errorHandler(callback, function(doc, cb) {
+ cb(!!doc);
+ })
+ );
+};
+
+CradleAdapter.prototype.find = function(model, id, callback) {
+ this.client.get(
+ stringify(id),
+ errorHandler(callback, function(doc, cb) {
+ cb(idealize(doc));
+ })
+ );
+};
+
+CradleAdapter.prototype.count = function(model, callback, where) {
+ this.models(
+ model,
+ {where: where},
+ callback,
+ function(docs, cb) {
+ cb(docs.length);
+ }
+ );
+};
+
+CradleAdapter.prototype.models = function(model, filter, callback, func) {
+ this.client.all(
+ {include_docs: true},
+ errorHandler(callback, function(res, cb) {
+ var docs = res.map(function(doc) {
+ return idealize(doc);
+ });
+ var filtered = filtering(docs, model, filter, this._models)
+
+ func ? func(filtered, cb) : cb(filtered);
+ }.bind(this))
+ );
+};
+
+CradleAdapter.prototype.all = function(model, filter, callback) {
+ this.models(
+ model,
+ filter,
+ callback
+ );
+};
+
+/**
+ * Detroy methods
+ */
+CradleAdapter.prototype.destroy = function(model, id, callback) {
+ this.client.remove(
+ stringify(id),
+ function (err, doc) {
+ callback(err);
+ }
+ );
+};
+
+CradleAdapter.prototype.destroyAll = function(model, callback) {
+ this.models(
+ model,
+ null,
+ callback,
+ function(docs, cb) {
+ var docIds = docs.map(function(doc) {
+ return doc.id;
+ });
+ this.client.get(docIds, function(err, res) {
+ if(err) cb(err);
+
+ var funcs = res.map(function(doc) {
+ return this.client.remove.bind(this.client);
+ }.bind(this));
+
+ var args = res.map(function(doc) {
+ return [doc._id, doc._rev];
+ });
+
+ synchronize(funcs, args, cb);
+ }.bind(this));
+ }.bind(this)
+ );
+};
View
3 package.json
@@ -40,6 +40,7 @@
"riak-js": ">= 0.4.1",
"neo4j": ">= 0.2.5",
"mongodb": ">= 0.9.9",
- "felix-couchdb": ">= 1.0.3"
+ "felix-couchdb": ">= 1.0.3",
+ "cradle": ">= 0.6.3"
}
}
View
5 test/common_test.js
@@ -20,7 +20,8 @@ var schemas = {
mongoose: { url: 'mongodb://travis:test@localhost:27017/myapp' },
mongodb: { url: 'mongodb://travis:test@localhost:27017/myapp' },
redis: {},
- memory: {}
+ memory: {},
+ cradle: {}
};
var specificTest = getSpecificTests();
@@ -579,7 +580,7 @@ function testOrm(schema) {
});
- if (schema.name !== 'redis' && schema.name !== 'memory' && schema.name !== 'neo4j')
+ if (schema.name !== 'redis' && schema.name !== 'memory' && schema.name !== 'neo4j' && schema.name !== 'cradle')
it('should allow advanced queying: lt, gt, lte, gte, between', function (test) {
Post.destroyAll(function () {
Post.create({date: new Date('Wed, 01 Feb 2012 13:56:12 GMT')}, done);
View
1 test/performance.coffee
@@ -10,6 +10,7 @@ schemas =
url: 'mongodb://localhost/test'
redis: {}
memory: {}
+ cradle: {}
testOrm = (schema) ->

0 comments on commit d333fe1

Please sign in to comment.
Something went wrong with that request. Please try again.