Skip to content

Moko Plugin Creation Guide

Ryan Schmukler edited this page Jun 1, 2014 · 6 revisions

Moko is all about being extended with plugins. This guide will introduce how plugins work, as well as suggest best practices for writing plugins.

Hello World

A plugin, at its simplest, is a function that takes a model and does something with it. Here is a simple hello world plugin:

hello-world.js

module.exports = function(Model) {
  Model.helloWorld = function() {
    console.log(Model.name + " says hello!");
  }
};

user.js

var helloWorld = require('moko-hello-world');

var User = moko('User');

User.use(helloWorld);

Sync Layer Plugins

A sync layer plugin helps persist your model somewhere. It enables an instance of a model to call yield instance.save() and (optionally) yield instance.remove().

To be a valid sync layer plugin, you must implement the following methods:

  • Model.save - called when an instance is new (no primary key set). Called with this being the instance.
  • Model.update - called when an instance needs to be updated (primary key is set). Called with this being the instance.

In terms of determining what to save, this._dirty will have a list of attributes that have changed since the instance was last saved.

These methods should return an updated set of attributes, which will be copied over to the instance. For example, here is an in-memory sync layer.

module.exports = function(Model) {
  var collection = [];

  Model.save = function*() { 
    var id = collection.length;
    var data = this._dirty;
    data.id = id;
    collection.push(data)
    return data;
  };

  Model.update = function*() {
    var index = this.primary();
    var storedData = collection[index];
    var data = this._dirty;
    for(var key in data) { storedData[key] = data[key]; }
    return data;
  }
};
Suggested Additional Sync Methods:

Although the above will allow instance.save to work, there are a few more additional methods that can help add more utility:

  • Model.all(query) - Return all of the models, or if a query is specified, a subset that match the query
  • Model.find(id) - Return a single model which has primary key id
  • Model.remove() - Called when instance.remove is called with this being the instance.
  • Model.removeAll(query) - Remove all instances that match the query.

Continuing with our in-memory example (see above):

Model.all = function*(query) {
  var result = moko.utils.clone(collection);
  if(query) { 
    for(var key in query) { 
      result = result.filter(function(instance) { return instance[key] == query[key]; });
    }
  }
  return result;
};

Model.find = function*(id) {
  return moko.utils.clone(collection[id]);
};

Model.remove = function*() {
  collection.splice(this.primary(), 1);
}

Model.removeAll = function*(query) {
  if(!query) return collection = [];
  var toRemove = Model.all(query).map(function(data) { return data.id; } );
  toRemove.forEach(function(id) {
    collection.splice(id, 1);
  });
};

Using and Sharing Utils

Plugins often require a lot of duplicate functionality, such as clone. Moko exposes some of these in its moko.utils object. This helps keep dependencies clean. Built in utils include:

  • moko.utils.clone - Deep clones an object
  • moko.utils.isGenerator - Returns true if a function is a generator
  • moko.utils.type - Returns a String type, such as array, object, etc.

If your plugin requires a utility that isn't available, you should add it to moko.utils[utility] so that other plugins that depend on yours may make use of the utility as well.

Helping others find your plugins

There are a few ways to help users find your plugins:

  • Add your plugin to the Plugin List
  • Tag your package with the right keywords. Suggested moko keywords include moko-plugin and moko-sync
  • Request to join the organization. Moko is a very open project and is looking to add core members. If you're interested in joining the organization and having your repo hosted on github.com/mokojs/*, email Ryan and say hi :)

Clone this wiki locally