Merge pull request #67 from strongloop/feature/memory-persistence
Add an option for the memory connector to persist model instances
This commit is contained in:
commit
e65d21dcdb
|
@ -11,3 +11,4 @@ docs/html
|
|||
docs/man
|
||||
npm-debug.log
|
||||
.project
|
||||
test/memory.json
|
||||
|
|
|
@ -98,6 +98,7 @@ Connector.prototype.defineProperty = function (model, propertyName, propertyDefi
|
|||
*/
|
||||
Connector.prototype.disconnect = function disconnect(cb) {
|
||||
// NO-OP
|
||||
cb && process.nextTick(cb);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,8 @@ var util = require('util');
|
|||
var Connector = require('../connector');
|
||||
var geo = require('../geo');
|
||||
var utils = require('../utils');
|
||||
var fs = require('fs');
|
||||
var async = require('async');
|
||||
|
||||
/**
|
||||
* Initialize the Oracle connector against the given data source
|
||||
|
@ -10,24 +12,24 @@ var utils = require('../utils');
|
|||
* @param {Function} [callback] The callback function
|
||||
*/
|
||||
exports.initialize = function initializeDataSource(dataSource, callback) {
|
||||
dataSource.connector = new Memory();
|
||||
dataSource.connector = new Memory(null, dataSource.settings);
|
||||
dataSource.connector.connect(callback);
|
||||
};
|
||||
|
||||
exports.Memory = Memory;
|
||||
|
||||
function Memory(m) {
|
||||
if (m) {
|
||||
function Memory(m, settings) {
|
||||
if (m instanceof Memory) {
|
||||
this.isTransaction = true;
|
||||
this.cache = m.cache;
|
||||
this.ids = m.ids;
|
||||
this.constructor.super_.call(this, 'memory');
|
||||
this.constructor.super_.call(this, 'memory', settings);
|
||||
this._models = m._models;
|
||||
} else {
|
||||
this.isTransaction = false;
|
||||
this.cache = {};
|
||||
this.ids = {};
|
||||
this.constructor.super_.call(this, 'memory');
|
||||
this.constructor.super_.call(this, 'memory', settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,16 +38,77 @@ util.inherits(Memory, Connector);
|
|||
Memory.prototype.connect = function (callback) {
|
||||
if (this.isTransaction) {
|
||||
this.onTransactionExec = callback;
|
||||
} else {
|
||||
this.loadFromFile(callback);
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.loadFromFile = function(callback) {
|
||||
var self = this;
|
||||
if (self.settings.file) {
|
||||
fs.readFile(self.settings.file, {encoding: 'utf8', flag: 'r'}, function (err, data) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
callback && callback(err);
|
||||
} else {
|
||||
if (data) {
|
||||
data = JSON.parse(data.toString());
|
||||
self.ids = data.ids || {};
|
||||
self.cache = data.models || {};
|
||||
} else {
|
||||
if(!self.cache) {
|
||||
self.ids = {};
|
||||
self.cache = {};
|
||||
}
|
||||
}
|
||||
callback && callback();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
process.nextTick(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Flush the cache into the json file if necessary
|
||||
* @param {Function} callback
|
||||
*/
|
||||
Memory.prototype.saveToFile = function (result, callback) {
|
||||
var self = this;
|
||||
if (this.settings.file) {
|
||||
if(!self.writeQueue) {
|
||||
// Create a queue for writes
|
||||
self.writeQueue = async.queue(function (task, cb) {
|
||||
// Flush out the models/ids
|
||||
var data = JSON.stringify({
|
||||
ids: self.ids,
|
||||
models: self.cache
|
||||
}, null, ' ');
|
||||
|
||||
fs.writeFile(self.settings.file, data, function (err) {
|
||||
cb(err);
|
||||
task.callback && task.callback(err, task.data);
|
||||
});
|
||||
}, 1);
|
||||
}
|
||||
// Enqueue the write
|
||||
self.writeQueue.push({
|
||||
data: result,
|
||||
callback: callback
|
||||
});
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, result);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.define = function defineModel(definition) {
|
||||
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
||||
var m = definition.model.modelName;
|
||||
if(!this.cache[m]) {
|
||||
this.cache[m] = {};
|
||||
this.ids[m] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.create = function create(model, data, callback) {
|
||||
|
@ -68,10 +131,11 @@ Memory.prototype.create = function create(model, data, callback) {
|
|||
var idName = this.idName(model);
|
||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||
this.setIdValue(model, data, id);
|
||||
if(!this.cache[model]) {
|
||||
this.cache[model] = {};
|
||||
}
|
||||
this.cache[model][id] = JSON.stringify(data);
|
||||
process.nextTick(function () {
|
||||
callback(null, id);
|
||||
});
|
||||
this.saveToFile(id, callback);
|
||||
};
|
||||
|
||||
Memory.prototype.updateOrCreate = function (model, data, callback) {
|
||||
|
@ -90,9 +154,7 @@ Memory.prototype.updateOrCreate = function (model, data, callback) {
|
|||
|
||||
Memory.prototype.save = function save(model, data, callback) {
|
||||
this.cache[model][this.getIdValue(model, data)] = JSON.stringify(data);
|
||||
process.nextTick(function () {
|
||||
callback(null, data);
|
||||
});
|
||||
this.saveToFile(data, callback);
|
||||
};
|
||||
|
||||
Memory.prototype.exists = function exists(model, id, callback) {
|
||||
|
@ -110,7 +172,7 @@ Memory.prototype.find = function find(model, id, callback) {
|
|||
|
||||
Memory.prototype.destroy = function destroy(model, id, callback) {
|
||||
delete this.cache[model][id];
|
||||
process.nextTick(callback);
|
||||
this.saveToFile(null, callback);
|
||||
};
|
||||
|
||||
Memory.prototype.fromDb = function (model, data) {
|
||||
|
@ -273,7 +335,7 @@ Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
|
|||
if (!where) {
|
||||
this.cache[model] = {};
|
||||
}
|
||||
process.nextTick(callback);
|
||||
this.saveToFile(null, callback);
|
||||
};
|
||||
|
||||
Memory.prototype.count = function count(model, callback, where) {
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
var jdb = require('../');
|
||||
var DataSource = jdb.DataSource;
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
|
||||
describe('Memory connector', function () {
|
||||
var file = path.join(__dirname, 'memory.json');
|
||||
|
||||
function readModels(done) {
|
||||
fs.readFile(file, function (err, data) {
|
||||
var json = JSON.parse(data.toString());
|
||||
assert(json.models);
|
||||
assert(json.ids.User);
|
||||
done(err, json);
|
||||
});
|
||||
}
|
||||
|
||||
before(function (done) {
|
||||
fs.unlink(file, function (err) {
|
||||
if (!err || err.code === 'ENOENT') {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should save to a json file', function (done) {
|
||||
var ds = new DataSource({
|
||||
connector: 'memory',
|
||||
file: file
|
||||
});
|
||||
|
||||
var User = ds.createModel('User', {
|
||||
name: String,
|
||||
bio: String,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
});
|
||||
|
||||
var count = 0;
|
||||
var ids = [];
|
||||
async.eachSeries(['John1', 'John2', 'John3'], function (item, cb) {
|
||||
User.create({name: item}, function (err, result) {
|
||||
ids.push(result.id);
|
||||
count++;
|
||||
readModels(function (err, json) {
|
||||
assert.equal(Object.keys(json.models.User).length, count);
|
||||
cb(err);
|
||||
});
|
||||
});
|
||||
}, function (err, results) {
|
||||
// Now try to delete one
|
||||
User.deleteById(ids[0], function (err) {
|
||||
readModels(function (err, json) {
|
||||
assert.equal(Object.keys(json.models.User).length, 2);
|
||||
User.upsert({id: ids[1], name: 'John'}, function(err, result) {
|
||||
readModels(function (err, json) {
|
||||
assert.equal(Object.keys(json.models.User).length, 2);
|
||||
var user = JSON.parse(json.models.User[ids[1]]);
|
||||
assert.equal(user.name, 'John');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// The saved memory.json from previous test should be loaded
|
||||
it('should load from the json file', function (done) {
|
||||
var ds = new DataSource({
|
||||
connector: 'memory',
|
||||
file: file
|
||||
});
|
||||
|
||||
var User = ds.createModel('User', {
|
||||
name: String,
|
||||
bio: String,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
});
|
||||
|
||||
User.find(function (err, users) {
|
||||
// There should be 2 records
|
||||
assert.equal(users.length, 2);
|
||||
done(err);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue