Monday, February 10, 2014

Enyo Observables: Making kinds self-sufficient

These are exciting times in the Enyo world; Enyo is soon to release version 2.4. This is an extremely important release that brings MV* features and a host of language improvements. I am going to discuss a feature new to Enyo called "Observables", and the power it has when combined with Enyo's inheritance model.

Observables are a powerful tool for controlling an object's functionality. Although new to Eyno, observables are not a new idea and are implemented in other frameworks. The use of an observable is a simple idea. You create an object, then decide to watch a property on that object. The observable code is fired whenever the property is changed through a setter method. This allows for even finer modulariztion of code than normally possible. Functionality can be tied to the affected object directly. Functions and objects outside the object no longer need to understand how it should work.

Let's create a simple form with an audit trail. Without Observables, we would most likely have an event handler at the document or field level to update the audit trail when the fields change. With Observables, we are going to update the audit trail when a particular object property changes. The function bound by the observable will know how to update the audit trail. The event handlers are only going to tell the application object to change a single property value.

The form will consist of:

  • Input field
  • Repeater (The repeater is pulling data from a collection, and the bind object details that relationship. Binding is another feature coming to Enyo that I won't be discussing here)
The structure of the form is defined as follows:

enyo.ready(function () {
    enyo.kind({
        name: 'Area',
        kind: enyo.Control,
        title: 'Observable Test',
        dirty: false,
        modified: 0,
        lastModified: '',
        observers: {
            processDirty: ['dirty']
        },
        rendered: function () {
            this.inherited(arguments);
            this.$.repeater.set('collection', new enyo.Collection());
        },
        components: [{
            tag: 'span',
        }, {
            tag: 'br'
        }, {
            kind: enyo.Input,
            onchange: 'markDirty'
        }, {
            name: 'repeater',
            kind: enyo.DataRepeater,
            components: [{
                components: [{
                    name: 'item',
                    tag: 'li'
                }],
                bindings: [{
                    from: ".model.message",
                    to: ".$.item.content"
                }]
            }]
        }, {
            name: 'counter',
            tag: 'span',
            content: 'Modified 0 times.'
        }]
    });
});

There are two important pieces to notice in the above. The property we are going to use as our flag is the "dirty" property. The observable will be watching for when dirty's value is changed, perform an operation, and then reset the field to false. The second piece is the observers clause (processDirty: ['dirty']). The key is a function name, and the value array contains the property(s) on the kind that will fire the event when changed. The processDirty function will look like this:

 
    processDirty: function () {
           if (this.lastModified) {
                this.$.repeater.collection.add({
                    message: this.lastModified + ' modified on ' + new Date()
                });
            }
            this.$.counter.setContent('Modified ' + (++this.modified) + ' times.');
            this.lastModified = '';
            this.dirty = false;
        }
 

This function updates the fields who are linked in functionality to the "dirty" property of the kind. First it checks to see if the kind has a valid date in the lastModified property. If so, then it adds a line of text to the repeater's collection. The collection is an EnyoCollection to which the DataRepeater is bound. The repeater reflects in the UI what is stored in the collection. The result is that the front end will update the UI with the latest addition.

The final piece to tie this all together is to have the text fields change the "dirty" property. The function that will make this change is called "markDirty". Notice that the Input component has an onchange handler. Onchange is an event that Enyo Controls automatically fire, so identifying the "markDirty" function is all that is required. The markDirty function uses setter methods to set the properties. This is an important point. Observables only work when using the setter method. Changing the property directly will not cause the Observable to fire. The function looks like this:


        markDirty: function (inSender) {
            inSender.owner.set('lastModified', inSender.id);
            inSender.owner.set('dirty', true);
            return true;
        }

Observables are a powerful tool in every language they appear, including Enyo. One feature that Enyo has that makes this even more powerful is it's inheritance model. We can create a child kind from this kind and have that function run automatically. In practical terms, you can set a base set of operations for all your like controls. In the UI, you can create children that are adapted to meet that UI's needs, however, all of them are guaranteed to share a set of common operations.

For example, we will create a child of the above example. It will function almost exactly like the parent however, we want to enhance the observer function. Let's say entering data in the child is dangerous and we want to notify the user. We use this.inherited to call the parent observer function and update the audit trail, then we will add an alert specifically for this field:


    enyo.kind({
        kind: 'Area',
        name: 'AreaChild',
        processDirty: function () {
            this.inherited(arguments);
            alert('This is a serious change');
        }
    });
The child uses all of the parent's functionality, and only adds it's own flavor to the existing template. Now when the user changes the child kind field the audit trail is updated, and an alert warning the user is fired. Combining Observables and inheritance would allow a developer, or a group of developers, to create a common set of objects to work from. A toolbox of well crafted objects can create a strong foundation for all applications to inherit from.

All code can be found at http://jsfiddle.net/dposin/spj7q/

Thursday, November 21, 2013

Jasmine-Node and Rewire, Part 2: Making the test complete

In the previous post I introduced the concept of using Rewire with Jasmine-Node to simplify Node.js unit testing. Rewire added a __set__ and a __get__ function to the module which let us grab a function in the module, and test it directly. We could test the function's output by controlling the objects that the function worked with, and checking them upon function completion. We didn't need to manipulate any internals of the function so our testing is still honoring the "Black Box" concept of unit testing. The idea is that the unit test only cares about what the testee can take in, return, and manipulate outside itself. How that operation is performed is not important to the unit test.

There was one test we didn't look at which I want to discuss now.

Monday, October 21, 2013

Jasmine-Node and Rewire, Part 1

Node.js development can be a lot of fun. Unit testing can be fun too. Unit testing Node can be very much not fun. Unit testing Nodejs modules often feels like trying to build a neutered integration test with mocks, nocks, and intercepts. Since Node is a web server whose functionality revolves around responding to http requests, the most natural testing method would be to trip those URLs and check the response. However, that ends up being more of a test of the middleware, and doesn't allow for testing the function directly. If you want to ignore the http calls, the most rational way to test the functions inside the module would be to export them. Once exported the tests can easily be run against the exported functions. The problem with exposing all the functions via export means functionality that should be private is no longer private.

Tuesday, July 2, 2013

Using Nock and Jasmine-Node to Target Your Server's HTTP Requests

I am continuing along my path of "test driven development" discovery and am finding it fraught with challenges. All my progress has shown me how crucial TDD is for success, and I am constantly reminded of how effective it is. The latest challenge I had was to test the outgoing http call of my node server. I was testing a module that sent an http request in a private method. It was crucial that my testing confirm the outgoing URL. This is not as straight forward as it sounds.

Saturday, March 16, 2013

Using --Configure in Jasmine-Node Command Line

I recently had the honor of one of my minor changes to the Jasmine-Node project being merged into the project master branch. I added a new option to the command line used to invoke the Jasmine-Node tests. Using a --configure option followed by two additional entries will add a variable to the process.env in the invoked node process. Adding this feature allowed me to move my tests with my node server through it's path from testing to production.

Wednesday, February 20, 2013

Using CSS Media Queries as a Data Transport

Sorry for the long delay. I recently changed jobs and with family I had no time to get this out. I am going to try to return to every other week articles again.

My recent work involved creating web destinations based on both dynamic content, dynamic form, and responsive design with no preset dimensions. The pages would have to adjust to fit multiple sizes set at compile time. Each size could have its own specific properties and shared properties. The one thing I did know was that I could rely on any screen specific properties to have an identifier appended to the property name to make it unique. I needed a way to find the correct identifier for the current screen size. To make this work we decided to use CSS to store it. The solution worked for all modern browsers. Internet Explorer 8 received an alternate css and workflow since it doesn't support the MediaQueryList object.

Sunday, January 13, 2013

Tips For Using JsDoc on Enyo Kinds, and Other Factory Pattern Types

My journey to better practices has led me through the forest of unit testing to a new destination. I have reached that dark, scary marsh that many in the IT world dread. The very mention of its name sends grown men to tears and panic. I find myself at the Marsh of Documentaion!