FrozenCanuck / Lebowski

The test automation framework for SproutCore applications
http://frozencanuck.wordpress.com
MIT License
78 stars 8 forks source link

Multiple actions delivered to state #10

Closed rvalle closed 13 years ago

rvalle commented 13 years ago

I am still investigating it, but my statechart seems to be receiving several callbacks for a sendAction call.

I am getting the callback function called 3/4 times (at the same state).

My startechart is using all possible features: concurrent states, history states, plug ins, hierarchies of states, etc. I also find useful sending actions from within states...

Maybe I am doing something wrong... may be there is am issue. Is there any case

Is there any known issue (or misuse) that would trigger this situation?

rvalle commented 13 years ago

Ok, I see what is going on after tracing the code:

If you have a parentState with several concurrent states, and you try to handle the action at the parent state its when the issue is triggered.

Imagine you have a PortalState with several Portlets running concurrently, each one with a, lets say, PortletState. You might think that handling a SignOut action should be done by PortalState, regardless of the Portlets running, and their state. When you deliver SignOut action, Ki will try with each PortletStates (active states) and its ancestors, and effectively the action is called on the PortletState one time per each Substate.

I am not completely sure if this is the desired outcome, or if it is known limitation of Ki. But it doesn't look to me as a Bug, as the code seems to be implemented with this purpose.

If the substates are not concurrent, say, the portlets get activated on focus or something... then sending the Signout event would be executed only once.

I just think the use case described above is useful. What I am doing to workaround this is creating an additional substate that handles this particular operations.

FrozenCanuck commented 13 years ago

Hey, apologies for not getting back to you sooner on this.

Regarding the issue you are running into, you are correct: The parent state will get called for each concurrent substate that can not handle the sent action. This is on purpose. Basically, you need to see the parent states as a form of abstraction regarding the handling of events. Therefore, each state that is current in the statechart will be handle independently of the other current states. The parents state do not, and should not, keep track of how many times they are being called during a state transition when an action is sent to the statechart.

Because you are sending an action that can happen at any time when in any of the concurrent substates and you only want to handle the action once, then, yes, having another concurrent substate the specifically handles that action instead of the parent state is the correct solution. I actually had this question come up with another individual with regard to concurrent substates and a parent state.

rvalle commented 13 years ago

Thanks, yeap, its fine, just knowing it, and you can implement your goal.

FrozenCanuck commented 13 years ago

I totally forgot the other option you can apply, which completely skipped my mind -- D'uh!

Given that Ki.State derives from SC.Object, you can either extend Ki.State or just mix in an object literal that contains the common functionality that all your states need. This will then eliminate the need for another state and avoid adding the common functionality in a parent state. As an example, you can do the following:

MyApp.MyState = Ki.State.extend({
  foo: function() { ... }
});

MyApp.statechart = Ki.Statechart.create({

  rootState: Ki.State.design({
    initialSubstate: 'stateA',

    stateA: MyApp.MyState.design({ ... }),

    stateB: MyApp.MyState.design({ ... })
  })

});