Modélico [moˈðe.li.ko] is a universal-JS library for serialisable immutable models.
Note: babel-polyfill might be required
for browsers other than Chrome, Firefox and Edge. Additionally, IE 9 & 10 also require
a getPrototypeOf
polyfill as the one in es5-sham.
See browser tests for more details.
Installation
npm i modelico
To use it in the browser, grab the minified or the development files.
Run the current tests directly on your target browsers to see what setup is right for you:
Quick intro
The goal is to parse JSON strings like the following into JavaScript custom objects
{ "name": "Robbie"}
so that we can do things like this:
const pet1 = M; pet1; //=> 'my name is Robbie!' // pet1 will not be mutatedconst pet2 = pet1; pet2name; //=> 'Bane'pet1name; //=> 'Robbie'
M.fromJSON
is a simpler way to do the following:
const _ = Mmetadata;const pet1 = JSON;
Here is how Animal
would look like:
const M = ;const string = Mmetadata; Base { superAnimal fields; } { const name = thisname; return name === '' ? `I don't have a name` : `My name is !`; } static { return Object; }
A more complex example
The previous example features a standalone class. Let's look at a more involved example that builds on top of that:
{ "givenName": "Javier", "familyName": "Cejudo", "pets": [ { "name": "Robbie" } ]}
Notice that the data contains a list of pets (Animal
).
Again, our goal is to parse JSON into JavaScript classes to be able to do things like
const person1 = M; person1; //=> 'Javier Cejudo'person10; //=> 'my name is Robbie!'
Note: pets() returns a Modelico.List
, hence the need to use .inner()
to grab the underlying array. See the proxies section
for a way to use methods and properties of the inner structure directly.
We are going to need a Person
class much like the Animal
class we have already defined.
const M = ;const _ string list = Mmetadata; Base { superPerson fields; } { return this this; } static { return Object; }
A note on immutability
Following the examples above:
const person2 = person1; // person2 is a clone of person with the givenName// set to 'Javi', but person is not mutatedperson2; //=> 'Javi Cejudo'person1; //=> 'Javier Cejudo' const person3 = person1; person30name; //=> 'Bane'person10name; //=> 'Robbie'
Optional / null values
In the examples above, a pet with a null
or missing name
would cause a
TypeError
while reviving.
const pet = M;//=> TypeError: no value for key "name"
To support missing properties or null
values, you can declare the property
as a Maybe
:
const M = ;const string maybe = Mmetadata; Base // ... same as before static { return Object; }
Then, we can use it as follows:
const pet = M; petname; //=> truepetname; //=> Bane
ES2015 proxies
Most built-in types in Modélico (List, Set, Map, EnumMap and Date)
are wrappers around native structures. By default, it is necessary to
retrieve those structures to access their properties and methods
(eg. list.inner().length
).
However, if your environment supports ES2015 proxies, Modélico provides utilities to get around this:
const M = Modelico;const p = MproxyMap; const defaultMap = MMap;const proxiedMap = ; // without proxiesdefaultMap; //=> 2defaultMapsize; //=> 3 // with proxiesproxiedMap; //=> 2proxiedMapsize; //=> 3
Please note that native methods that modify the structure in place will instead return a new modelico object:
const proxiedMap2 = proxiedMap; proxiedMapsize; //=> 3 (still)proxiedMap2size; //=> 2
See proxy tests for more details.
ES5 classes
To support legacy browsers without transpiling, Modélico can be used
with ES5-style classes. In the case of our Animal
class:
{ var m = Mmetadata; { MBase; } Animalprototype = Object; Animalprototypeconstructor = Animal; Animalprototype { var name = thisname; return name === '' ? "I don't have a name" : 'My name is ' + name + '!'; }; Animal { return Object; }}windowModelico;
Acknowledgments 🙇
Inspired by Immutable.js, Gson and initially designed to cover the same use cases as an internal Skiddoo tool by Jaie Wilson.