node package manager
Don’t reinvent the wheel. Reuse code within your team. Create a free org »

grit

GRIT

Grit is the Model third of the traditional Model-View-Controller webapp trinity. It's asynchronous and evented, and usable both in the browser and in NodeJS; think of it as a leaner Backbone with a more Node-friendly API.

Grit has a single hard dependency, but only in browsers: it needs an implementation of EventEmitter stored on window.EventEmitter. A recommended EventEmitter implementation can be found here. On NodeJS servers, Grit has no dependencies.

Some may find Grit's Node-inspired callback system clunky or undesirable; glow, a Heavy Flow-based flow control plugin for Grit, comes highly recommended.

Sample

In pure JS:

var User = Model.extend(function(username) {
  Model.call(this, {username:username});
});
 
User.required('username');
 
User.validate('username', function(username) {
  return typeof username === 'string';
});
 
var user = new User('reissbaker');
user.on('change:username', function(user, oldUsername, newUsername) {
  alert('changing username from ' + oldUsername + ' to ' + newUsername);
});
user.set('username', 'rekabssier');

Or, in CoffeeScript:

class User extends Model
  @required 'username'
  @validate 'username', (username) ->
    typeof username == string

  constructor: (username) ->
    super username: username

user = new User 'reissbaker'
user.on 'change:username', (user, oldUsername, newUsername) ->
  alert "changing username from #{oldUsername} to #{newUsername}"
user.set 'username', 'rekabssier'

API

The API follows the NodeJS EventEmitter API, and the events emitted are inspired by Backbone.

Models

Constructor

  • new Model(properties, isNew, finishedCallback), where properties is a hash of properties, isNew is an optional boolean that tells the constructor whether this is a new model that should be created in the database or is an existing model being loaded into memory, and finishedCallback is a callback that gets called once the model has been persisted in the database (if the model is new -- otherwise, the callback won't do anything, since the model doesn't get created). Creates a new Model object, and will set the properties according to the properties hash passed in. Please note that if you mark properties as required with Model.required, you must either pass their initial values into the constructor or set initial values for them with Model.defaults.

Instance Methods

  • model.on(evt, handler), or model.addListener(evt, handler) add an event handler to the object. Model objects emit "change:p" events, where p is the name of the property being changed. Event handlers should be of the form function(model, oldValue, newValue), where model is the model being changed, oldValue is the previous value for the property being listened to, and newValue is the new value about to be set for the property being listened to.
  • model.removeListener(e, handler) removes an event handler.
  • model.get(property, callback) gets a named property and passes it to the callback. callback should be of the form function(err, data), like most NodeJS callbacks.
  • model.set(property, value[, silent, callback]) sets a property. Passing a truthy value as the silent parameter suppresses a change event; otherwise, a change:property event will be emitted, where property is the value passed in as the property parameter. The callback will be called once the set has propagated to the server or DB through the Model.update method. Callbacks should be of the form function(err).
  • model.destroy(callback) destroys the model on the server or DB by proxying out to Model.destroy. The callback will be called when the destruction has propagated to the server or DB, and should be of the form function(err).
  • model.loadAll(callback) loads all properties set as being on the class in Model.has by proxying out to class method Model.read. callback (optional) is of form function(), and gets called once all of the properties have been loaded.

Class Methods

Syncing
  • Model.create(model, data, callback) is the method to override for syncing model creation. model is the model itself, and data is the property data in the raw. callback is a function of the form function(err), and should be called once the server or DB is updated.
  • Model.read(model, data, property, callback is the method to override for reading individual properties from the server or DB. model is the model being read from, data is the model's data in the raw (you probably don't need to touch it!), property is the property currently being read, and callback is a callback of the form function(err, data), and you should pass successfully-retrieved data to it (or call it with an error).
  • Model.update(model, data, property, callback) is the method to override for updating individual properties on the server. model is the model currently being updated, data is the model's data in the raw (grab the property data from there, rather than asynchronously trying to get it with get), property is the property currently in need of syncing, and callback is a function of the form function(err), which you should call (without an error) on successful completion, or with an error upon an error.
  • Model.destroy(model, data, callback) is the method to override for syncing model destruction. model is the model currently being destroyed, data is its property data in the raw, and callback is a function of the form function(err), which should be called once the server or DB is updated.
  • Grit.find(findArg, callback) is the method to override for finding and loading entire models from the DB. findArg can be whatever you want, and callback should presumably be some sort of function that gets the model passed into it!
