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

@ -2,7 +2,6 @@
* The datastore itself * The datastore itself
* TODO * TODO
* Queue operations * Queue operations
* Enable upserts
* Update and removes should only modify the corresponding part of the database * Update and removes should only modify the corresponding part of the database
*/ */
@ -84,7 +83,7 @@ Datastore.prototype.insert = function (newDoc, cb) {
; ;
try { try {
newDoc._id = customUtils.uid(16); newDoc._id = newDoc._id || customUtils.uid(16);
persistableNewDoc = model.serialize(newDoc); persistableNewDoc = model.serialize(newDoc);
} catch (e) { } catch (e) {
return callback(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 * Update all docs matching query
* For now, very naive implementation (recalculating the whole database) * 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) { self.data.forEach(function (d) {
if (Datastore.match(d, query) && (multi || numReplaced === 0)) { if (Datastore.match(d, query) && (multi || numReplaced === 0)) {
numReplaced += 1; numReplaced += 1;
newData.push(Datastore.modify(d, newDoc)); newData.push(model.modify(d, newDoc));
} else { } else {
newData.push(d); 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 * Serialize an object to be persisted to a one-line string
* Accepted primitive types: Number, String, Boolean, Date, null * Accepted primitive types: Number, String, Boolean, Date, null
* Accepted secondary types: Objects, Arrays * Accepted secondary types: Objects, Arrays
* TODO: throw an error if variable name begins with '$'
*/ */
function serialize (obj) { function serialize (obj) {
var res; 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; Date.prototype.toJSON = dateToJSON;
res = JSON.stringify(obj, function (k, v) { 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 (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; 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 // Interface
module.exports.serialize = serialize; module.exports.serialize = serialize;
module.exports.deserialize = deserialize; module.exports.deserialize = deserialize;
module.exports.deepCopy = deepCopy; module.exports.deepCopy = deepCopy;
module.exports.modify = modify;

@ -36,7 +36,7 @@ describe('Database', function () {
describe('Insert', 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) { d.find({}, function (err, docs) {
docs.length.should.equal(0); 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' ==== // }); // ==== End of 'Insert' ==== //

@ -112,6 +112,18 @@ describe('Model', function () {
done(); 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' ==== // }); // ==== End of 'Serialization, deserialization' ==== //

Loading…
Cancel
Save