Skip to content

Commit

Permalink
make this.userId available to before/after find/findOne within server…
Browse files Browse the repository at this point in the history
… publish
  • Loading branch information
matb33 committed Jul 25, 2013
1 parent 04c5737 commit 8ccd52f
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 69 deletions.
135 changes: 76 additions & 59 deletions collection-hooks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
(function () {
(function (g) {

var directFind, directFindOne, directInsert, directUpdate, directRemove;
var _validatedInsert, _validatedUpdate, _validatedRemove;
var directFind, directFindOne, directInsert, directUpdate, directRemove, directPublish;

function delegate() {
var i, len, c;
Expand Down Expand Up @@ -62,29 +61,29 @@
// "after" hooks for find are strange and only here for completeness.
// Most of their functionality can be done by "transform".

Meteor.Collection.prototype.find = function (selector, options, userId) {
Meteor.Collection.prototype.find = function (selector, options) {
var result;

userId = userId || getUserId.call(this);
selector = selector || {};
if (Meteor.__collection_hooks_publish_userId) this.userId = Meteor.__collection_hooks_publish_userId;

if (delegate.call(this, "before", "find", userId, selector, options) !== false) {
if (delegate.call(this, "before", "find", selector, options) !== false) {
result = directFind.call(this, selector, options);
delegate.call(this, "after", "find", userId, selector, options, result);
delegate.call(this, "after", "find", selector, options, result);
}

return result;
};

Meteor.Collection.prototype.findOne = function (selector, options, userId) {
Meteor.Collection.prototype.findOne = function (selector, options) {
var result;

userId = userId || getUserId.call(this);
selector = selector || {};
if (Meteor.__collection_hooks_publish_userId) this.userId = Meteor.__collection_hooks_publish_userId;

if (delegate.call(this, "before", "findOne", userId, selector, options) !== false) {
if (delegate.call(this, "before", "findOne", selector, options) !== false) {
result = directFindOne.call(this, selector, options);
delegate.call(this, "after", "findOne", userId, selector, options, result);
delegate.call(this, "after", "findOne", selector, options, result);
}

return result;
Expand Down Expand Up @@ -152,65 +151,83 @@
};

if (Meteor.isServer) {
_validatedInsert = Meteor.Collection.prototype._validatedInsert;
_validatedUpdate = Meteor.Collection.prototype._validatedUpdate;
_validatedRemove = Meteor.Collection.prototype._validatedRemove;

// These are triggered on the server, but only when a client initiates
// the method call. They act similarly to observes, but simply hi-jack
// _validatedXXX. Additionally, they hi-jack the collection
// instance's _collection.insert/update/remove temporarily in order to
// maintain validator integrity (allow/deny).

Meteor.Collection.prototype._validatedInsert = function (userId, doc) {
var id, self = this;
var _insert = self._collection.insert;

self._collection.insert = function (doc) {
if (delegate.call(self, "before", "insert", userId, doc) !== false) {
id = _insert.call(this, doc);
delegate.call(self, "after", "insert", userId, id && this.findOne({_id: id}) || doc);
}
(function () {

var _validatedInsert = Meteor.Collection.prototype._validatedInsert;
var _validatedUpdate = Meteor.Collection.prototype._validatedUpdate;
var _validatedRemove = Meteor.Collection.prototype._validatedRemove;

var directPublish = Meteor.publish;

// These are triggered on the server, but only when a client initiates
// the method call. They act similarly to observes, but simply hi-jack
// _validatedXXX. Additionally, they hi-jack the collection
// instance's _collection.insert/update/remove temporarily in order to
// maintain validator integrity (allow/deny).

Meteor.Collection.prototype._validatedInsert = function (userId, doc) {
var id, self = this;
var _insert = self._collection.insert;

self._collection.insert = function (doc) {
if (delegate.call(self, "before", "insert", userId, doc) !== false) {
id = _insert.call(this, doc);
delegate.call(self, "after", "insert", userId, id && this.findOne({_id: id}) || doc);
}
};

_validatedInsert.call(self, userId, doc);
self._collection.insert = _insert;
};

_validatedInsert.call(self, userId, doc);
self._collection.insert = _insert;
};
Meteor.Collection.prototype._validatedUpdate = function (userId, selector, mutator, options) {
var previous, self = this;
var _update = self._collection.update;

Meteor.Collection.prototype._validatedUpdate = function (userId, selector, mutator, options) {
var previous, self = this;
var _update = self._collection.update;
self._collection.update = function (selector, mutator, options) {
if (delegate.call(self, "before", "update", userId, selector, mutator, options) !== false) {
previous = this.find(selector).fetch();
_update.call(this, selector, mutator, options);
delegate.call(self, "after", "update", userId, selector, mutator, options, previous);
}
};

self._collection.update = function (selector, mutator, options) {
if (delegate.call(self, "before", "update", userId, selector, mutator, options) !== false) {
previous = this.find(selector).fetch();
_update.call(this, selector, mutator, options);
delegate.call(self, "after", "update", userId, selector, mutator, options, previous);
}
_validatedUpdate.call(self, userId, selector, mutator, options);
self._collection.update = _update;
};

_validatedUpdate.call(self, userId, selector, mutator, options);
self._collection.update = _update;
};
Meteor.Collection.prototype._validatedRemove = function (userId, selector) {
var previous, self = this;
var _remove = self._collection.remove;

Meteor.Collection.prototype._validatedRemove = function (userId, selector) {
var previous, self = this;
var _remove = self._collection.remove;
self._collection.remove = function (selector) {
if (delegate.call(self, "before", "remove", userId, selector, previous) !== false) {
previous = this.find(selector).fetch();
_remove.call(this, selector);
delegate.call(self, "after", "remove", userId, selector, previous);
}
};

self._collection.remove = function (selector) {
if (delegate.call(self, "before", "remove", userId, selector, previous) !== false) {
previous = this.find(selector).fetch();
_remove.call(this, selector);
delegate.call(self, "after", "remove", userId, selector, previous);
}
_validatedRemove.call(self, userId, selector);
self._collection.remove = _remove;
};

_validatedRemove.call(self, userId, selector);
self._collection.remove = _remove;
};
Meteor.publish = function (name, func) {
return directPublish.call(this, name, function () {
var result;

Meteor.__collection_hooks_publish_userId = this.userId;
result = func.apply(this, arguments);
delete Meteor.__collection_hooks_publish_userId;

return result;
});
};

})();
}

Meteor.Collection.prototype.clearHooks = function (verb, type) {
Meteor.Collection.prototype.clearHooks = function (type, verb) {
if (!this.__collection_hooks) this.__collection_hooks = {};
if (!this.__collection_hooks[type]) this.__collection_hooks[type] = {};
this.__collection_hooks[type][verb] = [];
Expand Down Expand Up @@ -238,4 +255,4 @@
};
}

})();
})(this);
9 changes: 5 additions & 4 deletions package.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ Package.on_test(function (api) {
// hook) are not yet written. If you can figure out how, please tell
// me or help me write these tests. I can't figure out how to run a
// Tinytest that relies on both server and client talking to each other.
api.add_files(["tests.js"], both);

api.add_files(["client_server_userId_tests.coffee"], both);
});
api.add_files("tests.js", both);
api.add_files("tests_publish.js", both);
api.add_files("tests_userid_in_find_hooks_within_publish.js", both);
//api.add_files(["client_server_userId_tests.coffee"], both);
});
11 changes: 5 additions & 6 deletions tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ Tinytest.addAsync("before find", function (test, next) {

test.equal(Collection.find({a: 1}).count(), 0);

Collection.before("find", function (userId, selector) {
console.log("userId", userId);
Collection.before("find", function (selector, options) {
selector.b = 1;
});

Expand All @@ -23,7 +22,7 @@ Tinytest.addAsync("before find with edit to empty selector", function (test, nex

test.equal(Collection.find({a: 1}).count(), 0);

Collection.before("find", function (userId, selector) {
Collection.before("find", function (selector, options) {
selector.b = 1;
});

Expand All @@ -42,7 +41,7 @@ Tinytest.addAsync("after find", function (test, next) {

test.equal(Collection.find({a: 1}).count(), 0);

Collection.after("find", function (userId, selector, options, result) {
Collection.after("find", function (selector, options, result) {
result.forEach(function (record) {
Collection.update(record._id, {
$set: { b: 1 }
Expand All @@ -63,7 +62,7 @@ Tinytest.addAsync("before findOne", function (test, next) {

test.equal(Collection.find({a: 1}).count(), 0);

Collection.before("findOne", function (userId, selector) {
Collection.before("findOne", function (selector, options) {
selector.b = 1;
});

Expand All @@ -82,7 +81,7 @@ Tinytest.addAsync("after findOne", function (test, next) {

test.equal(Collection.find({a: 1}).count(), 0);

Collection.after("findOne", function (userId, selector, options, result) {
Collection.after("findOne", function (selector, options, result) {
result.b = 1;
});

Expand Down
50 changes: 50 additions & 0 deletions tests_publish.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
var Collection = new Meteor.Collection("tests_publish");

if (Meteor.isServer) {
(function () {

Meteor.methods({
tests_publish_reset: function () {
Collection.remove({});
Collection.insert({pass: false});
Collection.insert({pass: true});
}
});

Meteor.call("tests_publish_reset", function () {
Meteor.publish("tests_publish", function () {
return Collection.find({pass: (this.userId === Meteor.__collection_hooks_publish_userId)});
});
});

})();
}

if (Meteor.isClient) {
(function () {

Tinytest.addAsync("global publish_userId available within publish", function (test, next) {
function run() {
Meteor.call("tests_publish_reset", function (err, result) {
test.equal(!!err, false);

var handle = Meteor.subscribe("tests_publish", function () {
var doc = Collection.findOne({pass: true});
if (doc) {
next();
}
});
});
}

if (Meteor.userId()) {
Meteor._debug("already logged in -- running tests");
run();
} else {
Meteor._debug("logging in to run tests");
Meteor.insecureUserLogin("tests_publish", run);
}
});

})();
}
Loading

0 comments on commit 8ccd52f

Please sign in to comment.