Property defaults, assertions
  • Model.defaults(defaultsHash) accepts a hash of default values to set for properties on model construction. If you don't specify default values for required properties, you must pass them into the constructor.
  • Model.required(properties...) marks properties as required. All required properties that do not have default values set must be passed at initialization into the Model constructor. If a required property is missing, all calls to set will fail to validate.
  • Model.unrequired(properties...) unmarks properties previously marked as being required using Model.required.
  • Model.has(properties...) marks the model as having the properties, although without necessarily requiring them to be present to pass validation. For loadAll to work correctly, all properties must have been specified with Model.has.
  • Model.lacks(properties...) unmarks properties previously marked as being had using Model.has.
  • Model.validate(property, validator) adds a validation callback to a property. Validation callbacks must be of the form function(value), and the value that gets passed in is the new value being set. If all validators of a property return truthy values, the property will successfully be set; otherwise, it won't.
Inheritance
  • Model.extend(constructorFunction) makes it easy for normal JS classes to inherit from Grit's Models. Just pass the constructor function to extend, and don't forget to call Model.call(this) (or SubClass.call(this)) on the first line of the constructor function. If you're using CoffeeScript, just use the native extends keyword, and if you override the constructor, don't forget to call super before trying to access any Model methods.

Placeholders

Creation

  • placeholder(findArg, ModelClass) takes any piece of data for the findArg and a class that extends from Model as the ModelClass argument. Creates and returns a Placeholder that will be able to proxy out to the ModelClass's find method by passing the findArg, and load specified Models only as needed.

Loading

  • placeholderInstance.load(callback) loads the Model specified in the creation function, if possible. callback should be of the form function(err, model), and will be called with the loaded model once it's ready.

Collections

Constructor

  • new Collection(models), where models is an optional array of models. Creates a new Collection containing the models.

Instance methods

  • collection.on(evt, handler) or collection.addListener(evt, handler) adds an event handler to the object. Collections forward all events from their contained models, and also fire add and remove events for functions of the form function(element), where element is what is being added or removed.
  • collection.removeListener(evt, handler) removes an event handler.
  • collection.push(options, element, callback), where options is an optional hash, element is a Model or Placeholder, and callback is an optional callback of the form function(err, model). This method pushes an element into the collection. If an options hash is provided and its forceLoad key is true, it will load any Placeholders into full Models and save them to the local cache. callback gets called once all loading is completed, or if no loading was done, as soon as the data is pushed in.
  • collection.pop(options, callback), where options is an optional hash and callback is an optional callback of the form function(err, model). Pops an element from the dataset. If an options hash is provided and its forceLoad key is true, the method will load any Placeholders into full Models. callback is called once all loading is completed, or if no loading was done then as soon as the data is popped out.
  • collection.pushFront(options, element, callback) is exactly the same as collection.push, except that it pushes to the front of the dataset.
  • collection.popFront(options, callback) is exactly the same as collection.pop, except that it pops from the front of the dataset.
  • collection.set(index, options, element, callback), where index is a number, options is an optional hash, element is a Model or Placeholder, and callback is an optional callback of the form function(err, model, removed). Sets the index in the collection to the element passed in. If an options hash is provided and its forceLoad key is true, will load any Placeholders seen -- including any being overwritten -- into full Models. The callback is called once all loading has completed, or if no loading is done then as soon as the element is in the dataset.
  • collection.remove(something, options, callback), where something is either a numeric index, a Placeholder, or a Model, options is an optional hash, and callback is an optional callback of the form function(err, model). If given an index, removes the item at the index. If given a Placeholder or Model, searches for the item given and removes it. BE AWARE: if an options hash is provided and its forceLoad key is set to true, and a Model or Placeholder is passed in, the entire dataset may be loaded. If an index is passed in, only the item at that index will be loaded. The callback will be called as soon as any loading is completed, or if no loading was done then as soon as the item is removed.
  • collection.at(index, options, callback), where index is a number, options is an optional hash, and callback is a function of the form function(err, model). Passes the element at the index in the dataset to the callback. If an options hash is given and its forceLoad key is true, will load any Placeholders found into full Models and store them in the local cache. callback will be called as soon as loading is completed, or if no loading was done it will be called immediately.
  • collection.length() returns the size of the dataset.
  • collection.forEach(options, fn, callback), where options is an optional hash, fn is a function of the form function(err, model, index) that will be run on every item in the dataset, and callback is an optional function of the form function(). Runs fn on every item in the dataset. If an options hash is provided and its inOrder key is true, the function will be called in order over the dataset; otherwise, it may run in order or out of order (the index passed in will always be the correct index, however). If the options hash's forceLoad key is true, every Placeholder in the collection will be loaded into full Models and saved to the local cache. callback will be called once fn has finished running on every element and all loading is complete.

LICENSE

GPL v3. Check LICENSE.txt and "GNU General Public License".txt for full details.