mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
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:
@@ -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) {
|
||||
|
||||
@@ -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 || {};
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}));
|
||||
}
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user