Comparing version 0.0.1 to 0.0.2
var fs = require('fs') | ||
, crypto = require('crypto') | ||
, path = require('path') | ||
; | ||
@@ -7,13 +8,27 @@ | ||
/** | ||
* Check if directory exists and create it on the fly if it is not the case | ||
* TODO: make recursive | ||
* Check if a directory exists and create it on the fly if it is not the case | ||
* Does it recursively | ||
* cb is optional, signature: err | ||
*/ | ||
function ensureDirectoryExists (dir, cb) { | ||
var callback = cb || function () {}; | ||
var callback = cb || function () {} | ||
, parts, currentPart | ||
; | ||
fs.exists(dir, function (exists) { | ||
if (typeof dir === 'string') { return ensureDirectoryExists({ toTreat: dir, treated: '' }, callback); } | ||
if (dir.toTreat.length === 0) { return callback(); } | ||
parts = dir.toTreat.split(path.sep); | ||
currentPart = path.join(dir.treated, parts[0]); | ||
parts = parts.slice(1).join(path.sep); | ||
fs.exists(currentPart, function (exists) { | ||
if (exists) { | ||
return callback(); | ||
return ensureDirectoryExists({ toTreat: parts, treated: currentPart }, callback); | ||
} else { | ||
fs.mkdir(dir, '0777', callback); | ||
return fs.mkdir(currentPart, '0777', function (err) { | ||
if (err) { return callback(err); } | ||
return ensureDirectoryExists({ toTreat: parts, treated: currentPart }, callback); | ||
}); | ||
} | ||
@@ -38,28 +53,4 @@ }); | ||
/** | ||
* Deep copy an DB object | ||
* TODO: Put in serialization/deserialization and tackle all cases | ||
*/ | ||
function deepCopy (obj) { | ||
var res; | ||
if (typeof obj === 'boolean' || typeof obj === 'number' || typeof obj === 'string') { | ||
return obj; | ||
} | ||
if (typeof obj === 'object') { | ||
res = {}; | ||
Object.keys(obj).forEach(function (k) { | ||
res[k] = deepCopy(obj[k]); | ||
}); | ||
return res; | ||
} | ||
return undefined; // For now everything else is undefined. We should probably throw an error instead | ||
} | ||
module.exports.ensureDirectoryExists = ensureDirectoryExists; | ||
module.exports.uid = uid; | ||
module.exports.deepCopy = deepCopy; |
@@ -196,3 +196,3 @@ /** | ||
Datastore.modify = function (obj, updateQuery) { | ||
updateQuery = customUtils.deepCopy(updateQuery); | ||
updateQuery = model.deepCopy(updateQuery); | ||
updateQuery._id = obj._id; | ||
@@ -199,0 +199,0 @@ return updateQuery; |
@@ -51,5 +51,38 @@ /** | ||
/** | ||
* Deep copy a DB object | ||
*/ | ||
function deepCopy (obj) { | ||
var res; | ||
if ( typeof obj === 'boolean' || | ||
typeof obj === 'number' || | ||
typeof obj === 'string' || | ||
obj === null || | ||
(obj && obj.constructor && obj.constructor.name === 'Date') ) { | ||
return obj; | ||
} | ||
if (obj instanceof Array) { | ||
res = []; | ||
obj.forEach(function (o) { res.push(o); }); | ||
return res; | ||
} | ||
if (typeof obj === 'object') { | ||
res = {}; | ||
Object.keys(obj).forEach(function (k) { | ||
res[k] = deepCopy(obj[k]); | ||
}); | ||
return res; | ||
} | ||
return undefined; // For now everything else is undefined. We should probably throw an error instead | ||
} | ||
// Interface | ||
module.exports.serialize = serialize; | ||
module.exports.deserialize = deserialize; | ||
module.exports.deepCopy = deepCopy; |
{ | ||
"name": "nedb", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"author": { | ||
@@ -5,0 +5,0 @@ "name": "tldr.io", |
@@ -14,10 +14,23 @@ var Datastore = require('../lib/datastore') | ||
describe('Database', function () { | ||
var d = new Datastore(testDb); | ||
beforeEach(function (done) { | ||
customUtils.ensureDirectoryExists(path.dirname(testDb), function () { | ||
fs.exists(testDb, function (exists) { | ||
if (exists) { | ||
fs.unlink(testDb, done); | ||
} else { return done(); } | ||
}); | ||
}); | ||
async.waterfall([ | ||
function (cb) { | ||
customUtils.ensureDirectoryExists(path.dirname(testDb), function () { | ||
fs.exists(testDb, function (exists) { | ||
if (exists) { | ||
fs.unlink(testDb, cb); | ||
} else { return cb(); } | ||
}); | ||
}); | ||
} | ||
, function (cb) { | ||
d.loadDatabase(function (err) { | ||
assert.isNull(err); | ||
return cb(); | ||
}); | ||
} | ||
], done); | ||
}); | ||
@@ -28,29 +41,25 @@ | ||
it('Able to insert a document in the database and retrieve it even after a reload', function (done) { | ||
var d = new Datastore(testDb); | ||
d.loadDatabase(function (err) { | ||
assert.isNull(err); | ||
d.find({}, function (err, docs) { | ||
docs.length.should.equal(0); | ||
d.find({}, function (err, docs) { | ||
docs.length.should.equal(0); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
// The data was correctly updated | ||
d.find({}, function (err, docs) { | ||
assert.isNull(err); | ||
docs.length.should.equal(1); | ||
Object.keys(docs[0]).length.should.equal(2); | ||
docs[0].somedata.should.equal('ok'); | ||
assert.isDefined(docs[0]._id); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
// The data was correctly updated | ||
d.find({}, function (err, docs) { | ||
assert.isNull(err); | ||
docs.length.should.equal(1); | ||
Object.keys(docs[0]).length.should.equal(2); | ||
docs[0].somedata.should.equal('ok'); | ||
assert.isDefined(docs[0]._id); | ||
// After a reload the data has been correctly persisted | ||
d.loadDatabase(function (err) { | ||
d.find({}, function (err, docs) { | ||
assert.isNull(err); | ||
docs.length.should.equal(1); | ||
Object.keys(docs[0]).length.should.equal(2); | ||
docs[0].somedata.should.equal('ok'); | ||
assert.isDefined(docs[0]._id); | ||
// After a reload the data has been correctly persisted | ||
d.loadDatabase(function (err) { | ||
d.find({}, function (err, docs) { | ||
assert.isNull(err); | ||
docs.length.should.equal(1); | ||
Object.keys(docs[0]).length.should.equal(2); | ||
docs[0].somedata.should.equal('ok'); | ||
assert.isDefined(docs[0]._id); | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
@@ -64,18 +73,14 @@ }); | ||
it('Can insert multiple documents in the database', function (done) { | ||
var d = new Datastore(testDb); | ||
d.loadDatabase(function (err) { | ||
assert.isNull(err); | ||
d.find({}, function (err, docs) { | ||
docs.length.should.equal(0); | ||
d.find({}, function (err, docs) { | ||
docs.length.should.equal(0); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'another' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { | ||
d.find({}, function (err, docs) { | ||
docs.length.should.equal(3); | ||
_.pluck(docs, 'somedata').should.contain('ok'); | ||
_.pluck(docs, 'somedata').should.contain('another'); | ||
_.pluck(docs, 'somedata').should.contain('again'); | ||
done(); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'another' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { | ||
d.find({}, function (err, docs) { | ||
docs.length.should.equal(3); | ||
_.pluck(docs, 'somedata').should.contain('ok'); | ||
_.pluck(docs, 'somedata').should.contain('another'); | ||
_.pluck(docs, 'somedata').should.contain('again'); | ||
done(); | ||
}); | ||
@@ -88,2 +93,23 @@ }); | ||
it('Can insert and get back from DB complex objects with all primitive and secondary types', function (done) { | ||
var da = new Date() | ||
, obj = { a: ['ee', 'ff', 42], date: da, subobj: { a: 'b', b: 'c' } } | ||
; | ||
d.insert(obj, function (err) { | ||
d.findOne({}, function (err, res) { | ||
assert.isNull(err); | ||
res.a.length.should.equal(3); | ||
res.a[0].should.equal('ee'); | ||
res.a[1].should.equal('ff'); | ||
res.a[2].should.equal(42); | ||
res.date.getTime().should.equal(da.getTime()); | ||
res.subobj.a.should.equal('b'); | ||
res.subobj.b.should.equal('c'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); // ==== End of 'Insert' ==== // | ||
@@ -95,11 +121,7 @@ | ||
it('Can find all documents an empty query is used', function (done) { | ||
var d = new Datastore(testDb); | ||
async.waterfall([ | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'another', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { return cb(err); }); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'another', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { return cb(err); }); | ||
}); | ||
@@ -123,11 +145,7 @@ }); | ||
it('Can find all documents matching a basic query', function (done) { | ||
var d = new Datastore(testDb); | ||
async.waterfall([ | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { return cb(err); }); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { return cb(err); }); | ||
}); | ||
@@ -155,11 +173,7 @@ }); | ||
it('Can find one document matching a basic query and return null if none is found', function (done) { | ||
var d = new Datastore(testDb); | ||
async.waterfall([ | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { return cb(err); }); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'again' }, function (err) { return cb(err); }); | ||
}); | ||
@@ -193,11 +207,7 @@ }); | ||
it("If the query doesn't match anything, database is not modified", function (done) { | ||
var d = new Datastore(testDb); | ||
async.waterfall([ | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'another' }, function (err) { return cb(err); }); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err) { | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err) { | ||
d.insert({ somedata: 'another' }, function (err) { return cb(err); }); | ||
}); | ||
@@ -240,4 +250,3 @@ }); | ||
it("Can update multiple documents matching the query", function (done) { | ||
var d = new Datastore(testDb) | ||
, id1, id2, id3; | ||
var id1, id2, id3; | ||
@@ -273,11 +282,9 @@ // Test DB state after update and reload | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err, doc1) { | ||
id1 = doc1._id; | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err, doc2) { | ||
id2 = doc2._id; | ||
d.insert({ somedata: 'again' }, function (err, doc3) { | ||
id3 = doc3._id; | ||
return cb(err); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err, doc1) { | ||
id1 = doc1._id; | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err, doc2) { | ||
id2 = doc2._id; | ||
d.insert({ somedata: 'again' }, function (err, doc3) { | ||
id3 = doc3._id; | ||
return cb(err); | ||
}); | ||
@@ -303,4 +310,3 @@ }); | ||
it("Can update only one document matching the query", function (done) { | ||
var d = new Datastore(testDb) | ||
, id1, id2, id3; | ||
var id1, id2, id3; | ||
@@ -337,11 +343,9 @@ // Test DB state after update and reload | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err, doc1) { | ||
id1 = doc1._id; | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err, doc2) { | ||
id2 = doc2._id; | ||
d.insert({ somedata: 'again' }, function (err, doc3) { | ||
id3 = doc3._id; | ||
return cb(err); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err, doc1) { | ||
id1 = doc1._id; | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err, doc2) { | ||
id2 = doc2._id; | ||
d.insert({ somedata: 'again' }, function (err, doc3) { | ||
id3 = doc3._id; | ||
return cb(err); | ||
}); | ||
@@ -372,4 +376,3 @@ }); | ||
it('Can remove multiple documents', function (done) { | ||
var d = new Datastore(testDb) | ||
, id1, id2, id3; | ||
var id1, id2, id3; | ||
@@ -392,11 +395,9 @@ // Test DB status | ||
function (cb) { | ||
d.loadDatabase(function (err) { | ||
d.insert({ somedata: 'ok' }, function (err, doc1) { | ||
id1 = doc1._id; | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err, doc2) { | ||
id2 = doc2._id; | ||
d.insert({ somedata: 'again' }, function (err, doc3) { | ||
id3 = doc3._id; | ||
return cb(err); | ||
}); | ||
d.insert({ somedata: 'ok' }, function (err, doc1) { | ||
id1 = doc1._id; | ||
d.insert({ somedata: 'again', plus: 'additional data' }, function (err, doc2) { | ||
id2 = doc2._id; | ||
d.insert({ somedata: 'again' }, function (err, doc3) { | ||
id3 = doc3._id; | ||
return cb(err); | ||
}); | ||
@@ -403,0 +404,0 @@ }); |
@@ -117,2 +117,37 @@ var model = require('../lib/model') | ||
describe('Deep copying', function () { | ||
it('Should be able to deep copy any serializable model', function () { | ||
var d = new Date() | ||
, obj = { a: ['ee', 'ff', 42], date: d, subobj: { a: 'b', b: 'c' } } | ||
, res = model.deepCopy(obj); | ||
; | ||
res.a.length.should.equal(3); | ||
res.a[0].should.equal('ee'); | ||
res.a[1].should.equal('ff'); | ||
res.a[2].should.equal(42); | ||
res.date.getTime().should.equal(d.getTime()); | ||
res.subobj.a.should.equal('b'); | ||
res.subobj.b.should.equal('c'); | ||
obj.a.push('ggg'); | ||
obj.date = 'notadate'; | ||
obj.subobj = []; | ||
// Even if the original object is modified, the copied one isn't | ||
res.a.length.should.equal(3); | ||
res.a[0].should.equal('ee'); | ||
res.a[1].should.equal('ff'); | ||
res.a[2].should.equal(42); | ||
res.date.getTime().should.equal(d.getTime()); | ||
res.subobj.a.should.equal('b'); | ||
res.subobj.b.should.equal('c'); | ||
}); | ||
}); // ==== End of 'Deep copying' ==== // | ||
}); |
41152
1085