Moko
Generator-powered, highly extendible models made for use with co.
Installation
npm install moko
Example Usage
var moko = validators = mongo = ; var User = ; User User ; ;
Table of Contents
Philosophy
moko
is the spiritual successor to
modella, updated for use with ECMA
6 generators.
moko
provides bare-bones models and an API to extend them. Plugins are mixed
into models adding functionality as needed. Plugins are easy to write (see the Moko Plugin Creation
Guide) and
readily shared within the moko community (see the Moko Plugin List)
The organization is open for those who would like to join. Simply reach out to Ryan and say hi!
Events
Moko provides two types of events, async
(powered by generators) and sync
(powered by functions). async
events happen before an operation and allow you to mutate the data,
while sync
events allow you to react to changes. In general, async
events
end with ing
(eg. saving
, creating
, initializing
).
Plugin authors are also encouraged to emit their own events to make it easy for users to hook into the plugins.
Built in async events:
Moko will emit the following async events. Notice that you must use generators for async events, although your generators do not necessarily need to yield themselves.
initializing(attrs)
- called when a model is first initialzedsaving(dirty)
- called before savecreating(dirty)
- called before save when the model did not exist priorupdating(dirty)
- called before save when the model did exist prior
Examples:
User; var user = name: 'Stephen';console // Logs "Bob";
User; var user = name: 'Stephen'; user;
User;
Built in sync events:
Function (not generator) events are emitted after something happens on the model.
Built in events include:
initialize(instance)
- called after an instance is done initializingchange(attr, newVal, oldVal)
- called when an attr changeschange attr(newVal, oldVal)
- called whenattr
changessave
- called after savecreate
- called after save when model did not exist priorupdate
- called after save when model did exist prior
User ; User; ;
Fire and forget email sending on user-creation.
User;
API Documentation
Creating and Configuring Models
Model Creation
To create a Model, simply call moko
with the name of the model. If preferred
you can also call new Moko(name)
.
var moko = ; var User = ; User instanceof moko // trueconsole // => 'User' // or var Person = 'Person';
Model Configuration
All model configuration methods are chainable.
Model.attr(name, opts)
Defines attribute name
on instances, adding change
events. (see
events below)
opts
is an object that can be used by plugins.
var User = ; User ; User; var user = name: 'Steve'; username = 'Bob';
Model.validate(fn*)
Adds a validator fn*(instance)
which can add errors to an instance.
var User = ;; var { ifusername != 'Steve' user;}; User;
Model.use(plugin)
Configures a model to use a plugin. See the list of plugins or the plugin creation guide to get started writing your own.
var mongo = validators = timestamps = ; var db = ; User ;
Event Methods
A moko Model mixes in co-emitter, see the full documentation for details. It exposes the following methods:
on(event, fn*)
- add a listener for an eventonce(event, fn*)
- add a one-time listener for an eventemit(event, ...args)
- emit an event with...args
off(event, listener)
- remove a listener for an eventremoveAllListeners()
- removes all listenershasListeners(event)
- check whether an event has listenerslisteners(event)
- get an array of listeners for an event
Working with Instances
Instances are created by yielding
to a new Model
. This allows async events
to happen on initializing
(such as pre-populating relations from the
database).
var user = name: 'Bob';
instance.set(attrs)
Takes an object of attrs
and sets the models properties accordingly. If an
attribute is passed in that isn't defined on the model, it will be skipped.
var User = ; User; var bob = ;bob; bobname == 'Bob' // truebobage === undefined // true, age wasn't a defined attr
instance.toJSON()
Returns a cloned object of the instances attrs
.
thisbody = user; // inside koa
Persistence / Sync Layer
moko
provides a variety of methods to persist models to a sync layer. Out of
the box it does not use have any sync layer baked in, so without using one (as a
plugin) these methods can throw errors.
instance.primary(val)
If val
is undefined, returns the primary key of the model (by default
instance._id
or instance.id
, whichever exists.
If val
is specified, sets the primary key attribute (instance._id or instance.id
).
You can also specify a primary key manually at time of attribute definition:
var User = ; var user = ; user;console // 'Bob' user // 'Bob'
instance.isNew()
Returns whether instance.primary()
is defined.
var userA = _id: 123 ;userA; // false var userB = ;userB; // true
instance.save(skipValidations)
Will save if a sync-layer plugin has been registered. Will only save if the model is valid, otherwise will throw an error.
To save regardless of being valid or not, pass in skipValidations
as true.
try user; catche // deal with error;
instance.remove()
Will remove the model if the sync layer provides it.
Will set instance.removed
to true.
user;
Errors and Validation
instance.error(attr, reason)
Registers an error (reason
) on attr
.
if!username user;
instance.errors([attr])
Returns an error object, in the format of {attr: [errors]}
.
If attr
is specified, returns an array of errors for that attr
.
If no errors are registered for attr
it returns an empty array.
user;user;user; user // { name: ['is stupid', 'is short'], age: ['is too young'] }user // ['is stupid', 'is short']user // []
instance.isValid(attr)
Runs all validators, and checks whether the instance has any errors or not.
If attr
is provided, reports whether that specific attr
is valid.
To support async validations, you must yield
to isValid
.
var valid = user;
Utilities
Moko exports common utilities to make it so that plugins don't need to end up requiring the same modules.
Built in utilities include:
moko.utils.clone
- does a deep clone of an objectmoko.utils.isGenerator
- returns true if a function is a generatormoko.utils.type
- returns a string representation of type (eg.array
)