How to “include” shell source in Perl script

June 27, 2017

Often times you’d want to share a set of environment variables between shell scripts and Perl scripts. Suppose that our variables are defined in a script called “etc/env”:

FOO=BARoo
GURGLE="throbbozongo"

In shell, we can easily include our “env” script with “source” command:

#!/bin/sh
. etc/env
# Or equivalent but more readable:
source etc/env

Right, but how do we include shell script in Perl script? The obvious solution is to read the file, split on /=/ and assign the variables, easy peasy:

open my $env, '<', 'etc/env';
while (<$env>) {
    chomp;
    my ($var, $value) = split /=/;
    $ENV{$var} = $value if $var;
}
close $env;

Uh, wait a bit… $ENV{GURGLE} is now double quoted? Ha, that’s easy too:

...
   $ENV{$var} = ($value =~ s/^['"]|['"]$//gr) if $var;
...

A couple more iterations to account for matching single/double quotes and we may be happily on our way. However we will soon find out that this approach is too naïve to survive the first contact with the Real World: Bourne shell has many quirks and string quotation/interpolation is among the weirdest. Do we really want to account for cases like this?

DISCLAIMER='I '"positively"' can'\''t and won'\''t '"parse"' that!'
QUX=`echo "$FOO" | tr [:upper:] [:lower:]`
SQL="
select foo from bar where baz = '$QUX';
"

These are relatively innocent examples by a long shot, and over time even more interesting stuff will creep into your “env” scripts. Hey it’s Bourne shell, anything goes!

Right, uh… Right… Let’s use shell to parse shell scripts! Yeah!

#!/usr/bin/perl

use strict;
use warnings;

# set -a is to auto-export all variables
my $env = `set -a; source etc/env; env`;

for my $line (split /\n/, $env) {
    # see above...
}

But something’s wrong. Some variables not set? We’re looking inside $env and see this:

...
SQL=
select foo from bar where baz = 'barbaz';

QUX=barbaz
...

What? What?! Oh right, we’re splitting on /=/… That’s why we have $ENV{SQL} that is an empty string and $ENV{select foo from bar where baz } that contains ” ‘barbaz’;”…

I could go on and on with that but I believe I should have proven my point by now: parsing shell scripts is not an option. Not even a last resort option. It’s just too damn hard!

Ok ok you got me you say. Let’s do it the other way around, by including our Perl code in Bourne shell script instead, using the old arcane incantations we found in the depths of perlrun. Note “-a” argument to /bin/sh; this is to export all defined environment variables automatically:

#!/bin/sh -a
#! -*-perl-*-

. etc/env

eval 'exec perl -x -S $0 ${1+"$@"}'
    if 0;

print STDOUT "THIS IS PERL BABY!\n";

Eh, not really…

$ bin/foo
syntax error at bin/foo line 4, near "."
Execution of bin/foo aborted due to compilation errors.

Grr! This is getting ridiculous! Let’s use our Dark Bourne Shell Powers we’ve learned in the Battle of the Quotes to fool both interpreters:

#!/bin/sh -a
#! -*-perl-*-

# This line is evaluated to '. etc/env' in shell and does what we need,
# i.e. includes the etc/env script; in Perl this line evaluates to
# concatenation of two strings into "etc/env" and effectively does nothing.
"". "etc/env";

eval 'exec perl -x -S $0 ${1+"$@"}'
    if 0;

print STDOUT "THIS IS PERL BABY!\n";

Now behave, you piece of electronic crap!

$ bin/foo
THIS IS PERL BABY!

MUA-HA-HA! Now you see! But wait, now you don’t…

$ perl -d bin/foo

Loading DB routines from perl5db.pl version 1.49_05
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

/bin/sh: -d: invalid option
Usage:	/bin/sh [GNU long option] [option] ...
	/bin/sh [GNU long option] [option] script-file ...

(facepalm) (facepalm) (facepalm) That shebang… It’s a shell script, remember? Well our old pal Perl is just trying to be helpful and calls the “right” script interpreter for us… Except that it’s not.

Ok ok. You see where I’m getting at, right? Let’s peruse the arcane volume of perlrun once more, and read right below the place where we found the eval incantation:

       If the "#!" line does not contain the word "perl" nor the word "indir"
       the program named after the "#!" is executed instead of the Perl
       interpreter...

The word “perl” nor the word “indir”… Uh, what if?..

#!/bin/sh -a #indir
#! -*-perl-*-

"". "etc/env";

eval 'exec perl -x -S $0 ${1+"$@"}'
    if 0;

print STDOUT "THIS IS PERL BABY!\n";

WHOA! IT WORKED!

$ bin/foo
THIS IS PERL BABY!

$ perl -d bin/foo

Loading DB routines from perl5db.pl version 1.49_05
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(bin/foo:4):	"". "etc/env";
DB<1> R
Warning: some settings and command-line options may be lost!

Loading DB routines from perl5db.pl version 1.49_05
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(bin/foo:4):	"". "etc/env";

And we don’t even need -x -S parameters as well as the pretty but unnecessary
“-*-perl-*-” comment/decoration, since we’re effectively executing the whole
script in Perl anyway. So the final incantation looks like this:

#!/bin/sh -a #indir

"". "etc/env";

eval 'exec perl $0 ${1+"$@"}'
    if 0;

print "sql: $ENV{SQL}\n";
print "disclaimer: $ENV{DISCLAIMER}\n";
print "argv: " . (join ' ', @ARGV) . "\n";

Lo and behold!

$ bin/foo bar baz
sql: 
select foo from bar where baz = 'barbaz';

disclaimer: I positively can't and won't parse that!
argv: bar baz

Neat huh? Happy coding! ;)

How to get Ext JS Component by a DOM element

February 13, 2014

Sometimes you need to operate at the DOM level, with DOM elements. And sometimes when you’re doing something with an element, you need to know which Component it belongs to. Since a Component may actually consist of many elements, finding a Component from DOM may seem hard to do, when in fact it is not.

The trick is to use DOM element id. In Ext JS, every Component has a main element, whose id is the same as Component’s id; so finding the Component turns out to be rather trivial:

        function findComponentByElement(node) {
            var topmost = document.body,
                target = node,
                cmp;

            while (target && target.nodeType === 1 && target !== topmost) {
                cmp = Ext.getCmp(target.id);

                if (cmp) {
                    return cmp;
                }

                target = target.parentNode;
            }

            return null;
        }

        var node, cmp;
        
        node = document.getElementById('foo');
        cmp  = findComponentByElement(node);

If you have an Ext.Element object instead of a bare HTMLElement node, just pass its DOM pointer instead:

var el, cmp;

el  = Ext.Element.getActiveElement(); // Currently focused element
cmp = findComponentByElement(el.dom);

Happy coding! :)

Using render selectors to capture element references

August 14, 2013

One of the little known features of Ext JS is renderSelectors. It is used mostly internally but can come handy from time to time on the application side, too. The main purpose of the renderSelectors is to support compound Component templates: when you create a Component that consists of several DOM elements, grabbing the references to them manually is kind of boring. So suppose that you want to create a Component that has an image and a piece of text, some kind of a banner:

<div class="mycomponent">
    <img src="/foo.jpg" style="float:left">
    <p>This is some text</p>
</div>

You can probably implement it like that:

Ext.define('MyComponent', {
    extend: 'Ext.Component',

    cls: 'mycomponent',
    tpl: [
        '<img src="/foo.jpg" style="float:left">',
        '<p>This is some text</p>',
    ],
    data: {} // Needed for the tpl to kick in on rendering
});

That would work as expected, right? You can create an instance of this Component, and it will render the structure we wanted to the DOM. But what if you need to change the image and text over time? Let’s make this Component dynamic by using the DOM selectors:

Ext.define('MyComponent', {
    extend: 'Ext.Component',

    cls: 'mycomponent',
    tpl: [
        '<img src="/foo.jpg" style="float:left">',
        '<p>This is some text</p>',
    ],
    data: {},

    setImage: function(newSrc) {
        this.getEl().down('img').set({ src: newSrc });
    },

    setText: function(newText) {
        this.getEl().down('p').setHTML(newText);
    }
});

