persistNewState uses the serialization hook

pull/2/head
Louis Chatriot 10 years ago
parent 3659cde79a
commit 64aa1808a9
  1. 6
      lib/datastore.js
  2. 16
      lib/persistence.js
  3. 96
      test/persistence.test.js

@ -18,6 +18,7 @@ var customUtils = require('./customUtils')
* Node Webkit stores application data such as cookies and local storage (the best place to store data in my opinion) * Node Webkit stores application data such as cookies and local storage (the best place to store data in my opinion)
* @param {Boolean} options.autoload Optional, defaults to false * @param {Boolean} options.autoload Optional, defaults to false
* @param {Function} options.onload Optional, if autoload is used this will be called after the load database with the error object as parameter. If you don't pass it the error will be thrown * @param {Function} options.onload Optional, if autoload is used this will be called after the load database with the error object as parameter. If you don't pass it the error will be thrown
* @param {Function} options.afterSerialization and options.beforeDeserialization Optional, serialization hooks
*/ */
function Datastore (options) { function Datastore (options) {
var filename; var filename;
@ -42,7 +43,10 @@ function Datastore (options) {
} }
// Persistence handling // Persistence handling
this.persistence = new Persistence({ db: this, nodeWebkitAppName: options.nodeWebkitAppName }); this.persistence = new Persistence({ db: this, nodeWebkitAppName: options.nodeWebkitAppName
, afterSerialization: options.afterSerialization
, beforeDeserialization: options.beforeDeserialization
});
// This new executor is ready if we don't use persistence // This new executor is ready if we don't use persistence
// If we do, it will only be ready once loadDatabase is called // If we do, it will only be ready once loadDatabase is called

@ -34,6 +34,10 @@ function Persistence (options) {
} }
} }
// After serialization and before deserialization hooks
this.afterSerialization = options.afterSerialization || function (s) { return s; };
this.beforeDeserialization = options.beforeDeserialization || function (s) { return s; };
// For NW apps, store data in the same directory where NW stores application data // For NW apps, store data in the same directory where NW stores application data
if (this.filename && options.nodeWebkitAppName) { if (this.filename && options.nodeWebkitAppName) {
console.log("=================================================================="); console.log("==================================================================");
@ -119,11 +123,11 @@ Persistence.prototype.persistCachedDatabase = function (cb) {
if (this.inMemoryOnly) { return callback(null); } if (this.inMemoryOnly) { return callback(null); }
this.db.getAllData().forEach(function (doc) { this.db.getAllData().forEach(function (doc) {
toPersist += model.serialize(doc) + '\n'; toPersist += self.afterSerialization(model.serialize(doc)) + '\n';
}); });
Object.keys(this.db.indexes).forEach(function (fieldName) { Object.keys(this.db.indexes).forEach(function (fieldName) {
if (fieldName != "_id") { // The special _id index is managed by datastore.js, the others need to be persisted if (fieldName != "_id") { // The special _id index is managed by datastore.js, the others need to be persisted
toPersist += model.serialize({ $$indexCreated: { fieldName: fieldName, unique: self.db.indexes[fieldName].unique, sparse: self.db.indexes[fieldName].sparse }}) + '\n'; toPersist += self.afterSerialization(model.serialize({ $$indexCreated: { fieldName: fieldName, unique: self.db.indexes[fieldName].unique, sparse: self.db.indexes[fieldName].sparse }})) + '\n';
} }
}); });
@ -200,7 +204,7 @@ Persistence.prototype.persistNewState = function (newDocs, cb) {
if (self.inMemoryOnly) { return callback(null); } if (self.inMemoryOnly) { return callback(null); }
newDocs.forEach(function (doc) { newDocs.forEach(function (doc) {
toPersist += model.serialize(doc) + '\n'; toPersist += self.afterSerialization(model.serialize(doc)) + '\n';
}); });
if (toPersist.length === 0) { return callback(null); } if (toPersist.length === 0) { return callback(null); }
@ -215,7 +219,7 @@ Persistence.prototype.persistNewState = function (newDocs, cb) {
* From a database's raw data, return the corresponding * From a database's raw data, return the corresponding
* machine understandable collection * machine understandable collection
*/ */
Persistence.treatRawData = function (rawData) { Persistence.prototype.treatRawData = function (rawData) {
var data = rawData.split('\n') var data = rawData.split('\n')
, dataById = {} , dataById = {}
, tdata = [] , tdata = []
@ -227,7 +231,7 @@ Persistence.treatRawData = function (rawData) {
var doc; var doc;
try { try {
doc = model.deserialize(data[i]); doc = model.deserialize(this.beforeDeserialization(data[i]));
if (doc._id) { if (doc._id) {
if (doc.$$deleted === true) { if (doc.$$deleted === true) {
delete dataById[doc._id]; delete dataById[doc._id];
@ -302,7 +306,7 @@ Persistence.prototype.loadDatabase = function (cb) {
storage.readFile(self.filename, 'utf8', function (err, rawData) { storage.readFile(self.filename, 'utf8', function (err, rawData) {
if (err) { return cb(err); } if (err) { return cb(err); }
var treatedData = Persistence.treatRawData(rawData); var treatedData = self.treatRawData(rawData);
// Recreate all indexes in the datafile // Recreate all indexes in the datafile
Object.keys(treatedData.indexes).forEach(function (key) { Object.keys(treatedData.indexes).forEach(function (key) {

@ -46,7 +46,7 @@ describe('Persistence', function () {
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' + model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "3", nested: { today: now } }) model.serialize({ _id: "3", nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
; ;
treatedData.sort(function (a, b) { return a._id - b._id; }); treatedData.sort(function (a, b) { return a._id - b._id; });
@ -61,7 +61,7 @@ describe('Persistence', function () {
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
'garbage\n' + 'garbage\n' +
model.serialize({ _id: "3", nested: { today: now } }) model.serialize({ _id: "3", nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
; ;
treatedData.sort(function (a, b) { return a._id - b._id; }); treatedData.sort(function (a, b) { return a._id - b._id; });
@ -75,7 +75,7 @@ describe('Persistence', function () {
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' + model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ nested: { today: now } }) model.serialize({ nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
; ;
treatedData.sort(function (a, b) { return a._id - b._id; }); treatedData.sort(function (a, b) { return a._id - b._id; });
@ -89,7 +89,7 @@ describe('Persistence', function () {
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", hello: 'world' }) + '\n' + model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", nested: { today: now } }) model.serialize({ _id: "1", nested: { today: now } })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
; ;
treatedData.sort(function (a, b) { return a._id - b._id; }); treatedData.sort(function (a, b) { return a._id - b._id; });
@ -104,7 +104,7 @@ describe('Persistence', function () {
model.serialize({ _id: "2", hello: 'world' }) + '\n' + model.serialize({ _id: "2", hello: 'world' }) + '\n' +
model.serialize({ _id: "1", $$deleted: true }) + '\n' + model.serialize({ _id: "1", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now }) model.serialize({ _id: "3", today: now })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
; ;
treatedData.sort(function (a, b) { return a._id - b._id; }); treatedData.sort(function (a, b) { return a._id - b._id; });
@ -118,7 +118,7 @@ describe('Persistence', function () {
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ _id: "2", $$deleted: true }) + '\n' + model.serialize({ _id: "2", $$deleted: true }) + '\n' +
model.serialize({ _id: "3", today: now }) model.serialize({ _id: "3", today: now })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
; ;
treatedData.sort(function (a, b) { return a._id - b._id; }); treatedData.sort(function (a, b) { return a._id - b._id; });
@ -132,8 +132,8 @@ describe('Persistence', function () {
, rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' + , rawData = model.serialize({ _id: "1", a: 2, ages: [1, 5, 12] }) + '\n' +
model.serialize({ $$indexCreated: { fieldName: "test", unique: true } }) + '\n' + model.serialize({ $$indexCreated: { fieldName: "test", unique: true } }) + '\n' +
model.serialize({ _id: "3", today: now }) model.serialize({ _id: "3", today: now })
, treatedData = Persistence.treatRawData(rawData).data , treatedData = d.persistence.treatRawData(rawData).data
, indexes = Persistence.treatRawData(rawData).indexes , indexes = d.persistence.treatRawData(rawData).indexes
; ;
Object.keys(indexes).length.should.equal(1); Object.keys(indexes).length.should.equal(1);
@ -268,6 +268,86 @@ describe('Persistence', function () {
}); });
}); });
describe.only('Data can be persisted using serialization hooks', function () {
it("A serialization hook can be used to transform data before writing new state to disk", function (done) {
var as = function (s) { return "before_" + s + "_after"; }
, hookTestFilename = 'workspace/hookTest.db'
, d = new Datastore({ filename: hookTestFilename, autoload: true
, afterSerialization: as
})
;
d.insert({ hello: "world" }, function () {
var _data = fs.readFileSync(hookTestFilename, 'utf8')
, data = _data.split('\n')
, doc0 = data[0].substring(7, data[0].length - 6)
;
data.length.should.equal(2);
data[0].substring(0, 7).should.equal('before_');
data[0].substring(data[0].length - 6).should.equal('_after');
doc0 = model.deserialize(doc0);
Object.keys(doc0).length.should.equal(2);
doc0.hello.should.equal('world');
d.insert({ p: 'Mars' }, function () {
var _data = fs.readFileSync(hookTestFilename, 'utf8')
, data = _data.split('\n')
, doc0 = data[0].substring(7, data[0].length - 6)
, doc1 = data[1].substring(7, data[1].length - 6)
;
data.length.should.equal(3);
data[0].substring(0, 7).should.equal('before_');
data[0].substring(data[0].length - 6).should.equal('_after');
data[1].substring(0, 7).should.equal('before_');
data[1].substring(data[1].length - 6).should.equal('_after');
doc0 = model.deserialize(doc0);
Object.keys(doc0).length.should.equal(2);
doc0.hello.should.equal('world');
doc1 = model.deserialize(doc1);
Object.keys(doc1).length.should.equal(2);
doc1.p.should.equal('Mars');
d.ensureIndex({ fieldName: 'idefix' }, function () {
var _data = fs.readFileSync(hookTestFilename, 'utf8')
, data = _data.split('\n')
, doc0 = data[0].substring(7, data[0].length - 6)
, doc1 = data[1].substring(7, data[1].length - 6)
, idx = data[2].substring(7, data[2].length - 6)
;
data.length.should.equal(4);
data[0].substring(0, 7).should.equal('before_');
data[0].substring(data[0].length - 6).should.equal('_after');
data[1].substring(0, 7).should.equal('before_');
data[1].substring(data[1].length - 6).should.equal('_after');
doc0 = model.deserialize(doc0);
Object.keys(doc0).length.should.equal(2);
doc0.hello.should.equal('world');
doc1 = model.deserialize(doc1);
Object.keys(doc1).length.should.equal(2);
doc1.p.should.equal('Mars');
idx = model.deserialize(idx);
assert.deepEqual(idx, { '$$indexCreated': { fieldName: 'idefix' } });
done();
});
});
});
});
});
describe('Prevent dataloss when persisting data', function () { describe('Prevent dataloss when persisting data', function () {

Loading…
Cancel
Save