FrozenCanuck / Ki

A Statechart Framework for SproutCore
http://frozencanuck.wordpress.com
Other
105 stars 7 forks source link

Adding StateChart Observers #20

Closed geoffreyd closed 13 years ago

geoffreyd commented 13 years ago

I've got a mixin that I use that allows easy additions of observers in the statechart to fire state events when observed value changes.

https://gist.github.com/4cdb482c27eb896e632d

This can be used like:

https://gist.github.com/63c790db4883498578ca

This would be a neat addition. One question would be, should it be using sendEvent or invokeStateMethod in the addStatechartObserver method?

FrozenCanuck commented 13 years ago

This is an interesting idea. I like where you're going with it. The idea of whether to invoke sendEvent and invokeStateMethod probably needs to be something that you can additionally provide to the addStatechartObserver, that way it offers an appropriate level of flexibility.

FrozenCanuck commented 13 years ago

Something else I would do is offer a way of allowing a user to declare observations on the hash literal passed to Ki.State. That then removes having to always explicitly add and remove observations with the enterState and exitState. Something possibly like:

loadingState: Ki.State.design({

  statusChanged: function(target, key, revision) {
    if (target === App.ticketsController && key === 'status') {
      if (target.get(key) === SC.Record.READY_CLEAN) {
        this.gotoState('idle') ;
      }
      // else ignore any other status changes
    }
  }.stateObserver('App.ticketsController', 'status')

});

The code above then will allow the state to automatically add and remove the observation during its enterState and exitState procedures. In addition, the statechart can handle the target and key check implicitly, which then removes the need for users having to write the checks themselves. So you then get something like:

loadingState: Ki.State.design({

  statusChanged: function(target, key, revision) {
    if (target.get(key) === SC.Record.READY_CLEAN) {
      this.gotoState('idle')
    }
  }.stateObserver('App.ticketsController', 'status')

});

A lot less code and looks a lot cleaner. In any case, let me play around with this idea some more since you bring up an interesting case.

FrozenCanuck commented 13 years ago

Slight improvement:

loadingState: Ki.State.design({

  statusChanged: function(target, key, revision) {
    if (target.get(key) === SC.Record.READY_CLEAN) {
      this.gotoState('idle')
    }
  }.stateObserver('App.ticketsController.status')

});

Changed stateObserver('App.ticketsController', 'status') to stateObserver('App.ticketsController.status'). Just makes things a bit cleaner. The state's logic can implicitly split the target from the key instead of the user explicitly doing it. This also means that you can then add a bunch of paths to stateObserver if you so choose to, like so:

stateObserver('App.ticketsController.status', 'App.someController.value')

I'm still playing around with how the API should work, but I like where this is going.