-
Notifications
You must be signed in to change notification settings - Fork 1
Immutable
Immutable allows us to generate constructors that validate schema's, using Blueprint, and to render immutable objects.
im•mu•ta•ble (ĭ-myo͞oˈtə-bəl), adj. Not subject or susceptible to change.
When out objects are subject to change, side effects can cause problems. Making our objects immutable, and achieving the Open/Closed Principle in JavaScript can be verbose. polyn's Immutable makes this trivial.
Immutable is part of the polyn package. To install it:
npm install --save polyn
Then you can require it like so:
var Immutable = require('polyn').Immutable;
Immutable is part of the polyn package. To install it, download the release
folder, or:
bower install --save polyn
Then add a script tag:
<script src="polyn.min.js"></script>
Then it will be available on the window:
var Immutable = window.polyn.Immutable;
Immutable builds on Blueprint. All Blueprint validation features are avaiable to Immutables.
var Foo = new Immutable({
num: 'number',
str: 'string',
arr: 'array',
currency: 'money',
bool: 'bool',
date: 'datetime',
regex: 'regexp',
obj: 'object',
func: {
type: 'function',
args: ['arg1', 'arg2']
},
dec: {
type: 'decimal',
places: 2
},
nullable: {
type: 'string',
required: false
},
custom: {
validate: function (val, errors, self) {
if (val !== 42) {
errors.push('custom must be 42');
}
}
}
});
Given the constructor above, the following would pass validation and return an immutable object:
var foo = new Foo({
num: 42,
str: 'string',
arr: [],
currency: '42.42',
bool: true,
date: new Date(),
regex: /[A-B]/,
obj: {
foo: 'bar'
},
func: function (arg1, arg2) {},
dec: 42.42,
custom: 42
});
However, the next example would return an exception object:
var foo = new Foo({});
If for some reason, you don't want the Immutable to be validated upon construction, you can defer or omit validation, by setting the __skipValdation
property:
var Foo = new Immutable({
name: 'string',
__skipValdation: true
});
// Now this will not generate an error
var foo = new Foo({});
If you wish to validate the Immutable later, you can use the validate
feature:
var Foo = new Immutable({
name: 'string',
__skipValdation: true
});
var foo = new Foo({});
// You can validate asynchronously
Foo.validate(foo, function (errors, result) {
console.log('errors:', result.errors); // prints errors: ['...']
console.log('result:', result.result); // prints result: false
});
// Or synchronously
var result = Foo.validate(foo);
console.log('errors:', result.errors); // prints errors: ['...']
console.log('result:', result.result); // prints result: false
When an error occurs, or validation fails, Immutable returns an exception object. It's easy to check for the occurrence for an error, by using the exceptions properties:
{
type: 'InvalidArgumentException', // types vary by exception
error: Error('hello world!'),
messages: ['validation error 1', 'validation error 2'],
isException: true
}
Here's an example that checks for the existence of an error:
var Foo = new Immutable({
name: 'string'
});
var foo = new Foo({ name: 'Andy' });
if (foo.isException) {
// do something with the exception
}
Immutable has a hook for exceptions. By default, exceptions are written to the console. You can override this behavior with whatever you want, using configure
.
Immutable.configure({
inError(function (err) {
throw err.error;
});
});
var Foo = new Immutable({
name: 'string'
});
// this would throw
var foo = new Foo({});
Just because we have an Immutable object doesn't mean we can't change the values of properties. We just can't change them on a given reference. When we need to modify an Immutable object, we can merge
it with another object, to create a new Immutable:
var Person = new Immutable({
firstName: 'string',
lastName: 'string'
});
// creates an instance of Person, with the
// firstName of Andy, and lastName of Bobby
var andy = new Person({ firstName: 'Andy', lastName: 'Bobby' });
// creates another instance of Person,
// with the firstName of Ricky, and the lastName of Bobby
var bobby = Person.merge(andy, { firstName: 'Ricky' });
This way, any code that references andy
will experience no side-effects when we change the first name to Ricky
. We end up with two separate objects.
Sometimes we need a plain old Object. Perhaps we need to update several properties of an Immutable. To do this, we can use the toObject
feature:
var Person = new Immutable({
firstName: 'string',
lastName: 'string'
});
// creates an instance of Person, with the
// firstName of Andy, and lastName of Bobby
var andy = new Person({ firstName: 'Andy', lastName: 'Bobby' });
// creates a plain old JavaScript Object with the
// Immutable's properties and values
var updated = Person.toObject(andy);
updated.firstName = 'Ricky';
// creates another instance of Person,
// with the firstName of Ricky, and the lastName of Bobby
var bobby = new Person(updated);
When logging Immutables to the console, the property values are displayed as [Getter/Setter]
. This is not terribly helpful when you're debugging. When you need to see the actual values that are set, use the log
feature:
var Person = new Immutable({
name: 'string'
});
var person = new Person({ name: 'Andy' });
// Prints { name: 'Andy' } to the console
Person.log(person);