Friday, April 6, 2012

PlastronJS

For an MVC I'm going to be using PlastronJS.

It's a shameful plug because I wrote it. It should be familiar if you've used anything like backbone, spine, angular, ember or any number of MVC frameworks out there. So why did I write another one? A few reasons.
  • There isn't a framework that integrates well with the closure compiler
  • Most frameworks have dependancies on jquery and not closure-library
  • The frameworks are fairly lightweight
  • I was bored
PlastronJS is slightly different to what you've been used to. At it's core it works the same way - there are models that communicate to a data layer through sync and you can bind functions to changes in the models data.

So what is different? Probably the best reason to use PlastronJS are the schemas. A schema can be put on a model to provide you with a different interface for accessing the data. For instance at Catch we hold information about notes under their 'annotations'. If I want to star a note I would have to get the annotations object, change the 'catch:starred' attribute and then set the 'catch:starred' attribute. I'd also have to listen to changes on the annotations object, so any other changes to that would fire the function.

SCHEMA


A schema allows the setting and getting of custom attributes and helps you describe them based on attributes that already exist in the model. For instance I can create a star attribute like this:

// schema object
var schema = {
  'star': {
    get: function(annotations) {
      return annotations && annotations['catch:starred'];
    },
    set: function(star) {
      var ann = this.get('annotations');
      goog.object.extend(ann, {'catch:starred': star});
      this.set('annotations', ann, true);
    },
    require: ['annotations']
  }
};

The require attribute tells us what attributes from the model we need to get/set star. The get function will receive the get() value of our requires array. So if we required two elements we would have those two as inputs to the get function. The get function can also throw errors for validation and they will be caught by the model's error function.

When setting the value we'll usually pass the optional "silent" parameter if using the set() with other attributes. This is because the original set() call for star is the one we want to decide whether to fire a change event. You could also just set it on the actually data in the model like this:

this.attr_['annotations'] = ann;

You'll also notice that the functions are bound to the model, so you can just call 'this'.

There is also compare functionality in the schema. A model decides what has changed by comparing the current attribute and a saved attribute from the last change. When saving an attribute it does an unsafe recursive clone (so no recursive references in the model's data please) so the object will not be equal. Primitive types and arrays of primitive types are checked for equality, otherwise you'll probably want to define a comparison function.

// schema object
var schema = {
  'annotations': {
    cmp: mvc.Model.Compare.RECURSIVE
  }
};

There are already a couple of functions under mvc.Model.Compare you can use. RECURSIVE goes through the object/array recursively and checks that everything matches. There is also a STRING and a SERIALZE that compares the .toSring() and JSON.serialze() of the object against each other. You may notice I didn't need to give a cmp for 'star' and that's because event though it is under an object it's getter will only return a primitive type.

If you want to learn more about Schemas in PlastronJS head over to https://github.com/rhysbrettbowen/PlastronJS and go through the README and the source code (which should be commented thoroughly). Hopefully you'll be able to define an interface for your complex models which will relieve a lot of the stress when working with them in more complex applications.

1 comment: