Overview
A tiny framework for creating models in node.js based server code.
When writing server code that fetches and persists data to a backing store, be it MySQL, Redis, Riak etc. there are several features you want and a certain amount of boilerplate code that is needed. MnoVnoC is a simple framework that provides the following helper features:
- Validate model state before persisting to the backend
- Know if data has changed and needs saving
- Make CRUD (Create, Read, Update and Delete) operations consistent
- Abstract backend calls from you app code, so you can hide implementation details and centralize connection pooling, retry logic etc.
The Model follows an Active record pattern: http://en.wikipedia.org/wiki/Active_record_pattern
Installing
npm install MnoVnoC
Model
This is the meat of the module. The sections below explain how to declare, define and use models.
Defining a model and creating an instance
var Model = ;var Person = Model; var p = name: 'bob' age: 55 ;pname // bobpage // 55
At this point you have a working model, but you cannot perform any CRUD operations using this mode, to do so see the CRUD section below.
Settings defaults on a model
When you define a model you can specify defaults for the properties in the model. If these values are not passed in the constructor they will get set on the new model e.g.
var Model = Model;var Person = Model; var p = ;pname // frankpage // 50 var p2 = name: 'bob' ;pname // bobpage // 50
Validating a models state
One of the main things you want to be able to do in a model is validation of fields to make sure user values conform to the business rules of your model. You can explicity call validate() to do a check of the current state of the model, or when you call save() the validate() method is called before the model tries to save the data, you can check the error parameter in the save callback to see if there were any validation errors.
NOTE: you must always code validate() with a callback.
Example model definition with validation function
var Model = Model;var Person = Model;
Explicit validation check
var p = name: 'joe' age: 200; p;
Validation checks on save
var p = ; p;
CRUD - create, read, update, delete
When you define a model, it comes with a field called "data" which is an object containing the following methods, create, fetch, update, destroy. If you try to call these default methods an exception will be throw. In your model definition you can override the data object and set the methods to connect and handle whatever backend store you are utilizing e.g.
var Person = Model;
IMPORTANT: You never call the data.XXX methods directly, they are called indirectly by other methods on the model, as explained below.
Model.fetch()
To fetch values from your backing store into a model you need to override the data.fetch function.
var Model = Model;var Person = Model; var p = id: 123 ;p;
NOTE: The options parameter is optional, but there may be cases where you want to pass in optional data that gets passed through to the data.XXX methods
Model.destroy()
To delete an instance of your model, you need to override the data.destroy method:
var Person = Model; var p = id: 1234 ;p;
Model.save()
To create or update an existing model, you call model.save(), it takes a callback and an options parameter (optional). Calling save can do one of two things, if the model represents a "new" object on the backend then data.create will be called, if the model represents an existing entity on the backend then data.update() will be called.
To distinguish between the two, if a model instance has a field called "id" then it is assumed the model represents an existing data item and update() is called, if the model instance does not have an "id" field, then it is assumed to be a new data item and "create" will be called.
An example of creating a new model
var Person = Model; // Because the model does not have an "id" value, it is assumed// to be new and so data.create will be calledvar p = name: 'bob' age: 55 ;p;
NOTE: In the create method, you will want to set model.id with the ID of the data from the backing store, before calling back from create so that the model instance now has an id field. If you don't do this then if you reuse the model instance and call save() again, create() will be called instead of update() since the model will still be considered "new" without an id field.
An example of updating an existing model
var Person = Model; // Since the model has an "id" property, it is considered to exist already on// the backend, hence any call to save() will direct to data.update() instead of// data.create()var p = id: 1234 name: 'frank' ;p;
Model.changed / Model.hasChanged
Sometimes you want to know if a model is dirty, meaning some of the fields have changed since the model was last fetched, you may also want to know which of the fields have changed since the last time. You can access this information via Model.hasChanged() which returns true if the model has changed since it was last fetched, and Model.changed() is an object that contains the fields that have changed.
HOWEVER... in order to get this information, you need to update fields using the Model.set(name, value) function. If you set a value directly on an object e.g.
myPersonname = 'Pete';
This will not update the changed event, so instead, you need to update the object via the set() method e.g.
myPerson;
If you do this inside the update method you will get the hasChanged() and changed() information e.g.
Use getters and setters on your models
The set() method is nice, but calling myPerson.set('name', 'Pete') is downright ugly, instead I would recommend that you use JavaScript getters and setters. If you haven't used these before, they are just methods you can define that get called when you get/set a field on an object. Take 2 minutes to read this article: http://ejohn.org/blog/javascript-getters-and-setters/ before continuing if you don't know how to use getters/setters already.
By using getters/setters on your model, you can hide the ugly set(name, value) calls behind normal myPerson.name = 'Frank' syntax and still get the benefit of the changed() and hasChanged() information.
Below is an example of how you can use getters and setters.
/** * This example shows you how you can use getters/setters to make your * model easy to use and get changed information */ var Model = Model; var Person = Model; var p = id: 1234 name: 'frank' age: 95 salary: 10000; // Take a peek at the current state of the modelconsoledirp; // Lets update somethingpage = 96;psalary = 15000; // We can see what has changedconsole;consoledirp; // save the updated model, since this model has an id, it is assumed// it is an existing model that is being updated, so it goes into the// data.update methodp;
NOTE: Hopefully you read the comments, but if you call set() with a parameter name that starts with _ when you call changed() the name of the fields in the changed() object has the _ removed.
Events
Any model you define that derives from Model, will have event support, since Models inherit from the node.js EventEmitter class.
To trigger an event on your model use the "emit" method e.g.
var Model = ;var Person = Model; var p = name: 'bob' age: 55 ;p; p; // event raised at this point
refreshedAt
One way to tell if a models data has been fetched from the backing store is to check for the presence of a field called refreshedAt on the model. Every time fetch() is successfully called the refreshedAt value contains an updated Date value e.g.
var p = id: 12345 ;console // undefined p;
Collection
No collection support at the moment, if you need one you can add it :)
More Examples
For more examples, see the unit tests
Development
git clone git@github.com:markdaws/MnoVnoC.gitcd MnoVnoCnpm installnpm test