Smart re-use your publications.
Use the Meteor package system
$ meteor add astrocoders:publish
Take a look at your current publications:
Meteor.publish('items', function(){
if(this.userId){
return Items.find();
} else {
this.ready();
}
});
Meteor.publish('photos', function(){
if(this.userId){
return Photos.find();
} else {
this.ready();
}
});
And for every publication you wanna to send only if the user is logged in you gotta to repeat that. Not nice, right? Now take a look how they will become with AstroPublish:
Items.publish('items').ifSignedIn().apply();
Photos.publish('photos').ifSignedIn().apply();
Liked? And how about this one:
// `this` is the context of Meteor.publish
AstroPublish.defineMethod({
type: 'query',
name: 'isOwner',
fn(){
return {
owner: this.userId
}
}
});
// ...
Items.publish('items').ifSignedIn().isOwner().apply();
Photos.publish('photos').ifSignedIn().isOwner().one().latest().apply();
Of course you can do more than one publication!:
in client:
this.subscribe('itemsEdit', FlowRouter.getParam('_id'));
in server:
Items.publish('itemsEdit').isOwner().query((itemId) => {
return {
itemId
}
}).apply();
Call .publish(pubName)
and the chain the methods you want and then invoke
apply()
at the end to create the publication.
- .ifSignedIn, only query if the user are logged in.
- .one, sets limit to 1
- .lastest, it's the same as:
{
sort: {
createdAt: -1
}
}
- .mongoRule, usage:
Items.publish('items').mongoRule(() => {
return {
fields: {
_id: 1
}
}
}).apply();
// And the subscription will only get those fields.
- .fields, usage:
Items.publish('items').fields('name', 'price').apply();
// And the subscription will only get those fields.
- .limit, usage:
Items.publish('items').lastest().limit(10).apply();
- .byGotId. It's really common you create a publication that only gets an id
param and the query for the correspondent document in the database (
{_id: id}
):
// This method expects a subscription like this:
this.subscribe('fooEdit', fooId);
// ...
Items.publish('fooEdit').byGotId().apply();
- .with. Publish a cursor of another collection in the same publication:
Items.publish().with(ItemsPrices.publish()).apply();
// You can have as with'es you want and use all the chain methods available
// in the with'ed publication:
Items.publish().with(ItemsPrices.publish()).with(Things.publish().ifSignedIn())
.apply();
- ifHasRole. Only publish if the user is within one of the passed roles:
Items.publish().ifHasRole('admin').apply();
// as many as you need:
Items.publish().ifHasRole('admin', 'super-cool').apply();
You can create custom methods calling AstroPublish.definedMethod
.
There are just three types of methods available:
query
The returns of these methods will be merged into a single object and will be used during the publication return. These methods must always return an object.
AstroPublish.defineMethod({
type: 'query',
name: 'hasPriceBetween',
// with `context: 'chain'` option.fn callback will receive
// the arguments from the chain, like it is in built-in method `field`
context: 'chain',
fn: function(min, max){
// Return a query here
return {
price: {
$lt: min,
$gt: max
}
}
}
});
// ...
Items.publish('items').hasPriceBetween(50, 100).apply();
But if instead of getting the arguments from the chain context, you wanna to get them from the arguments send to the publish by the subscribe you do: client
this.subscribe('itemsByPrice', 50, 100);
server
AstroPublish.defineMethod({
type: 'query',
name: 'hasPriceBetween',
// Without the 'context: chain' the arguments here are the arguments
// received by the `Meteor.publish` from subscription.
fn: function(min, max){
// Return a query here
return {
price: {
$lt: min,
$gt: max
}
}
}
});
// ...
Items.publish('items').hasPriceBetween(50, 100).apply();
predicate
Must always returntrue
orfalse
. They are invoked before any query in the collection. Think about them like theif(this.userId) Collection.find()
of our first example.
AstroPublish.defineMethod({
type: 'predicate',
// you can use 'context: chain' here too
name: 'ifUserIsCool',
fn: function(){
let user = Meteor.users.findOne(this.userId);
return !!user.isCool;
}
});
// ...
Items.publish('items').ifUserIsCool().apply();
mongoRule
The return of these methods will be merge into a single object and will be used as the second argument of theCollection.find(query, mongoRules)
.
AstroPublish.defineMethod({
type: 'mongoRule',
context: 'chain',
name: 'skip',
fn: function(number){
return {
skip: number
};
}
});
// ...
Items.publish('items').skip(5).apply();
- You used
.query(() => )
instead ofquery(function(){})
sothis
is not going to work properly and you even noticed it ;); - You forget to call
.apply()
and the end of the method chaining; - Add a call to
.debug(true)
at the method chaining do debug with node-inspector.
Run tests using Meteor Command
$ meteor test-packages ./
MIT. Enjoy!