Meteor Experiment: Smart Publications & Smart Methods

I just published a package that lets you easily generate publications based on special properties defined in your collection schema:

It supports public/private fields, and joins (non-reactive for now, sorry!). Here’s a 5-min video that explains more:

EDIT And now, Smart Methods too!

You can find the example app using both here:

Ultimately, the goal is to generate a whole suite of scaffolding tools, all based on your collection schema.

Let me know what you think : )

4 Likes

This is pretty slick. I like the Collection.publish method, but wonder if it would be more intuitive if the Schema property would be publish instead of public?

Also, the join field in the schema, how does it determine the join? Is it always going to be by _id?

1 Like

I’m very open to naming suggestions, publish does make more sense. And yes, it’s always based on _ids (at least for now).

It looks like a good idea. So far I’ve only watched the smart publications video.

I wonder how useful this package would actually be. Common usecases are publishing different fields to different users, and different fields at different times.

For example, admin can see more fields than other users.
Another example: if you had a Posts collection, page listing 10 posts might only want the posts title and not the content, whereas the an indvidual post page would want the post content too.

Also, Tasks.publish("myPublicationName"); publishes all documents for a collection. This doesn’t seem to useful either. Rarely do you need that functionality either.

I think these problems could be solved by adding additional fields to the schemas. I wonder if by the time we’ve added all the necessary schema keys if the final solution would be any easier than the current way to standard documents. I think it would be interesting to see how it played out though.

1 Like

public (or publish if that’s what it’s renamed to) could become a function and do something like:

public: function () { return userIsAdmin(); }

But the other issues mentioned above still exist.

Yes, that’s a good point, I need to think about how to add these features. But generally speaking, I imagine you would use a system like that in the beginning, and then once you need more granularity just replace it with your own publications.

I’ve tried similar things. To understand my solution, you have to know, that my app consists of multiple modules. Every module can have multiple collections and methods. Modules represent a “thing”. If wanted, an entry of any collection can become a user. So when I have a “customer” collection, I could enable these customers to become users and log in.

The system I’ve built look like this:

var isOwnPatient, permission;

__Client_And_Server__();

/*
 * We apply permissions to collections. In this case,
 * dentists can log in and interact with patients.
*/

/*
 * All methods are checked automatically.
 * When using a function to check if a user, in this
 * case the dentist is allowed to run it, we get the
 * own document as first parameter, followed by all the
 * arguments the method has been called with.
 * Has to return true or false.
 */
isOwnPatient = function(dentist, data) {
  return data.dentistId === dentist._id;
};

/*
 * The permission can be a function, might be required
 * because of Meteor's loading order. Alternatively we
 * could just make it an object.
 */
permission = function() {
  return {

    // This is the module the dentist can interact with.
    module: Confinet.CRM.Patients,

    // Here we define, what data the dentist can get
    // from the collections of the Patients module.
    // We only have one collection in most cases.
    collections: {

      // Obviously, a dentist can query his patients.
      patients: {

        // A dentist can subscribe using *any* query, but
        // it will be added to `$and`, along with the following
        // "safety-query" we return. Can be a function, or just
        // a mongo query (object).
        query: function (dentist) {
          return { dentistId: dentist._id };
        },

        // A dentist will get all the fields. We could also
        // set it to an array of fields or a function,
        // and return that array based on certain conditions.
        fields: '*'
      }
    },
    methods: {
      create: isOwnPatient,
      update: isOwnPatient,
      delete: false
    }
  };
};

// share is coming from using CoffeeScript
share.Dentists.getCollection().permit(permission);

// ---
// generated by coffee-script 1.9.2

The patient subscription a dentist can query looks like this:

var Patients;

__Server__();

Patients = share.Patients.getCollection();

Meteor.publish('patients', function (query) {

  // Instead of using `find` on a collection,
  // we set the user and use `safeFind`, which
  // will use our checks
  return Patients
    .setUser(this.userId)
    .safeFind(query);
});

// ---
// generated by coffee-script 1.9.2

Methods are created using the normal method syntax, but using the module instead of Meteor, like Patients.methods. Those methods will automatically be checked.

I’ve posted this somewhere else already, but didn’t get any feedback. The system is designed to work safely for my application specifically, but the idea might work everywhere with little changes.

As you can see, I could easily create a .publish function for collections, and it would be safe to query those. Fields would be checked, and access to documents would be checked as well. The system requires you to allow access, anything else won’t be returned.

Might look a little bit complicated first, but once you understand it, it’s so easy to use :smiley: Let me know what you think :relaxed:

Can you recommend an scaffolding tools for generating meteor methods where given a schema, it generates create, update, remove, as well as push and pop Meteor methods.