diff --git a/packages/livedata/livedata_connection.js b/packages/livedata/livedata_connection.js index 647bacf601..26d1ef900c 100644 --- a/packages/livedata/livedata_connection.js +++ b/packages/livedata/livedata_connection.js @@ -980,7 +980,7 @@ _.extend(Meteor._LivedataConnection.prototype, { + msg.id); } serverDoc.document = msg.fields || {}; - serverDoc.document._id = LocalCollection._idParse(msg.id); + serverDoc.document._id = Meteor.idParse(msg.id); } else { self._pushUpdate(updates, msg.collection, msg); } diff --git a/packages/livedata/livedata_connection_tests.js b/packages/livedata/livedata_connection_tests.js index 2b3d699491..a5a03ce241 100644 --- a/packages/livedata/livedata_connection_tests.js +++ b/packages/livedata/livedata_connection_tests.js @@ -336,7 +336,7 @@ Tinytest.add("livedata stub - methods", function (test) { test.equal(counts, {added: 1, removed: 0, changed: 0, moved: 0}); // data methods do not show up (not quiescent yet) - stream.receive({msg: 'added', collection: collName, id: LocalCollection._idStringify(docId), + stream.receive({msg: 'added', collection: collName, id: Meteor.idStringify(docId), fields: {value: 'tuesday'}}); test.equal(coll.find({}).count(), 1); test.equal(coll.find({value: 'friday!'}).count(), 1); @@ -444,7 +444,7 @@ Tinytest.add("livedata stub - methods calling methods", function (test) { // get data from the method. data from this doc does not show up yet, but data // from another doc does. - stream.receive({msg: 'added', collection: coll_name, id: LocalCollection._idStringify(docId), + stream.receive({msg: 'added', collection: coll_name, id: Meteor.idStringify(docId), fields: {value: 'tuesday'}}); o.expectCallbacks(); test.equal(coll.findOne(docId), {_id: docId, a: 1}); @@ -655,7 +655,7 @@ Tinytest.add("livedata stub - reconnect method which only got result", function // Get some data. stream.receive({msg: 'added', collection: collName, - id: LocalCollection._idStringify(stubWrittenId), fields: {baz: 42}}); + id: Meteor.idStringify(stubWrittenId), fields: {baz: 42}}); // It doesn't show up yet. test.equal(coll.find().count(), 1); test.equal(coll.findOne(stubWrittenId), {_id: stubWrittenId, foo: 'bar'}); @@ -690,7 +690,7 @@ Tinytest.add("livedata stub - reconnect method which only got result", function test.equal(callbackOutput, ['bla']); test.equal(onResultReceivedOutput, ['bla']); stream.receive({msg: 'added', collection: collName, - id: LocalCollection._idStringify(stubWrittenId), fields: {baz: 42}}); + id: Meteor.idStringify(stubWrittenId), fields: {baz: 42}}); test.equal(coll.findOne(stubWrittenId), {_id: stubWrittenId, baz: 42}); o.expectCallbacks({added: 1}); @@ -722,7 +722,7 @@ Tinytest.add("livedata stub - reconnect method which only got result", function // Get some data. stream.receive({msg: 'added', collection: collName, - id: LocalCollection._idStringify(stubWrittenId2), fields: {baz: 42}}); + id: Meteor.idStringify(stubWrittenId2), fields: {baz: 42}}); // It doesn't show up yet. test.equal(coll.find().count(), 2); test.equal(coll.findOne(stubWrittenId2), {_id: stubWrittenId2, foo: 'bar'}); @@ -766,7 +766,7 @@ Tinytest.add("livedata stub - reconnect method which only got result", function // Receive data matching our stub. It doesn't take effect yet. stream.receive({msg: 'added', collection: collName, - id: LocalCollection._idStringify(stubWrittenId2), fields: {foo: 'bar'}}); + id: Meteor.idStringify(stubWrittenId2), fields: {foo: 'bar'}}); o.expectCallbacks(); // slowMethod is done writing, so we get full reconnect quiescence (but no @@ -922,7 +922,7 @@ Tinytest.add("livedata stub - multiple stubs same doc", function (test) { // Get some data... slightly different than what we wrote. stream.receive({msg: 'added', collection: collName, - id: LocalCollection._idStringify(stubWrittenId), fields: {foo: 'barb', other: 'field', + id: Meteor.idStringify(stubWrittenId), fields: {foo: 'barb', other: 'field', other2: 'bla'}}); // It doesn't show up yet. test.equal(coll.find().count(), 1); @@ -940,7 +940,7 @@ Tinytest.add("livedata stub - multiple stubs same doc", function (test) { // More data. Not quite what we wrote. Also ignored for now. stream.receive({msg: 'changed', collection: collName, - id: LocalCollection._idStringify(stubWrittenId), fields: {baz: 43}, cleared: ['other']}); + id: Meteor.idStringify(stubWrittenId), fields: {baz: 43}, cleared: ['other']}); test.equal(coll.find().count(), 1); test.equal(coll.findOne(stubWrittenId), {_id: stubWrittenId, foo: 'bar', baz: 42}); diff --git a/packages/livedata/livedata_server.js b/packages/livedata/livedata_server.js index ee3f789676..f3f529a523 100644 --- a/packages/livedata/livedata_server.js +++ b/packages/livedata/livedata_server.js @@ -789,8 +789,8 @@ Meteor._LivedataSubscription = function (session, subscriptionId) { // a ddp consumer that isn't minimongo self._idFilter = { - idStringify: LocalCollection._idStringify, - idParse: LocalCollection._idParse + idStringify: Meteor.idStringify, + idParse: Meteor.idParse }; }; diff --git a/packages/minimongo/minimongo.js b/packages/minimongo/minimongo.js index cc86323187..97408a88c4 100644 --- a/packages/minimongo/minimongo.js +++ b/packages/minimongo/minimongo.js @@ -719,3 +719,52 @@ LocalCollection.prototype.resumeObservers = function () { query.results_snapshot = null; } }; + + +LocalCollection._idStringify = function (id) { + if (id instanceof LocalCollection._findObjectIDClass()) { + return id.valueOf(); + } else if (typeof id === 'string') { + if (id === "") { + return id; + } else if (id[0] === "-" || // escape previously dashed strings + id[0] === "~" || // escape escaped numbers, true, false + LocalCollection._looksLikeObjectID(id) || // escape object-id-form strings + id[0] === '{') { // escape object-form strings, for maybe implementing later + return "-" + id; + } else { + return id; // other strings go through unchanged. + } + } else if (id === undefined) { + return '-'; + } else if (typeof id === 'object') { + throw new Error("Meteor does not currently support objects other than ObjectID as ids"); + } else { // Numbers, true, false, null + return "~" + JSON.stringify(id); + } +}; + + + +LocalCollection._idParse = function (id) { + if (id === "") { + return id; + } else if (id === '-') { + return undefined; + } else if (id[0] === '-') { + return id.substr(1); + } else if (id[0] === '~') { + return JSON.parse(id.substr(1)); + } else if (LocalCollection._looksLikeObjectID(id)) { + return new (LocalCollection._findObjectIDClass())(id); + } else { + return id; + } +}; + +if (typeof Meteor !== 'undefined') { + Meteor.idParse = LocalCollection._idParse; + Meteor.idStringify = LocalCollection._idStringify; +} + +})(); diff --git a/packages/mongo-livedata/collection.js b/packages/mongo-livedata/collection.js index 2d5d3fb410..e62a05492c 100644 --- a/packages/mongo-livedata/collection.js +++ b/packages/mongo-livedata/collection.js @@ -96,7 +96,7 @@ Meteor.Collection = function (name, options) { // Apply an update. // XXX better specify this interface (not in terms of a wire message)? update: function (msg) { - var mongoId = msg.id && LocalCollection._idParse(msg.id); + var mongoId = msg.id && Meteor.idParse(msg.id); var doc = self._collection.findOne(mongoId); // Is this a "replace the whole doc" message coming from the quiescence @@ -661,4 +661,5 @@ Meteor.Collection.prototype._validatedRemove = function(userId, selector) { self._collection.remove.call(self._collection, idSelector); }; + })(); diff --git a/packages/mongo-livedata/mongo_driver.js b/packages/mongo-livedata/mongo_driver.js index 8d520a8b0b..5a9caa0375 100644 --- a/packages/mongo-livedata/mongo_driver.js +++ b/packages/mongo-livedata/mongo_driver.js @@ -375,7 +375,7 @@ _.extend(SynchronousCursor.prototype, { while (true) { var doc = self._synchronousNextObject().wait(); if (!doc || !doc._id) return null; - var strId = LocalCollection._idStringify(doc._id); + var strId = Meteor.idStringify(doc._id); if (self._visitedIds[strId]) continue; self._visitedIds[strId] = true; return doc;