Getting started
mg uses regular Backbone models, but adds a few additional properties, a model registry and a model cache. To set up mg hydration, define additional properties on the model and set the sync function. For example:
var Post = Backbone.Model.extend({
sync: mg.sync('Post')
});
mg.define('Post', Post);
The sync function is added so that mg can intercept model.sync(), allowing it to detect when new models are created.
The mg.define() call registers the model with hydration, so that relations can find the right model class and metadata.
Defining relations
To define relations, define the type of the related model on .rels["keyname"]. For example, to have Post.author be hydrated as an instance of Person:
var Post = Backbone.Model.extend({
rels: {
author: { type: 'Person' }
}
});
mg.define('Post', Post);
mg supports both one-to-one, one-to-many and many-to-one relations. You do not need to explicitly define the type of relation, as it will be inferred from the JSON data.
For example, given the following JSON data, a one-to-one relation is detected:
mg.hydrate('Post', {
id: 1,
author: 1000
}, function(err, model) { ... });
This would be hydrated with .get('author') set to the Person with id=1000. If that model is not available from the local cache, it is fetched before returning the hydrated model.
Given the following JSON data, a one-to-many relation is detected:
mg.hydrate('Post', {
id: 1,
author: [ 200, 300 ]
}, function(err, model) { ... });
Alternatively, the content of the array can be objects with an id attribute (controlled via Backbone's .idAttribute):
mg.hydrate('Post', {
id: 1,
author: [ { id: 200 }, { id: 300 } ]
}, function(err, model) { ... });
Both of these would be hydrated with .get('author') set to a Backbone.Collection instance containing the Person models with id=200 and id=300.
Fetching models: the find API
The find API makes backend calls to find model ids, and returns fully hydrated models.
The URL is determined using model.url, following the Backbone conventions. Here are some examples, see the BB docs as well. Using urlRoot:
var Post = mg.define('Post', Backbone.Model.extend({
urlRoot: '/posts/'
}));
Or using a function:
var Post = mg.define('Post', Backbone.Model.extend({
url: function() {
return '/posts/' + encodeURIComponent(this.id) + '?exclude=foo';
}
}));
Models are cached in-memory. This means that you should always use .findById(), since it will retrive the model from cache if possible.
findById(name, id, onDone): retrieves a model by id. Reads the model from model.url + / + id.
findOne(name, conditions, onDone): retrieves a single model by any condition. Reads the model from model.url + ? + condition=value.
find(name, conditions, onDone) / : retrieves a model by a set of conditions.
Alternatively, you can use model.fetch() ??
Hydration API
The hydration API allows you to take data loaded via some other channel, and hydrate it.
hydrate(name, data, onDone): hydrate an existing JSON blob (with related model hydration).
Hydration fetches all related models and finally calls onDone with err, result.
Defining data types for hydration
To define data types for models and validation:
field: {
type: Number|Boolean|RegExp|Date|'Person',
default: (value)
}
When fields have a defined data type, the hydration layer ensure that the values are of the correct type (e.g. ensuring that numbers are numbers, booleans are booleans and RegExps are regexps).
Setting the collection class
By default, collections of Post models are contained inside a Backbone.Collection. However, your can define a different container class if you prefer (e.g. with Post-specific collection methods).
var Post = Backbone.Model.extend({
collection: 'Posts',
});
mg.define('Post', Post);
var Posts = Backbone.Collection.extend({
// additional property
special: true
});
mg.define('Posts', Posts);
Validation API
field: {
validation: RegExp|function|array of RegExp/function,
}
Validation is optional and separate from saving. Built in validators (inspired by mongoose):
required: trueis builtin.- Numbers have min and max validators
- Strings have enum and match validators
For example:
rels: {
type: Date
}
To validate:
var err = model.validate();
Creating / reading / updating / deleting
How mg hooks into CRUD:
- Create:
mguses the.syncfunction to get notified of newly created instances once the backend returns a response that contains an id. These models are added to the cache, and any streaming collections are notified. - Read:
mgprovides the.findand.hydrateAPIs for reading in and hydrating related models - Update: the normal
Backbone.syncbehavior takes place. Sincemgensures that only one instance of a class + id pair exists on the client side, any updates trigger events on the same instance of the model. For properties that are models or collections of models,mghooks into the.saveand triggers a save on the related collection or model (one level deep). - Delete:
mgshould also remove the model from all collections and remove it from the cache.
Save API
Saving collection add/remove actions; should be done when the model containing the relation collection is saved.
For properties that are models or collections of models, mg hooks into the .save and triggers a save on the related collection or model (one level deep).
Cache API
.store(name, value): inserts a JSON structure into the cache.
For example, to store a JSON structure in order to preload a model:
cache.store('test', { id: 7000, name: 'bar' });
.fetch(name, uri, onDone): fetches a JSON structure from a remote URL.
.keys(name): returns all the cache keys (e.g. model ids).
.get(name, id, onDone): fetches a JSON structure by id (either remote or local).
.local(name, id): returns true if a model is locally cached.
If a request is already pending to a particular URL, then the cache will not start a new request. Instead, the
Remapping
module.exports = {
// inline define
Post: mg.define('Post', Backbone.Model.extend({
sync: mg.sync('Post'),
plural: 'posts'
})),
// easier getter
posts: function(conditions, onDone) {
return mg.find('Post', conditions, onDone);
}
};
Streaming collections
Streaming collections make it easy to track all instances of a specific type.
stream(name, conditions, onLoaded)