There is nothing wrong with this approach, but doing DOM lookups every time is a bit inefficient. Taking references and reusing them would be much more effective, and this is the way it was done before Ext JS 4.x:

Ext.define('MyComponent', {
    extend: 'Ext.Component',

    cls: 'mycomponent',
    tpl: [
        '<img src="/foo.jpg" style="float:left">',
        '<p>This is some text</p>',
    ],
    data: {},

    onAfterRender: function() {
        this.callParent();

        this.imgEl = this.getEl().down('img');
        this.textEl = this.getEl().down('p');
    },

    setImage: function(newSrc) {
        this.imgEl.set({ src: newSrc });
    },

    setText: function(newText) {
        this.textEl.setHTML(newText);
    }
});

Since that is a fairly common task, we shouldn’t do it manually every time, right? That is exactly what renderSelectors are for, and let’s use them:

Ext.define('MyComponent', {
    extend: 'Ext.Component',

    cls: 'mycomponent',
    tpl: [
        '<img src="/foo.jpg" style="float:left">',
        '<p>This is some text</p>',
    ],
    data: {},

    renderSelectors: {
        imgEl: 'img',
        textEl: 'p'
    },

    setImage: function(newSrc) {
        this.imgEl.set({ src: newSrc });
    },

    setText: function(newText) {
        this.textEl.setHTML(newText);
    }
});

No manual work anymore, it’s all nice and declarative. But wait, there’s more! Since renderSelectors are just DOM queries that run down the DOM tree from the Component’s main element, you can actually use them to look up the elements in your Component’s children!

Wait, what? Why would I want to do anything like that?

One fairly common example is showing an Ext JS window with some external content in it, like receipts generated by 3rd party system, document print previews, etc. The way to do that is to use an IFrame, but how do we make it look neat and ExtJS-y? Here’s how:

Ext.define('MyIframeWindow', {
    extend: 'Ext.window.Window',

    // Window is a floating component, it cannot size itself
    // So we will size it to a percentage of the Viewport dimensions
    width: Ext.Element.getViewportWidth() * 0.8,
    height: Ext.Element.getViewportHeight() * 0.8,

    // The easiest way to make IFrame fit into the window
    // is to make it a Component and use fit layout on it
    layout: 'fit',
    items: [{
        xtype: 'box',
        autoEl: {
            tag: 'iframe'
        }
    }],

    // IFrame element is in the child Component but who cares?
    renderSelectors: {
        iframeEl: 'iframe'
    },

    loadDocument: function(uri) {
        var el = this.iframeEl;

        // Windows are lazy and render themselves to the DOM
        // only when they're shown the first time, so if you
        // call loadDocument before first show(), the iframeEl
        // will be missing. This is to avoid the kaboom.
        if (el) {
            el.set({ src: uri });
        }
    }
});

Easy, right? Now you have an IFrame that neatly fits the window, can be resized and dragged around, etc. Just make sure you show() it before calling loadDocument():

var myWindow = new MyIframeWindow();

myWindow.show();
myWindow.loadDocument('foo.html');

Neat, huh? Happy coding! :)

Using synchronous bidirectional communication with Controllers

August 8, 2013

In a big application, Controllers often need to communicate between each other in both directions; i.e. requesting some information and acting upon receiving it. Direct method calls are usually used for this:

