Skip to content
&y edited this page Nov 20, 2016 · 17 revisions

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.

Usage

Node

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;

Browser

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;

Validation

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({});

Lazy validation

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

Exceptions

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
}

onError

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({});

Mutation

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.

Object

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);

Logging to the Console

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);