Make autopublish into an empty package, detected via weak deps.

This commit is contained in:
David Glasser
2013-07-24 13:46:53 -07:00
parent cbcafd0c20
commit fa27498139
15 changed files with 63 additions and 81 deletions

View File

@@ -312,13 +312,14 @@ Meteor.publish(null, function() {
// Accounts.addAutopublishFields Notably, this isn't implemented with
// multiple publishes since DDP only merges only across top-level
// fields, not subfields (such as 'services.facebook.accessToken')
autopublishFields = {
var autopublishFields = {
loggedInUser: ['profile', 'username', 'emails'],
otherUsers: ['profile', 'username']
};
// Add to the list of fields or subfields to be automatically
// published if autopublish is on
// published if autopublish is on. Must be called from top-level
// code (ie, before Meteor.startup hooks run).
//
// @param opts {Object} with:
// - forLoggedInUser {Array} Array of fields published to the logged-in user
@@ -330,42 +331,45 @@ Accounts.addAutopublishFields = function(opts) {
autopublishFields.otherUsers, opts.forOtherUsers);
};
Meteor.server.onAutopublish(function () {
// ['profile', 'username'] -> {profile: 1, username: 1}
var toFieldSelector = function(fields) {
return _.object(_.map(fields, function(field) {
return [field, 1];
}));
};
if (Package.autopublish) {
// Use Meteor.startup to give other packages a chance to call
// addAutopublishFields.
Meteor.startup(function () {
// ['profile', 'username'] -> {profile: 1, username: 1}
var toFieldSelector = function(fields) {
return _.object(_.map(fields, function(field) {
return [field, 1];
}));
};
Meteor.server.publish(null, function () {
if (this.userId) {
return Meteor.users.find(
{_id: this.userId},
{fields: toFieldSelector(autopublishFields.loggedInUser)});
} else {
return null;
}
}, /*suppress autopublish warning*/{is_auto: true});
// XXX this publish is neither dedup-able nor is it optimized by our special
// treatment of queries on a specific _id. Therefore this will have O(n^2)
// run-time performance every time a user document is changed (eg someone
// logging in). If this is a problem, we can instead write a manual publish
// function which filters out fields based on 'this.userId'.
Meteor.server.publish(null, function () {
var selector;
if (this.userId)
selector = {_id: {$ne: this.userId}};
else
selector = {};
Meteor.server.publish(null, function () {
if (this.userId) {
return Meteor.users.find(
{_id: this.userId},
{fields: toFieldSelector(autopublishFields.loggedInUser)});
} else {
return null;
}
}, /*suppress autopublish warning*/{is_auto: true});
// XXX this publish is neither dedup-able nor is it optimized by our
// special treatment of queries on a specific _id. Therefore this
// will have O(n^2) run-time performance every time a user document
// is changed (eg someone logging in). If this is a problem, we can
// instead write a manual publish function which filters out fields
// based on 'this.userId'.
Meteor.server.publish(null, function () {
var selector;
if (this.userId)
selector = {_id: {$ne: this.userId}};
else
selector = {};
return Meteor.users.find(
selector,
{fields: toFieldSelector(autopublishFields.otherUsers)});
}, /*suppress autopublish warning*/{is_auto: true});
});
selector,
{fields: toFieldSelector(autopublishFields.otherUsers)});
}, /*suppress autopublish warning*/{is_auto: true});
});
}
// Publish all login service configuration fields other than secret.
Meteor.publish("meteor.loginServiceConfiguration", function () {

View File

@@ -21,6 +21,10 @@ Package.on_use(function (api) {
// {{currentUser}}. If not, no biggie.
api.use('handlebars', 'client', {weak: true});
// Allow us to detect 'autopublish', and publish some Meteor.users fields if
// it's loaded.
api.use('autopublish', 'server', {weak: true});
api.exportSymbol('Accounts');
api.add_files('accounts_common.js', ['client', 'server']);

View File

@@ -12,7 +12,6 @@ if (Meteor.isClient) {
Facebook.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately
// be used from the client (if transmitted over ssl or on

View File

@@ -12,7 +12,6 @@ if (Meteor.isClient) {
Github.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
Accounts.addAutopublishFields({
// not sure whether the github api can be used from the browser,
// thus not sure if we should be sending access tokens; but we do it

View File

@@ -12,7 +12,6 @@ if (Meteor.isClient) {
Google.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
Accounts.addAutopublishFields({
forLoggedInUser: _.map(
// publish access token since it can be used from the client (if

View File

@@ -12,7 +12,6 @@ if (Meteor.isClient) {
Meetup.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately
// be used from the client (if transmitted over ssl or on
@@ -20,6 +19,4 @@ if (Meteor.isClient) {
forLoggedInUser: ['services.meetup'],
forOtherUsers: ['services.meetup.id']
});
}

View File

@@ -12,7 +12,6 @@ if (Meteor.isClient) {
Twitter.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
var autopublishedFields = _.map(
// don't send access token. https://dev.twitter.com/discussions/5025
Twitter.whitelistedFields.concat(['id', 'screenName']),

View File

@@ -12,12 +12,10 @@ if (Meteor.isClient) {
Weibo.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately
// be used from the client (if transmitted over ssl or on localhost)
forLoggedInUser: ['services.weibo'],
forOtherUsers: ['services.weibo.screenName']
});
}

View File

@@ -1 +0,0 @@
Meteor.server.autopublish();

View File

@@ -2,7 +2,5 @@ Package.describe({
summary: "Automatically publish the entire database to all clients"
});
Package.on_use(function (api) {
api.use('livedata', 'server');
api.add_files("autopublish.js", "server");
});
// This package is empty; its presence is detected by livedata and
// accounts-base.

View File

@@ -906,8 +906,8 @@ _.extend(Connection.prototype, {
// Mark all named subscriptions which are ready (ie, we already called the
// ready callback) as needing to be revived.
// XXX We should also block reconnect quiescence until autopublish is done
// re-publishing to avoid flicker!
// XXX We should also block reconnect quiescence until unnamed subscriptions
// (eg, autopublish) are done re-publishing to avoid flicker!
self._subsBeingRevived = {};
_.each(self._subscriptions, function (sub, id) {
if (sub.ready)

View File

@@ -1024,9 +1024,6 @@ Server = function () {
self.method_handlers = {};
self.on_autopublish = []; // array of func if AP disabled, null if enabled
self.warned_about_autopublish = false;
self.sessions = {}; // map from id to session
self.stream_server = new StreamServer;
@@ -1176,7 +1173,7 @@ _.extend(Server.prototype, {
return;
}
if (!self.on_autopublish && !options.is_auto) {
if (Package.autopublish && !options.is_auto) {
// They have autopublish on, yet they're trying to manually
// picking stuff to publish. They probably should turn off
// autopublish. (This check isn't perfect -- if you create a
@@ -1307,25 +1304,6 @@ _.extend(Server.prototype, {
if (exception)
throw exception;
return result;
},
// A much more elegant way to do this would be: let any autopublish
// provider (eg, mongo-livedata) declare a weak package dependency
// on the autopublish package, then have that package simply set a
// flag that eg the Collection constructor checks, and autopublishes
// if necessary.
autopublish: function () {
var self = this;
_.each(self.on_autopublish || [], function (f) { f(); });
self.on_autopublish = null;
},
onAutopublish: function (f) {
var self = this;
if (self.on_autopublish)
self.on_autopublish.push(f);
else
f();
}
});

View File

@@ -19,6 +19,10 @@ Package.on_use(function (api) {
// Detect whether or not the user wants us to audit argument checks.
api.use(['audit-argument-checks'], 'server', {weak: true});
// Allow us to detect 'autopublish', so we can print a warning if the user
// runs Meteor.publish while it's loaded.
api.use('autopublish', 'server', {weak: true});
api.exportSymbol('DDP');
api.exportSymbol('DDPServer', 'server');

View File

@@ -174,12 +174,12 @@ Meteor.Collection = function (name, options) {
self._defineMutationMethods();
// autopublish
if (!options._preventAutopublish &&
self._connection && self._connection.onAutopublish)
self._connection.onAutopublish(function () {
var handler = function () { return self.find(); };
self._connection.publish(null, handler, {is_auto: true});
});
if (Package.autopublish && !options._preventAutopublish && self._connection
&& self._connection.publish) {
self._connection.publish(null, function () {
return self.find();
}, {is_auto: true});
}
};
///

View File

@@ -20,8 +20,12 @@ Package.on_use(function (api) {
['client', 'server']);
api.use('check', ['client', 'server']);
// Allow us to detect 'insecure'.
api.use('insecure', {weak: true});
// Allow us to detect 'autopublish', and publish collections if it's loaded.
api.use('autopublish', 'server', {weak: true});
api.exportSymbol('_MongoLivedataTest', 'server');
api.add_files('mongo_driver.js', 'server');