Ext.define('My.controller.Foo', {
    extend: 'Ext.app.Controller',
    
    onPanelButtonClick: function(button) {
        // Before dispatching a message to Bar, we need to ask Baz
        // if it has the option we need
        var option = this.getController('Baz').getOption(button.text);
        
        // Now we can choose which Bar method to call
        if (option) {
            this.getController('Bar').openWindow(button.text, option);
        }
        else {
            this.getController('Bar').openDialog(button.text, !option);
        }
    },
    
    ...
});

With direct method call, this is very simple: just return a value from the called method. But how do we return a value from another Controller when we use events for information passing? One way would be to fire an event, have other Controller fire a return event with the information we requested, and listen to that return event. However, there is a simpler and faster approach that does not require much effort on our part. We can use the fact that JavaScript passes Arrays and Objects as a reference in function calls:

Ext.define('My.controller.Foo', {
    extend: 'Ext.app.Controller',
    
    onPanelButtonClick: function(button) {
        // Before dispatching a message to Bar, we are asking Baz
        // if it has the option we need by firing an event and
        // getting the return value synchronously
        var result = {};
        
        this.fireEvent('getBazOption', result);
        
        if (result.option) {
            ...
        }
    },
    
    ...
});

Ext.define('My.controller.Baz', {
    extend: 'Ext.app.Controller',
    
    init: function() {
        this.listen({
            controller: {
                '*': {
                    getBazOption: 'getBazOption'
                }
            }
        });
    },
    
    getBazOption(result) {
        result.option = this.option;
    },
    
    ...
});

This approach can be used along with class inheritance, with superclasses setting default values and subclasses overriding them if needed; it can also be used to collect information from more than one Controller in one go. Since event handlers are executed synchronously, we can avoid callbacks and have very simple and easy to follow application logic. Synchronous function calls are also much easier to debug than asynchronous callbacks.

Controller events in Ext JS 4.2

July 19, 2013

This is a kind of follow up to the last year’s “Improving Ext JS MVC Implementation” post. Since then I have joined Sencha and have been working on Ext JS team for several months. Bringing MVC improvements into Ext JS core made all kinds of sense, so here goes.

In any application that has more than one Controller, there is a question: how do these Controllers communicate? Before Ext JS 4.2, the answer was, by calling each other’s methods directly:

Ext.define('My.controller.Foo', {
    extend: 'Ext.app.Controller',

    onPanelButtonClick: function(button) {
        // Suppose that we need to open a window that is governed
        // by another Controller. To do this, we need to call that
        // Controller`s method:
        try {
            this.getController('Bar').openWindow(button.text);
        }
        catch (ex) {
            Ext.Error.raise('Application error: ' +
                            'Controller Bar is unavailable');
        }
    },

    ...
});

Ext.define('My.controller.Bar', {
    extend: 'Ext.app.Controller',

    openWindow: function(text) {
        ...
    },

    ...
});

This approach is problematic and error prone: not only we have to do lots of unnecessary stuff (try/catch, error raising, saving stack trace, etc) but, what is more important, this error checking happens at run time, when misspelled Controller or method name will lead to our application throwing an exception that the user cannot do a thing about. Of course we can relieve this problem by unit testing our Controllers; however if one Controller calls methods in several other Controllers we would have to instantiate them all, which then in turn will bring their dependencies. This leads to unmanageable and untestable mess very quickly.

Starting with Ext JS 4.2, there is a solution to this problem: Controller events. The code above can be refactored this way:

Ext.define('My.controller.Foo', {
    extend: 'Ext.app.Controller',

    onPanelButtonClick: function(button) {
        // Instead of calling Bar method directly, we fire an event.
        // Controller Bar listens to that event and acts accordingly.
        this.fireEvent('barOpenWindow', button.text);
    },

    ...
});

Ext.define('My.controller.Bar', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.listen({
            // We are using Controller event domain here
            controller: {
                // This selector matches any originating Controller
                '*': {      
                    barOpenWindow: 'openWindow'
                }
            }
        });
    },

    openWindow: function(text) {
        ...
    },

    ...
});

This way we solve the problems discussed above, while achieving very clean logic separation between Controllers. When Controller Foo wants Controller Bar to open a window, it sends a signal which may or may not be honored, depending on Bar’s availability and other factors Foo does not know or care about. This approach also makes Controller unit testing a lot easier: in fact the only thing left to test besides refs and component selectors are controller event handlers; all other logic can be encapsulated in Controller and tested separately. See more on this in Unit testing MVC Controllers guide.

You can also find more information on Event domains in Ext.app.Controller.listen() method documentation.

Abstract Controllers

July 11, 2013

In a typical real world application there might be quite a number of Views and Controllers similar to each other but doing different things, e.g., operating on different data sets. It is inefficient to repeat the same code over and over in different classes, and some code sharing technique is required. For Views, we can easily abstract most of the code in a base View class and add the required configuration in subclasses:

Ext.define('MyApp.view.AbstractPanel', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.mypanel',
    
    bbar: [{
        xtype: 'button',
        itemId: 'okButton',
        text: 'OK'
    }, {
        xtype: 'button',
        itemId: 'cancelButton',
        text: 'Cancel'
    }],
    
    ...
});

Ext.define('MyApp.view.FooPanel', {
    extend: 'MyApp.view.AbstractPanel',
    alias: 'widget.foopanel',
    
    // We are overriding buttons in this view
    bbar: [{
        xtype: 'button',
        itemId: 'yesButton',
        text: 'Yes'
    }, {
        xtype: 'button',
        itemId: 'noButton',
        text: 'No'
    }],
    
    ...
});

Ext.define('MyApp.view.BarPanel', {
    extend: 'MyApp.view.AbstractPanel',
    alias: 'widget.barpanel',
    
    // This view has the same buttons as base class,
    // and also an informational side bar, so we need to
    // accommodate for it
    dockedItems: [{
        xtype: 'panel',
        itemId: 'sidebar'
    }],
    
    ...
});

What about Controllers then? In fact, nothing prevents us from taking the same approach, albeit we will have to do it in slightly less automated way:

Ext.define('MyApp.controller.AbstractController', {
    extend: 'Ext.app.Controller',
    
    // Matches any MyApp.view.AbstractPanel descendant by xtype
    viewSelector: 'mypanel',
    okSelector: 'button#okButton',
    cancelSelector: 'button#cancelButton',
    
    constructor: function(config) {
        Ext.apply(this, config);
        
        var tplRefs = [{
                ref: 'view',
                selector: this.viewSelector
            }, {
                ref: 'okButton',
                selector: this.viewSelector + ' > ' + this.okSelector
            }, {
                ref: 'cancelButton',
                selector: this.viewSelector + ' > ' + this.cancelSelector
            }];
        
        this.refs = this.refs ? Ext.merge(tplRefs, this.refs) : tplRefs;
        
        this.callParent(arguments);
    },
    
    ...
});

Ext.define('MyApp.controller.Foo', {
    extend: 'MyApp.controller.AbstractController',
    
    // Now we only have to provide selectors, and voila -
    // the refs will be created automagically
    viewSelector: 'foopanel',
    okSelector: 'button#yesButton',
    cancelSelector: 'button#noButton',
    
    ...
});

Ext.define('MyApp.controller.Bar', {
    extend: 'MyApp.controller.AbstractController',
    
    // Buttons are the same so we only provide view selector
    viewSelector: 'barpanel',
    
    // We can add some refs that are unique to this View -
    // in our case, it is the sidebar. The refs should be as
    // precise as it is feasible, to avoid collisions with
    // other Views.
    refs: [
        { ref: 'sidebar', selector: 'barpanel > panel#sidebar' }
    ],
    
    ...
});

In the above example, we have abstracted a lot of duplicating functionality in our base Controller, greatly simplifying children Controller classes. A similar approach can be used with `control` component selectors and `listen` event domain selectors, respectively.

Ext JS MVC: Controlling multiple View instances

July 2, 2013

Oftentimes we need to control multiple instances of the same View class, and in majority of such cases we can share the same Controller instance between all of these Views. The key here is to make use of `xtype` and properly configured component selectors.

Suppose that we have a form; in that form we need to invalidate secondary field when primary field value is invalid, and vice versa:

Ext.define('MyApp.view.MyForm', {
    extend: 'Ext.form.Panel',
    alias: 'widget.myform',

    defaultType: 'textfield',

    items: [{
        fieldLabel: 'Primary',
        name: 'primary'
    }, {
        fieldLabel: 'Secondary',
        name: 'secondary'
    }]
});

Instead of hardcoding the logic in the Form class itself, we can abstract it into a Controller:

Ext.define('MyApp.controller.MyForm', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            /*
             * Comma in a ComponentQuery selector means that component
             * matches if either of subselectors is valid; i.e. it is
             * effectively an || operator. Only fields with names
             * 'primary' or 'secondary' that are children of a form
             * with xtype 'myform' will be watched for events,
             * and so we do not have to check the field validity in the
             * event handler even if we add more fields to our form.
             */
            'myform textfield[name=primary], myform textfield[name=secondary]': {
                validitychange: 'onValidityChange'
            }
        });
    },

    onValidityChange: function(field, isValid) {
        var map = {
                primary: 'secondary',
                secondary: 'primary'
            },
            selector, linkedField;

        /*
         * First we need to find the field that is linked
         * to the one on which validitychange event was fired.
         * We will do that by quering field`s parent form
         * for a field that has specified name.
         */
        selector    = 'textfield[name=' + map[field.name] + ']';
        linkedField = field.up('myform').down(selector);

        if (isValid) {
            linkedField.clearInvalid();
        }
        else {
            linkedField.markInvalid('Field ' + field.fieldLabel +
                                    ' should be valid');
        }
    }
});

Note that we are looking up field’s form by its `xtype`; this way it is more future-proof. For example, the fields may be moved into a different FieldSets; if we used `field.up()` it would not return the form object anymore and the construct would break. Looking up the form itself hedges us against the changes and ensures that we find exactly the field we need because the form has distinct `xtype`.

The only caveat (which is actually a feature) to watch for with `xtype` is that by default it matches a component of a class that has this `xtype`, and components of classes that extend it. E.g., if we were to make our MyForm a base class the Controller code above would also work:

Ext.define('MyApp.view.MyChildForm', {
    extend: 'MyApp.view.MyForm',
    alias: 'widget.mychildform',

    ...
});

In case you want your selectors to match only components of the classes that actually have `xtype` needed, use `(true)` suffix to make the search shallow:

'myform(true) textfield[name=primary], myform(true) textfield[name=secondary]'

See Ext.ComponentQuery documentation for more information on component selectors.

DOM event handling in Ext JS MVC

June 19, 2013

Sometimes you need to work with DOM elements in your custom Components, or stock Ext JS Components do not fire events in certain conditions that you would like to react to in your Controllers. What do you do in such case? You can refire DOM event as Component event.

Suppose you want Panel’s `mouseover` events to be controlled MVC style. This is how it could look like:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    listeners: {
        mouseover: {
            element: 'el',
            fn: function(event) {
                this.fireEvent('myPanelMouseOver', this);
            }
        }
    }
});

… except that it would not work, because `this` scope in event handler will be set to the `Ext.dom.Element` instance of our Panel’s main element, instead of the Panel itself! To make it work, we should find the Component to which the Element belongs, and fire event from that Component:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    listeners: {
        mouseover: {
            element: 'el',
            fn: 'onElMouseover'
        }
    },

    onElMouseover: function(event) {
        // In this case, we can easily find the Component,
        // because el will have the same id as the Panel.
        // Performance will not be a problem either
        // since Ext.getCmp() lookups are very fast.
        var cmp = Ext.getCmp(this.id);

        // Now we can fire a Component event that Controllers
        // will see and can react to.
        if (cmp) {
            cmp.fireEvent('myPanelMouseover', cmp, event);
        }
        // The code between <debug></debug> tags will be removed
        // from production code by Sencha Cmd compiler, so this
        // is "dev eyes only"
        //<debug>
        else {
            Ext.Error.raise('mouseover handler has Element ' +
                            'with id ' + this.id + ', but no ' +
                            'matching Component found!');
        }
        //</debug>
    }
});

This approach will be beneficial in simpler cases, where relationship between DOM Element and Component is straightforward and it is easy to find Component by its Element. However there are times when it is not so; in such case we will have to capture the scope (Component) and reuse it:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    listeners: {
        afterrender: function() {
            // Monitored listener does not need to be explicitly destroyed,
            // Component destructor takes care of it
            this.mon(
                // This time we want only Panel body to report mouseover
                this.body, 'mouseover',

                // Event handler
                function(event) {
                    this.fireEvent('myPanelMouseOver', this, event);
                },

                // Finally, set the scope for event handler
                this
            );
        }
    }
});

Another good example is Action column in a Grid: it allows adding action icons, like ‘edit’ or ‘delete’ to perform corresponding operations on a row; however it is not controllable by default. In such case we can use `handler` config option and refire the events we need:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.mypanel',

    columns: [{
        xtype: 'actioncolumn',
        items: [{
            icon: 'edit.png',
            handler: function(grid, rowIndex) {
                var record = grid.getStore().getAt(rowIndex);
                grid.fireEvent('editAction', grid, record);
            }
        }, {
            icon: 'delete.png',
            handler: function(grid, rowIndex) {
                var record = grid.getStore().getAt(rowIndex);
                grid.fireEvent('deleteAction', grid, record);
            }
        }]
    }]
});

Ext.define('MyApp.controller.MyPanel', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            mypanel: {
                editAction: 'onEditAction',
                deleteAction: 'onDeleteAction'
            }
        });
    },

    onEditAction: function(grid, record) {
        ...
    },

    onDeleteAction: function(grid, record) {
        ...
    }
});

Of course the handlers can be abstracted further, they are repeated here for the sake of clarity.

Improving Ext JS MVC implementation

August 16, 2012

UPDATE: The ideas and code described in this article have been incorporated into the Ext JS MVC subsystem; the solution has been released as part of Ext JS 4.2. The Ext.ux.Application is still relevant for Ext JS 4.1 applications though.

 

 

Anybody who has been working with Ext JS 4.x probably tried to use what Sencha guys call MVC architecture. It looks good on paper, even better when you first try to get your feet wet with it… But then, when you have already decided that it’s the Right Thing and wrote your first production Controller, you realize that something’s wrong. When you experimented with it, you probably didn’t bother writing any tests, right? It’s just some toy code, an example, why bother? But production code should be tested thoroughly, we’re serious about our code quality… That’s when the bummer moment comes. You can’t unit-test Controller classes!

You plunge in Ext.app.Controller code and realize with dismay that it can’t be instantiated without satisfying its Ext.app.Application bindings. And Application will haul along its dependency tree, all Controllers and Views and Stores and Models, effectively loading up the whole application. That’s wrong, that’s very wrong. But what would you do? The decision to go with Ext JS can’t be changed, commitments are made and deadlines are set. You probably poke around for a bit and then just give in and skip Controller testing altogether, hoping that Sencha will come up with solution Real Soon Now.

Well, it’s been more than a year since Ext JS 4.0 came out but solution isn’t there. I don’t know if Sencha guys see this problem as unimportant, or just don’t have resources to work on it, but I just couldn’t wait anymore. The project I’m responsible for at work is too big to go untested, and test suite should be available right from the start, or else. Naturally I tried to see who was there first. To my dismay, I couldn’t find any ready to use solution. None! There are at least two projects claiming improvements to Ext JS MVC architecture, namely GluJS and DeftJS. Both look interesting, but GluJS code looks downright unreadable, and DeftJS is in 0.x versions, suggesting that code maturity may be of a problem here. Besides, both projects aim at significantly changing existing architecture, while I’m not convinced that it needs to be changed. Improved, yes. Changed? Not necessarily.

So I actually dug deep into MVC code and found out that it wasn’t that bad. In fact it wasn’t bad at all, it looked more like rushed to release but didn’t have any irrecoverable flaws. A bit of refactoring, some guts swapped between Controller and Application, and here we go: Controller is no longer dependent on Application, it can live on its own. There’s still not much sense in trying to use it standalone, but for testing purposes it’s more than enough. Refs? Working. Getters? Here. Control method? Reporting for duty, sir.

Besides testing there was one other thing that bugged me with Ext JS MVC, and that was the missing mechanism for Controller coupling. In simple words, a Controller can control() events from Views, but it can’t receive events fired in other Controllers! But wait, how do you pass events between parts of your application then? Um, well, you can always resort to callback chains… But again, this is wrong. You can have some coupling between your Controllers, or you can have a centralized event bus, but you got to have something, or your application becomes a bowl of spaghetti real quick.

Now let’s take the usual example of a Controller:

Ext.define('My.controller.Foo', {
    extend: 'Ext.app.Controller',

    views: [ 'Foo', ... ],

    init: function() {
        var me = this;

        me.control({
            'foo_xtype': {
                event: me.onFooEvent
            }
        });
    },

    onFooEvent: function() {
        var me = this;

        // Suppose event foo means we need to open another panel.
        // Sure, but that panel is under foreign Controller.
        // What do we do here? Usually, this:
        var panel = new My.view.Bar({ ... });

        // Or, in a sloth-defying case (we feel real smart today)
        me.getController('Bar').openPanelBar();

        // Well, if we are *really truly* against sloppy code
        // we could even do this:
        me.getController('Bar').fireEvent('letsOpenPanelBar');
    },

    ...
});

But neither of these is any good. Wouldn’t it be nice to just have Controllers listen to each other? Like this:

Ext.define('My.controller.Foo', {
    ...

    onFooEvent: function() {
        var me = this;

        me.fireEvent('heyBarLetsOpenAnotherPanel');
    }
});

Ext.define('My.controller.Bar', {
    extend: 'Ext.app.Controller',

    views: [ 'Bar', ... ],

    init: function() {
        var me = this;

        me.control({
            'controller#Foo': {
                heyBarLetsOpenAnotherPanel: me.openPanelBar
            },

            // Or maybe even listen to *all* Controllers?
            'controller': {
                globalEvent: me.achieveWorldDomination
            }
        });
    },

    openPanelBar: function() {
        var me = this,
            panel;

        panel = me.getView('Bar');

        if ( !panel ) {
            panel = new My.view.Bar({ ... });
        }
    }
});

That’s much better. No more tight coupling, no need to stub out Controllers you work with just to test your own, and we’re returning to the old faithful event model. Readability improves significantly, too; no more poking in other Controllers just to see what the heck was that method doing?

Oh, and now that we can listen to other Controllers, why not make Application the top level Controller? It has the traits through inheritance, so we may just as well use them.

Sounds nice, so let’s see how well it will fare in the Real World. The code’s in Github repo as usual. If you have any trouble with it, let me know.

How to insert Unix time as PostgreSQL timestamp

July 22, 2012

Quick googling turned up lots of insane (or just outdated) type conversion advice, while the answer is very simple: use standard function to_timestamp(), like this:

INSERT INTO foo VALUES ( to_timestamp(1342986162) )

Reverse operation is equally easy:

SELECT EXTRACT( EPOCH FROM now() )

Hopefully next time when I forget how to do that, I’ll remember to look for it here.

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org