My take on sequencing Cairngorm events

I`m working with Cairngorm in nearly all of my projects and I like it because it somehow forces me to work structured. But there are some things that I don`t like or don`t get. For example sequencing of Commands in Cairngorm is done by subclassing SequenceEvent and putting all the logic for which event comes next directly into the Command itself. By doing so the command looses a lot of reusability. What if I want to use the same command outside of a sequence chain?
I would have to code two commands – one for the sequence and one that lives on it`s own. The other thing is that when i fire a cairngorm event from my view i have no way to know when the corresponding command is done. Only way would be through some sort of state variables in my model that keep track of the process. I believe that this approach is quite fragile because sometimes you might have to commands of the same instance running and you`ll never know which of same changed your model. And a model with hundreds of state variables just doesn`t look clean to me.
I lived with all these problems for some time know and never really thought about forking the original cairngorm framework because I like to work with the original framework to be safe for future changes.
But now I just can`t live with these problems anymore – so here is my first try into improving this stuff.

My idea is to have a singleton that keeps track of all your events and the coresponding commands. This class will be notified by the commands when they are done.
[ftf w=”700″ h=”300″]
package de.benz.cairngorm.control
{
import com.adobe.cairngorm.CairngormError;
import com.adobe.cairngorm.CairngormMessageCodes;
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;

import flash.events.EventDispatcher;
import flash.utils.Dictionary;

public class ExecutingCommands extends EventDispatcher
{

private static var instance:ExecutingCommands;
private var commands:Dictionary = new Dictionary();

public static function getInstance():ExecutingCommands{
if(instance == null){
instance = new ExecutingCommands();
}
return instance;
}

public function add(command:ICommand, evt:CairngormEvent):void{
commands[command] = evt;
}

public function remove(command:ICommand):void{
if( commands[ command ] === null)
throw new CairngormError( CairngormMessageCodes.COMMAND_NOT_REGISTERED, command);
var evt:CairngormEvent = commands[command];
dispatchEvent(new CommandExecutedEvent(CommandExecutedEvent.CommandExecuted,command,evt));
commands[ command ] = null;
delete commands[ command ];
}

public function ExecutingCommands()
{
}
}
}
[/ftf]

So what I`m doing here is storing commands using the coresponding event as a key in a dictionary. Now I needed to find a good place to call the add function of my ExecutingCommands class.
For this I extended the Cairngorm FrontController and overrode the executeCommand method.
[ftf w=”700″ h=”300″]
package de.benz.cairngorm.control
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.control.FrontController;

public class FrontController extends com.adobe.cairngorm.control.FrontController
{

public function FrontController(){
super();
}

override protected function executeCommand( event : CairngormEvent ) : void{
var commandToInitialise : Class = getCommand( event.type );
var commandToExecute : ICommand = new commandToInitialise();
ExecutingCommands.getInstance().add(commandToExecute,event);
commandToExecute.execute( event );
}

}
}
[/ftf]

Now the only thing that is left to do is removing the commands from the ExecutingCommands class when they are done. Until I find a better solution this has to be done by the command itself because it`s the only one who knows when it has finished. So I just call ExecutingCommands.getInstance().remove(this); in my result and fault functions of my commands.

So what I have now is a singleton that keeps track of all my running commands and notifies me whenever a certain command is done with a CommandExecutedEvent which looks like this:
[ftf w=”700″ h=”300″]
package de.benz.cairngorm.control
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;

import flash.events.Event;

public class CommandExecutedEvent extends Event
{

public static const CommandExecuted:String = “commandExecutedEvent”;

public var event:CairngormEvent;
public var command:ICommand;

public function CommandExecutedEvent(type:String, command:ICommand, event:CairngormEvent)
{
this.command = command;
this.event = event;
super(type);
}

override public function clone():Event {
return new CommandExecutedEvent (type,command,event);
}

}
}
[/ftf]

With this stuff in place it`s now easy to implement a SequenceEvent class with Commands which don`t hold the logic for the sequence itself. Here is a basic implementation:

[ftf w=”700″ h=”300″]
package de.benz.cairngorm.events
{
import com.adobe.cairngorm.control.CairngormEvent;

import de.benz.cairngorm.control.CommandExecutedEvent;
import de.benz.cairngorm.control.ExecutingCommands;

import mx.collections.ArrayCollection;

public class SequenceEvent
{

private var events:ArrayCollection = new ArrayCollection();
private var pendingEvent:CairngormEvent;

public function SequenceEvent()
{
ExecutingCommands.getInstance().addEventListener(CommandExecutedEvent.CommandExecuted, onCommandExecuted);
}

private function onCommandExecuted(evt:CommandExecutedEvent):void{
if(evt.event == pendingEvent){
events.removeItemAt(0);
if(events.length > 0){
pendingEvent = CairngormEvent(events.getItemAt(0));
dispatch();
}else{
trace(“Finished Sequence”);
}
}

}

public function addEvent(evt:CairngormEvent):void{
events.addItem(evt);
}

public function dispatch():void{
pendingEvent = CairngormEvent(events.getItemAt(0));
pendingEvent.dispatch();
}

}
}
[/ftf]

Now i can add normal Cairngorm events to this class and execute them in a sequence like this:
[ftf w=”700″ h=”300″]
var eventSequence:SequenceEvent = new SequenceEvent();
eventSequence.addEvent(new GetTalk(model.currentPageId));
eventSequence.addEvent(new GetTags(model.currentPageId));
eventSequence.addEvent(new GetComments(model.currentPageId));
eventSequence.addEvent(new GetRelatedTalks(model.currentPageId));
eventSequence.addEvent(new GetFavorites());
eventSequence.dispatch();
[/ftf]

This is all just a quick idea and not very thought out yet. One could extend all this with functionality for example how the sequence should handle faults in a command… I justed wanted to get your feedback if this is a way to go or if this is super stupid!? How are you doing it?

Tweet about this on TwitterShare on FacebookShare on LinkedInShare on Google+Pin on Pinterest

7 thoughts on “My take on sequencing Cairngorm events

  1. thanks for the link! Haven`t seen that one.
    But it seems that in that approach your chained Commands have to extend ModuleSequenceCommand which means you again have to decide at Command level if it`s chained or not. That`s something i try to avoid. But i`ll certainly have a look.

  2. Usually my commands all extend sequenceCommand (not forced to be a moduleSequenceCommand) and all my events extend chainEvent. Then I can chain them or not. The only thing I have to use is to assign the nextEvent (inside the sequenceCommand) to be the chainEvent.nextChainedEvent each time.

    Like this I can have the same event being chained in a sequence of 300 commands or totally alone. This could eventually be implemented within Cairngorm.

  3. @João

    “Like this I can have the same event being chained in a sequence of 300 commands or totally alone.”

    cool. I thought someone might have gone down this road and that Ben wasn’t out on his own.

    got a write up on this at all?

Leave a Reply

Your email address will not be published. Required fields are marked *