Fix client-side c.update({foo: /bar/}) and remove.

Minimongo supported JS RegExp object in selectors, but previously we serialized
them poorly over DDP. Fix by converting to the equivalent {foo: {$regex: 'bar'}}
form.

Fixes #346.
This commit is contained in:
David Glasser
2012-12-10 11:42:18 -08:00
parent 5e6bd79321
commit 28a136d7c2
3 changed files with 74 additions and 20 deletions

View File

@@ -163,6 +163,29 @@ _.extend(Meteor.Collection.prototype, {
});
// protect against dangerous selectors. falsey and {_id: falsey} are both
// likely programmer error, and not what you want, particularly for destructive
// operations. JS regexps don't serialize over DDP but can be trivially
// replaced by $regex.
Meteor.Collection._rewriteSelector = function (selector) {
// shorthand -- scalars match _id
if (LocalCollection._selectorIsId(selector))
selector = {_id: selector};
if (!selector || (('_id' in selector) && !selector._id))
// can't match anything
return {_id: Meteor.uuid()};
var ret = {};
_.each(selector, function (value, key) {
if (value instanceof RegExp)
ret[key] = {$regex: value.source};
else
ret[key] = value;
});
return ret;
};
// 'insert' immediately returns the inserted document's new _id. The
// others return nothing.
//
@@ -217,6 +240,8 @@ _.each(["insert", "update", "remove"], function (name) {
if ('_id' in args[0])
throw new Error("Do not pass an _id to insert. Meteor will generate the _id for you.");
ret = args[0]._id = Meteor.uuid();
} else {
args[0] = Meteor.Collection._rewriteSelector(args[0]);
}
if (self._manager && self._manager !== Meteor.default_server) {

View File

@@ -42,21 +42,6 @@ _Mongo = function (url) {
});
};
// protect against dangerous selectors. falsey and {_id: falsey}
// are both likely programmer error, and not what you want,
// particularly for destructive operations.
_Mongo._rewriteSelector = function (selector) {
// shorthand -- scalars match _id
if ((typeof selector === 'string') || (typeof selector === 'number'))
selector = {_id: selector};
if (!selector || (('_id' in selector) && !selector._id))
// can't match anything
return {_id: Meteor.uuid()};
else
return selector;
};
// callback: lambda (err, collection) called when
// collection is ready to go, or on error.
_Mongo.prototype._withCollection = function(collection_name, callback) {
@@ -145,9 +130,6 @@ _Mongo.prototype.remove = function (collection_name, selector) {
var write = self._maybeBeginWrite();
// XXX does not allow options. matches the client.
selector = _Mongo._rewriteSelector(selector);
var future = new Future;
self._withCollection(collection_name, function (err, collection) {
if (err) {
@@ -179,7 +161,6 @@ _Mongo.prototype.update = function (collection_name, selector, mod, options) {
var write = self._maybeBeginWrite();
selector = _Mongo._rewriteSelector(selector);
if (!options) options = {};
var future = new Future;
@@ -279,7 +260,7 @@ _Mongo.prototype._ensureIndex = function (collectionName, index, options) {
var CursorDescription = function (collectionName, selector, options) {
var self = this;
self.collectionName = collectionName;
self.selector = _Mongo._rewriteSelector(selector);
self.selector = Meteor.Collection._rewriteSelector(selector);
self.options = options || {};
};

View File

@@ -514,3 +514,51 @@ if (Meteor.isServer) {
onComplete();
});
}
if (Meteor.isServer) {
Meteor.methods({
createInsecureCollection: function (name) {
var c = new Meteor.Collection(name);
c._insecure = true;
Meteor.publish('c-' + name, function () {
return c.find();
});
}
});
}
testAsyncMulti('mongo-livedata - rewrite selector', [
function (test, expect) {
var collectionName = Meteor.uuid();
if (Meteor.isClient) {
Meteor.call('createInsecureCollection', collectionName);
Meteor.subscribe('c-' + collectionName);
}
var coll = new Meteor.Collection(collectionName);
var docId;
var updateCallback = expect(function (err2) {
test.isFalse(err2);
var doc = coll.findOne(docId);
test.isTrue(doc);
test.equal(doc.name, "foobar");
test.equal(doc.value, 43);
});
coll.insert({name: 'foobar', value: 42}, expect(function (err1, id) {
test.isFalse(err1);
test.isTrue(id);
docId = id;
var doc = coll.findOne(docId);
test.isTrue(doc);
test.equal(doc.name, "foobar");
test.equal(doc.value, 42);
coll.update({name: /o+b/}, {$inc: {value: 1}}, updateCallback);
}));
}
]);