What it does?
Gisele is tiny wrapper around JS plain objects combined with a little type validation system.
Starting with a simple use case, we are going to write a wrapper for a User entity, which has four fields: id, name, email and active. Both name and email are strings, id is a read-only number and active is a boolean.
The syntax is quite simple. Each fields has a type, and for the most common data types, we use the built-in constructors already present in the Javascript language.
This is what a User model looks like:
From now on, we can make instances of our User structure with actual user data:
Bob is now an instance of User, an object with nothing unusual. But there’s a trick used from ES5 that allows to define getters and setters for any object property.
That’s where the wrapper comes in: we can change the value of our model attributes, but they are actually written to an internal state object. Only when we call a method that this changes are actually applied over the initial data.
How it works
The fields you give to Model.create() are transformed into a custom property on each model instance. When you instantiate a model, a getter/setter pair of functions is defined for each field to handle changes. Whenever a field is changed, the setter function is called to save this in a special property. This is done with Object.defineProperty method.
The field values are stored apart from the instance, so the model state can be changed or restored through instance methods. This enables an easier tracking of changes to be saved. For example, a model instance can be attached to fields in a form. This works perfectly with frameworks like AngularJS. The model exposes a property $$dirty that flags a changed model.
Look at this example:
So, in our example, if we change Bob’s name or email, this can be reverted with a simple method call. The changes can also be applied to our original data, so it becomes one (the original data + the changes).
The Field class
Along with the Model class, there’s also Gisele.Field, which is a base class to define new fields.
Here’s an example of how to define a new field type called Point. This snippet is ES6.
From now on, we can declare a model using this type. There is one rule to follow here: the constructor must have only one argument. Since we are dealing with model fields, which represent a single value on a model, we can’t declare a constructor that deals with more than one thing.
We can also define some methods to our new model and use arrays of values in a field:
As you can see, the field declaration is a combination of a Constructor function and a Field subclass, and the field has a mechanism to parse/serialize values. This also works as a validator to each field.
Extending the model methods
The base class to all the models has a property that points to a prototype shared with every instance of our models. Model.fn, an object with some methods already defined on it.
When a new model instance is created, it receives a property named $$, which gives access to model’s inner methods. They are not part of our model’s prototype to avoid conflicts with property names.
Let’s override the default commit method with a new function:
Now everytime the model changes are applied, the changes are printed to the console. More methods can be added to this property as well. From a model custom method, the commit here can be accessed as this.$$.commit, as you can see in the Rectangle example above.
Gisele - a model library
Reviewed by Knowledge Valley
on
October 19, 2015
Rating:
No comments: