Archive for the ‘Cairngorm’ Category

Introducing Kashi

I’ve been working on what I’m gonna call a radical branch/sect of Cairngorm. It’s currently code-named Kashi because that is the brand of cookie that I was eating at the time. The idea is to keep the basic Cairngorm pattern, but rethink the pieces.

    The Front Router

    The most significant change so far is a FrontRouter mxml class. This let’s you map events to routes. Routes consists of one or more commands.

    Not only does this fix the messy state of Cairngorm’s SequenceCommand, but it adds the ability to set properties on commands before they execute. This makes the commands a lot more portable. You can use the same command multiple times with different models to push the results into.

    Added bonus: multiple events can trigger a single command. The view layer doesn’t need to know about this. 😛

    Name-wired Dependency Injection Defaults

    Okay. I need a more catchy name for this feature. Here’s the deal: injecting an instance of a model into views and commands is the best practice… better than locking the code into a singleton (Cairngorm) or even singleton factory (our current system). But it’s annoying to specify for “fooView” that you want “fooModel”. So, Kashi figures it out for you. If you have an instance named “fooView” it will automatically look for a model instance named “fooModel”. Also, if a route is named “fooStartupRoute” it’s commands will default to use “fooModel”. These are defaults, so you can always set them manually.

    I’ll post some concrete examples with code shortly.

    Advertisements

    PureMVC-like Notification Mediator in Cairngorm

    Caveat: My experience with PureMVC is limited to reading the excellent docs and a few blogs. Also, this post is total bait to get Ben Rimbey and Benjamin Corliss to blog a bit. 😛 

    So, PureMVC has a “Mediator” that sits between your UI and the View singleton. One of it’s jobs is too respond to PureMVC Notifications. Here’s what that looks like from the docs:

    override public function handleNotification( note : INotification ) : void {
     switch ( note.getName() ) {
      case ApplicationFacade.SEARCH_FAILED:
        controlBar.status = AppControlBar.STATUS_FAILED;
        controlBar.searchText.setFocus();
        break;  
    
     case ApplicationFacade.SEARCH_SUCCESS:
        controlBar.status = AppControlBar.STATUS_SUCCESS;
        break;  
    
      }
     }
    }

    So when you get a Notification with the name ApplicationFacade.SEARCH_FAILED or ApplicationFacade.SEARCH_SUCCESS it’s handled in the switch. I have a couple philosophical problems with this approach because the View should be a representation of the Model and the Model should maintain the state of the application. Adding the ability for the View or Control to talk directly to the View seems dangerous.

    I’m not saying Cairngorm is teh awesomes, but here’s an easy way to achieve the same affect and maintain the state in the Model. I’ll use a for-reals life example…

    In the mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <behind:AutofixBehind
        xmlns:mx="http://www.adobe.com/2006/mxml"
        xmlns:behind="views.behind.*"
        xmlns:ui="views.components.*"
        backgroundColor="#ffffff"
        currentState="{ model.complete ? 'complete' : 'scanning' }"
        > 
    
        <!-- State changes are handled in the code behind -->
        <behind:states>
            <mx:State name="scanning" enterState="enterScanningState( event )" />
            <mx:State name="complete" enterState="enterCompleteState( event )" />
        </behind:states>
    ...

    Okay. So this particular example binds the state to model.complete, but you could just as easily bind it to model.currentScanState or whatever. Then the code-behind AutofixBehind.as has the enterScanningState and enterCompleteState handlers in it. Meh?

    Cairngorm Observable Front Controller (part 2)

    (See part 1 here.)

    So I was thinking about how we can better test our applications using automation. I know there are complex and great tools out there to do this– specifically ones that use the mx.automation package, but I wanted to play around and make my own.

    Recording Cairngorm Events

    The first thing we need to do is import a bunch of stuff. I’ll get into what they do later…

    import flash.utils.getDefinitionByName; 
    import flash.utils.getQualifiedClassName; 
    import mx.rpc.xml.SimpleXMLDecoder; 
    import mx.rpc.xml.SimpleXMLEncoder; 
    import flash.xml.XMLDocument; 
    import flash.xml.XMLNode; 
    

    Then we hook onto that event from last time

    eventDispatcher.addEventListener( ExecuteCommandEvent.EXECUTED, eventFired );

    And we’ll want a variable to hold the session. I have to use the old-skool XMLDocument because I’m using the SimpleXMLEncoder to encode the Cairngorm Event into XML.

    private var _testScriptXML = new XMLDocument( "<script></script>" );

    Now let’s encode it:

    private function eventFired( event:ExecuteCommandEvent ):void {
        var simpleXMLEncoder:SimpleXMLEncoder = new SimpleXMLEncoder( _testScriptXML );
        var xmlNode:XMLNode = simpleXMLEncoder.encodeValue( event.cairngormEvent, 
    new QName("cairngormEvent"), _testScriptXML.firstChild );
                 
        xmlNode.attributes["class"] = getQualifiedClassName( event.cairngormEvent );
        xmlNode.attributes["type"] = event.cairngormEvent.type;
        xmlNode.attributes["bubbles"] = event.cairngormEvent.bubbles.toString();
        xmlNode.attributes["cancelable"] = event.cairngormEvent.cancelable.toString();        
    }

    The encoding is straight-forward, although a bit painful because we have to deal with XMLDocument instead of XML. Sigh…. Also at this point you could output the recording to a text area.

    Playback

    Okay. Recording is nice and all, but pretty useless. Let’s roll it back out now.

    private var _testCurrentNode:XMLNode; 
    private var _testPlaybackInterval:Number;

    The _testCurrentNode will be the current XML Node representation of the event that we are playing. _testPlaybackInterval will be used for setInterval.

    So now we add a function that takes a string version of our script, turns it into XML (old-skool style) and starts the playback.

    public function startTestHarnessForPlayback( scriptToPlay:String ):void {
        _testScriptXML = new XMLDocument( scriptToPlay ); 
        _testCurrentNode = _testScriptXML.firstChild.firstChild; 
        _testPlaybackInterval = setInterval( fireNextEvent, 1000 ); 
    }

    Firing the event gets a little more complicated. After decoding it we have to create the Class from a string using getDefinitionByName(). This method let’s us give it a string name of a class (like “foo.animal.Cat”) and it will create that Class (with a capital C). We then have to create an instance from the Class and populate it.

    private function fireNextEvent():void {
        trace("~" + _testCurrentNode.toString() ); 
    
        // decode the XML into an Object             
        var simpleXMLDecoder:SimpleXMLDecoder = new SimpleXMLDecoder();
        var decodedObj:Object = simpleXMLDecoder.decodeXML( _testCurrentNode );
    
        // create a new Cairngorm Event from the object 
        var eventClass:Class = getDefinitionByName( _testCurrentNode.attributes["class"] ) as Class;
        var event:CairngormEvent = new eventClass( _testCurrentNode.attributes["type"], _testCurrentNode.attributes["bubbles"] == "true", _testCurrentNode.attributes["cancelable"] == "true" );
         
        // populate the Cairngorm Event 
        for ( var key:String in decodedObj ) {
            if ( _testCurrentNode.attributes[ key ] == undefined ) { 
                event[ key ] =decodedObj[ key ]; 
            } 
        } 
    
        // dispatch it         
        event.dispatch(); 
    
        // move on, unless we're done 
        _testCurrentNode = _testCurrentNode.nextSibling; 
        if ( !_testCurrentNode ) { clearInterval( _testPlaybackInterval ); } 
    }

    Right on. So that will let you record and playback a Cairngorm application. I think if I was to take it a step further, I’d add pauses into the scripts and even event hooks that it waits for. Oh well. Maybe later.

    Cairngorm Observable Front Controller (part 1)

    One of the reasons I like Cairngorm is because it is small. Hence, the micro in micro-architecture.  (There are things I don’t like about Cairngorm too, but I’ll save that for a different post). Anyway, it’s smallness makes it easy to understand and build on top of.

    The Unobservable Front Controller

    As-is the Front Controller simply adds listeners to the Cairngorm Event Dispatcher. These listeners fire the executeCommand function and that creates a new command and executes it. Under normal circumstances, that’s awesome and it’s probably good that it is contained the way it is. But what if you want to log what commands are fired? This might be a good way to do analytics reporting and prevent tight coupling with the app.

    One way you could do this is by looping through the commands dictionary. But you’d have to know when all of the commands were done being added or removed.

    Another strategy is to extend the Front Controller…

    The Observable Front Controller

    The ObservableFrontController code looks like this:

      1 package testLib { 
      2  
      3     import com.adobe.cairngorm.control.CairngormEvent;
      4     import com.adobe.cairngorm.control.FrontController;
      5     import flash.events.EventDispatcher; 
      6      
      7     public class ObservableFrontController extends FrontController
      8     { 
      9         public var eventDispatcher:EventDispatcher = new EventDispatcher();
     10          
     11         public function ObservableFrontController() {
     12             super(); 
     13         } 
     14          
     15  
     16         override protected function executeCommand( event:CairngormEvent ):void {
     17             // dispatch the event wrapped in an ExecuteCommandEvent
     18             var executeEvent:ExecuteCommandEvent = new ExecuteCommandEvent();
     19             executeEvent.cairngormEvent = event;
     20             eventDispatcher.dispatchEvent( executeEvent ); 
     21              
     22             // move on 
     23             super.executeCommand( event );
     24         } 
     25          
     26     } 
     27 }

    All it does is override executeCommand and repackage the event into a ExecuteCommandEvent. Here’s the ExecuteCommandEvent:

      1 package testLib 
      2 { 
      3     import com.adobe.cairngorm.control.CairngormEvent;
      4     import flash.events.Event; 
      5  
      6     public class ExecuteCommandEvent extends Event
      7     { 
      8          
      9         public static const EXECUTED:String = "executeCommandEvent";
     10          
     11         public var cairngormEvent:CairngormEvent;
     12          
     13         public function ExecuteCommandEvent() {
     14             super(EXECUTED, false, false);
     15         } 
     16          
     17     } 
     18 }

    You with me? Not bad… yet. This sets it all up so I can add and a listener that will watch ANY command that the front controller fires. Next post I’ll talk about how you can make a simple automator tool that