diff --git a/.travis.yml b/.travis.yml index df3cac3b..51973f49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - 0.4 + - 0.4.12 - 0.6 before_install: - git submodule init && git submodule --quiet update diff --git a/lib/abstract-class.js b/lib/abstract-class.js index 402de391..9b2d2b42 100644 --- a/lib/abstract-class.js +++ b/lib/abstract-class.js @@ -5,6 +5,7 @@ var Validatable = require('./validatable').Validatable; var Hookable = require('./hookable').Hookable; var util = require('util'); var jutil = require('./jutil'); +var DEFAULT_CACHE_LIMIT = 1000; exports.AbstractClass = AbstractClass; @@ -154,7 +155,7 @@ AbstractClass.create = function (data, callback) { this._adapter().create(modelName, data, function (err, id) { if (id) { defineReadonlyProp(obj, 'id', id); - this.constructor.cache[id] = obj; + addToCache(this.constructor, obj); } done.call(this, function () { if (callback) { @@ -178,14 +179,15 @@ AbstractClass.find = function find(id, cb) { this.schema.adapter.find(this.modelName, id, function (err, data) { var obj = null; if (data) { - if (this.cache[data.id]) { - obj = this.cache[data.id]; + var cached = getCached(this, data.id); + if (cached) { + obj = cached; substractDirtyAttributes(obj, data); this.call(obj, data); } else { data.id = id; obj = new this(data); - this.cache[data.id] = obj; + addToCache(this, id); } } cb(err, obj); @@ -209,14 +211,15 @@ AbstractClass.all = function all(params, cb) { collection = data.map(function (d) { var obj = null; // do not create different instances for the same object - if (d.id && constr.cache[d.id]) { - obj = constr.cache[d.id]; + var cached = getCached(constr, d.id); + if (cached) { + obj = cached; // keep dirty attributes untouthed (remove from dataset) substractDirtyAttributes(obj, d); constr.call(obj, d); } else { obj = new constr(d); - if (d.id) constr.cache[d.id] = obj; + if (obj.id) addToCache(constr, obj); } return obj; }); @@ -248,9 +251,7 @@ function substractDirtyAttributes(object, data) { AbstractClass.destroyAll = function destroyAll(cb) { this.schema.adapter.destroyAll(this.modelName, function (err) { if (!err) { - Object.keys(this.cache).forEach(function (id) { - delete this.cache[id]; - }.bind(this)); + clearCache(this); } cb(err); }.bind(this)); @@ -365,7 +366,7 @@ AbstractClass.prototype.toObject = function (onlySchema) { AbstractClass.prototype.destroy = function (cb) { this.trigger('destroy', function (destroyed) { this._adapter().destroy(this.constructor.modelName, this.id, function (err) { - delete this.constructor.cache[this.id]; + removeFromCache(this.constructor, this.id); destroyed(function () { cb && cb(err); }); @@ -442,7 +443,7 @@ AbstractClass.prototype.propertyChanged = function (attr) { }; AbstractClass.prototype.reload = function (cb) { - var obj = this.constructor.cache[this.id]; + var obj = getCached(this.constructor, this.id); if (obj) { obj.reset(); } @@ -633,6 +634,7 @@ function defineScope(cls, targetClass, name, params, methods) { // helper methods // + function isdef(s) { var undef; return s !== undef; @@ -657,3 +659,37 @@ function defineReadonlyProp(obj, key, value) { }); } +function addToCache(constr, obj) { + touchCache(constr, obj.id); + constr.cache[obj.id] = obj; +} + +function touchCache(constr, id) { + var cacheLimit = constr.CACHE_LIMIT || DEFAULT_CACHE_LIMIT; + + var ind = constr.mru.indexOf(id); + if (~ind) constr.mru.splice(ind, 1); + if (constr.mru.length >= cacheLimit * 2) { + for (var i = 0; i < cacheLimit;i += 1) { + delete constr.cache[constr.mru[i]]; + } + constr.mru.splice(0, cacheLimit); + } +} + +function getCached(constr, id) { + if (id) touchCache(constr, id); + return id && constr.cache[id]; +} + +function clearCache(constr) { + constr.cache = {}; + constr.mru = []; +} + +function removeFromCache(constr, id) { + var ind = constr.mru.indexOf(id); + if (!~ind) constr.mru.splice(ind, 1); + delete constr.cache[id]; +} + diff --git a/lib/schema.js b/lib/schema.js index 564ca179..d7a09059 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -154,6 +154,7 @@ Schema.prototype.define = function defineClass(className, properties, settings) hiddenProperty(newClass, 'schema', schema); hiddenProperty(newClass, 'modelName', className); hiddenProperty(newClass, 'cache', {}); + hiddenProperty(newClass, 'mru', []); // setup inheritance newClass.__proto__ = AbstractClass; diff --git a/package.json b/package.json index c86a4c76..5da09060 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,20 @@ { "name": "jugglingdb", - "author": "Anatoliy Chakkaev", "description": "ORM for every database: redis, mysql, neo4j, mongodb, postgres, sqlite", - "version": "0.1.3", + "version": "0.1.4", + "author": "Anatoliy Chakkaev ", + "contributors": [ + { "name": "Anatoliy Chakkaev", "email": "rpm1602@gmail.com" }, + { "name": "Julien Guimont", "email": "julien.guimont@gmail.com" }, + { "name": "Henri Bergius", "email": "henri.bergius@iki.fi" }, + { "name": "redvulps", "email": "fabopereira@gmail.com" }, + { "name": "Amir M. Mahmoudi", "email": "a@geeknux.com" }, + { "name": "Justinas Stankevičius", "email": "justinas@justinas.me" }, + { "name": "Felipe Sateler", "email": "fsateler@gmail.com" }, + { "name": "Rick O'Toole", "email": "patrick.n.otoole@gmail.com" } + ], "repository": { + "type": "git", "url": "https://github.com/1602/jugglingdb" }, "main": "index.js", @@ -11,22 +22,22 @@ "test": "nodeunit test/*_test*" }, "engines": [ - "node >= 0.4.0" + "node >= 0.4.12" ], "dependencies": { - "node-uuid": "*" + "node-uuid": ">= 1.3.3" }, "devDependencies": { - "redis": ">= 0.6.7", - "mongoose": ">= 2.2.3", - "mysql": ">= 0.9.4", - "sequelize": "*", - "pg": "*", - "sqlite3": "*", - "nodeunit": ">= 0", - "coffee-script": ">= 0", - "riak-js": ">= 0", - "neo4j": ">= 0", - "mongodb": "*" + "coffee-script": ">= 1.2.0", + "nodeunit": ">= 0.6.4", + "redis": ">= 0.6.7", + "mongoose": ">= 2.2.3", + "mysql": ">= 0.9.4", + "pg": ">= 0.6.9", + "sqlite3": ">= 2.1.1", + "riak-js": ">= 0.4.1", + "neo4j": ">= 0.2.5", + "mongodb": ">= 0.9.9", + "felix-couchdb": ">= 1.0.3" } } diff --git a/test/common_test.js b/test/common_test.js index 0696d878..a46bb457 100644 --- a/test/common_test.js +++ b/test/common_test.js @@ -18,7 +18,7 @@ var schemas = { }, neo4j: { url: 'http://localhost:7474/' }, mongoose: { url: 'mongodb://travis:test@localhost:27017/myapp' }, - mongodb: { url: 'mongodb://travis:test@localhost:27017/myapp' }, + mongodb: { url: 'mongodb://travis:test@localhost:27017/myapp' }, redis: {}, memory: {} };