Okay, so you have a node app backed with some kind of NoSQL schema-less DB such as CouchDB and it all works pretty well,
But hey, even though schema-less is very cool and produces fast results, for small apps it may make sense, but as application code grows bigger and bigger, you will eventually end with low data integrity and things will start to become messy,
So this is what nodejs-model is for, it is a very minimal, extensible model structure for node, it doesn't dictate any DB requirements nor hook into it directly, it's just a plain javascript object with some enhanced capabilities for attributes accessors, validations, tagging and filtering.
Note: If you are aware of Ruby AcitveObject Validations you will probably find some common parts with the validation capability of it, But nodejs-model goes much further, read on :-)
If one or more of the bullets below makes sense to you, then you should try nodejs-model.
- Model attributes: A lightweight javascript model with simple accessors.
- Attribute validations: define validation rules per defined attribute.
- Accessibility via tags: Tag attributes with some labels, then allow retrieving/updating only attributes that matches some tags.
- Events: Events are fired when objects are being created or properties are modified.
- Converters: Simply hook converters into attributes, for example an encryptor converter may attach to the password attribute of a User model to encrypt the user's password immediately after it is set with new value.
#Installation
To install nodejs-model, use npm:
$ npm install nodejs-model --save
This is how it works:
Create a model definition with some validation rules
var model = require('nodejs-model');
//create a new model definition _User_ and define _name_/_password_ attributes
var User = model("User").attr('name', {
validations: {
presence: {
message: 'Name is required!'
}
}
}).attr('password', {
validations: {
length: {
minimum: 5,
maximum: 20,
messages: {
tooShort: 'password is too short!',
tooLong: 'password is too long!'
}
}
},
//this tags the accessibility as _private_
tags: ['private']
});
var u1 = User.create();
//getters are generated automatically
u1.name('foo');
u1.password('password');
console.log(u1.name());
//prints _foo_
//Invoke validations and wait for the validations to fulfill
u1.validate(function() {
if u1.isValid {
//validated, perform business logic
} else {
//validation failed, dump validation errors to the console
console.log(p1.errors)
}
});
//get object as a plain object, ready for JSON
console.log(u1.toJSON());
//produces: { name: 'foo' }
//now also with attributes that were tagged with 'private'
console.log(u1.toJSON('private'));
//produces: { name: 'foo' } { password: 'password' }
Simple as that, your model is enhanced with a validate() method, simply invoke it to validate the model object against the validation rules defined in the schema.
Assuming you have a simple model instance (u1
as defined in the basic example above, you can update it with new data
at some point after loading an object from DB / file / JSON / etc:
someObj = {
name: 'bar',
password: 'newpassword'
};
u1.update(someObj);
console.log(u1.name());
//prints bar
console.log(u1.password());
//NOTE: prints password
Pay attention that password wasn't updated, this is because when invoking update(object)
only public attributes (any
attribute that its tags metadata wasnt defined or defined as ['default'] can be updated.
With this specific example, since password is tagged with private, you can update by suppling the private tag
to the update()
2nd parameter as:
u1.update(someObj, 'private')
console.log(u1.name());
//prints bar
console.log(u1.password());
//NOTE: prints newpassword
The Presence ensure that an attribute value is not null or empty string, example:
var User = model("User").attr('name', {
validations: {
presence: true
});
true
- value will be required, default message is set.message
- string represents the error message if validator fails.
Example with custom message:
validations: {
presence: {
message: 'Name is required!'
}
}
Validates rules of the length of a property value.
is
keyword ornumber
- An exact lengtharray
- Will expand tominimum
andmaximum
. First element is the lower bound, second element is the upper bound.allowBlank
- Validation is skipped if equal totrue
and value is emptyminimum
- Minimum length of the value allowedmaximum
- Maximum length of the value allowed
wrongLength
- any string represents the error message ifis
/number
validation fails.tooShort
- any string represents the error message ifminimum
validation fails.tooLong
- any string represents the error message ifmaximum
validation fails.
// Examples of is, both are equal, exact 3 length match
length: 3
length: {is: 3}
//same as above, but empty string is allowed
length: { is: 3, allowBlank: true }
//min legnth: 2, max length: 4
length: [2, 4]
//same as above with custom error messages
length: { minimum: 2, maximum: 4, messages { tooShort: 'min 3 length!', tooLong: 'max 5 length!' } }
Regexp test validator
with
- the regular expression to testallowBlank
- Validation is skipped if equal totrue
and value is emptymessage
- any string represents the error message.
// Examples
format: { with: /^\d*$/, allowBlank: true, message: 'only digits are allowed, or empty string.' }
nodejs-model supports tags per defined attribute, when new attribute is defined with no tags it will be automatically tagged with the default tag.
Methods such as toJSON(tags_array) or update(updatedObj, tags_array)
are accessbility aware when
updating or producing model instance output.
You can define tags per attribute by:
User = model("User").attr('name', {
tags: ['ui', 'registered']
}).attr('password', {
tags: ['private']
}).attr('age');
u1 = User.create();
u1.name('foo');
u1.password('secret');
u1.age(55);
console.log(u1.toJSON());
//prints { age: 55 }, this is because invoking toJSON(), it will only create an object with attributes defined
as public.
console.log(u1.toJSON(['ui', 'private']));
//prints { name: 'foo', password: 'secret' }
//* means any property with any tags
console.log(u1.toJSON('*'));
//prints { name: 'foo', password: 'secret', age: 55 }
Update mehtod someInstance.update(newObj, tags)
is also tags-aware as with someInstance.toJSON(tags)
.
#Initializing Model Instances
It is possible to initialize a model instance by suppliying an init
method on the Model level,
Here is an example how to initialize a creation date attribute for a model:
var P = model('Person').attr('name').attr('creation_date');
//will be invoked just after a model is instantiated by P.create()
P.init = function(instance) {
instance.creationDate(d);
};
p1 = P.create();
console.log(p1.creationDate())
//prints a date
#More Info Check wiki pages:
#Contributers
- amitpaz - Co author, design, tests, etc.
#Contributions
You can contribute in few ways:
- Just use the module, this is the open source way, right? more usages, more stable and robust the model will be.
- Star it! :) - if you'r happy with it and find it useful.
- Code, if you are a coder and would like to contribute code then visit the Development page.
#License
See LICENSE file.