Before recommending that I'd like to think through the problem a bit more. (Sorry for not attaching code yet, I will if it makes it easier, but hopefully talking through it will be enough first)
If anyone would like to throw out example scenarios here that use subclasses and arguments for it, please do.
Here is a simple usage example for a case that, in the current version of the utility, is
difficultannoying to implement:
I want to pair StateMachine w/ AsyncCommand that will attempt to login to a service. The login form will be changed when the login request is outstanding, and either change back if failed or faded and the main view is to be displayed if login was successful.
This uses my practice of making the Command an IResponder - which I know has been discussed to be bad practice in PureMVC. However, I think the usefulness of doing this (especially with an AsyncMacroCommand) outweighs the "bad practice" argument. I'm willing to be swayed, but this has been my practice since using PureMVC which is why I'm still going with it. In this example, we're not using an AsyncMacroCommand, but I'll make the DoLoginCommand extend AsyncCommand anyway, for argument's sake.
So, my FSM:
LOGIN -> TRY_LOGIN
TRY_LOGIN -> LOGIN (transition to occur on a failed login)
TRY_LOGIN -> MAIN (transition to occur on a successful login)
If we dispatch an event from the Login form that will get picked up by a mediator, which will send a notification with the username/password information. However, we don't want the login service to be called until we are actually in the TRY_LOGIN state.
This currently requires us to send a LOGIN notification w/ the username/password. A command will extract this information from the notification and store it on a LoginProxy. It will then
sendNotification(StateMachine.ACTION, null, TRY_LOGIN)
Now in the mediator on ENTER_TRY_LOGIN we can disable the Login button and the input fields and display a message "Logging in...". However, cannot execute the service until the state machine has changed into TRY_LOGIN. So we have to listen on a mediator for StateMachine.CHANGED, and:
if(note.getBody().name == TRY_LOGIN) { // (body of note is a State)
// now run login service
sendNotification(DO_LOGIN); // (registered to DoLoginCommand)
}
DoLoginCommandpublic class DoLoginCommand extends AsyncCommand implements IResponder {
override public function execute(note:INotification):void {
var loginProxy:LoginProxy = facade.retrieveProxy(LoginProxy.NAME) as LoginProxy;
loginProxy.login(this); // LoginProxy will execute an HTTPService call and add this command as a responder to the token
}
public function fault(info:Object):void {
sendNotification(StateMachine.ACTION, null, AppFacade.LOGIN) // go back to login
}
public function result(data:Object):void {
/* pseudo code here */
if( data.result) { // (login successful)
sendNotification(StateMachine.ACTION, null, AppFacade.MAIN); // go to main
}
else { // (invalid credentials)
fault(null);
}
}
}
I love using StateMachine to enforce what state your application is in, but the shortcoming for me right now is not being able to trigger a state change from the login event and pass that event object to the notification that will initiate the state change, but have that be accessible in the ENTER notification for the target state. Thus, the current need to wait for CHANGED on a mediator before doing the actual login.
To be clear, I would like to be able to:
var evt:LoginEvent = ... // this has been retrieved from the mediator, and has username & password properties
sendNotification(StateMachine.ACTION, evt, AppFacade.TRY_LOGIN);
ENTER_TRY_LOGIN will be registered w/ the DoLoginCommand.
Ideal DoLoginCommand:...
override public function execute(note:INotification) {
var state:State = note.getBody();
state.body // (the LoginEvent sent as the body of the TRY_LOGIN action notification)
}