Use given _id if any, otherwise a uid

pull/2/head
Louis Chatriot 12 years ago
parent d189b9aafb
commit 0854009b22
  1. 6
      lib/customUtils.js
  2. 16
      lib/datastore.js
  3. 21
      lib/model.js
  4. 20
      test/db.test.js
  5. 12
      test/model.test.js

@ -19,12 +19,12 @@ function ensureDirectoryExists (dir, cb) {
/**
* Return a random alphanumerical string of length len
* There is a very small probability for the length to be less than len
* (il the base64 conversion yields to many pluses and slashes) but
* There is a very small probability (less than 1/1,000,000) for the length to be less than len
* (il the base64 conversion yields too many pluses and slashes) but
* that's not an issue here
*/
function uid (len) {
return crypto.randomBytes(Math.ceil(len * 5 / 4))
return crypto.randomBytes(Math.ceil(Math.max(8, len * 2)))
.toString('base64')
.replace(/[+\/]/g, '')
.slice(0, len);

@ -2,7 +2,6 @@
* The datastore itself
* TODO
* Queue operations
* Enable upserts
* Update and removes should only modify the corresponding part of the database
*/
@ -84,7 +83,7 @@ Datastore.prototype.insert = function (newDoc, cb) {
;
try {
newDoc._id = customUtils.uid(16);
newDoc._id = newDoc._id || customUtils.uid(16);
persistableNewDoc = model.serialize(newDoc);
} catch (e) {
return callback(e);
@ -185,17 +184,6 @@ Datastore.prototype.persistWholeDatabase = function (data, cb) {
};
/**
* Modify an object according to the updateQuery
* For now the updateQuery only replaces the object
*/
Datastore.modify = function (obj, updateQuery) {
updateQuery = model.deepCopy(updateQuery);
updateQuery._id = obj._id;
return updateQuery;
};
/**
* Update all docs matching query
* For now, very naive implementation (recalculating the whole database)
@ -238,7 +226,7 @@ Datastore.prototype.update = function (query, newDoc, options, cb) {
self.data.forEach(function (d) {
if (Datastore.match(d, query) && (multi || numReplaced === 0)) {
numReplaced += 1;
newData.push(Datastore.modify(d, newDoc));
newData.push(model.modify(d, newDoc));
} else {
newData.push(d);
}

@ -11,17 +11,21 @@ var dateToJSON = function () { return { $$date: this.getTime() }; }
* Serialize an object to be persisted to a one-line string
* Accepted primitive types: Number, String, Boolean, Date, null
* Accepted secondary types: Objects, Arrays
* TODO: throw an error if variable name begins with '$'
*/
function serialize (obj) {
var res;
// Keep track of the fact this is a Date object
// Keep track of the fact that this is a Date object
Date.prototype.toJSON = dateToJSON;
res = JSON.stringify(obj, function (k, v) {
// Non-treatable edge case here: if part of the object if of the form { $$date: number }
// Its serialized-then-deserialized version it will transformed into a Date object
// But you really need to want it to trigger such behaviour, even when warned not to use '$' at the beginning of the field names...
if (k[0] === '$' && !(k === '$$date' && typeof v === 'number')) { throw 'Keys cannot begin with the $ character'; }
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) { return v; }
if (v && v.constructor && v.constructor.name === 'Date') { return { $$date: v.toString() }; }
//if (v && v.constructor && v.constructor.name === 'Date') { console.log("==============="); return { $$date: v.toString() }; }
return v;
});
@ -80,8 +84,19 @@ function deepCopy (obj) {
}
/**
* Modify a DB object according to an update query
* For now the updateQuery only replaces the object
*/
function modify (obj, updateQuery) {
updateQuery = deepCopy(updateQuery);
updateQuery._id = obj._id;
return updateQuery;
};
// Interface
module.exports.serialize = serialize;
module.exports.deserialize = deserialize;
module.exports.deepCopy = deepCopy;
module.exports.modify = modify;

@ -36,7 +36,7 @@ describe('Database', function () {
describe('Insert', function () {
it('Able to insert a document in the database and retrieve it even after a reload', function (done) {
it('Able to insert a document in the database, setting an _id if none provided, and retrieve it even after a reload', function (done) {
d.find({}, function (err, docs) {
docs.length.should.equal(0);
@ -132,6 +132,24 @@ describe('Database', function () {
});
});
it('Cannot insert a doc that has a field beginning with a $ sign', function (done) {
d.insert({ $something: 'atest' }, function (err) {
assert.isDefined(err);
done();
});
});
it('If an _id is already given when we insert a document, use it and not the default uid', function (done) {
d.insert({ _id: 'test', stuff: true }, function (err, newDoc) {
if (err) { return done(err); }
newDoc.stuff.should.equal(true);
newDoc._id.should.equal('test');
done();
});
});
}); // ==== End of 'Insert' ==== //

@ -112,6 +112,18 @@ describe('Model', function () {
done();
});
it('Reject field names beginning with a $ sign', function (done) {
var a = { $something: 'totest' }
, b;
try {
b = model.serialize(a);
return done('An error should have been thrown');
} catch (e) {
return done();
}
});
}); // ==== End of 'Serialization, deserialization' ==== //

Loading…
Cancel
Save