Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using machina as a state machine for multiple entities #58

Closed
jeff-collins opened this issue Oct 23, 2014 · 6 comments
Closed

using machina as a state machine for multiple entities #58

jeff-collins opened this issue Oct 23, 2014 · 6 comments

Comments

@jeff-collins
Copy link

I like the structure of the code that is written with machina. I'd like to use it for a light document workflow. The only problem is that obviously each document in the system will have its own state. I can't see in the API a way to "restore" an instance of a FSM with the current state of a document on a document CRUD event and then let the state machine determine what to do next. The API documentation says not to assign the "state" property directly. Is there a way to essentially restore state to a FSM for a moment when a Document is modified by assigning its status field as the state of the FSM, call handle() on the FSM, and then restore it to the next Document's status that comes through, and call handle() on it again - this time in the context of the next Document's status field?

Other options I'm considering are - create the Fsm from scratch on every CRUD event, and dynamically make the initialState be the current state of the Document, but that would seem pretty heavy. The other thought is to just call transition() artificially to set the state. But since this is being done as a means to artificially "reset" the state on the FSM, it would mean that _onEnter() could not be easily used because it would be hard to tell the difference between an actual state transition as the result of an event, and just a restore.

Any guidance is appreciated.

@ifandelse
Copy link
Owner

@jeff-collins just a quick reply after reading your comments above....the good news is I'm working on a "behavioral FSM" approach for the next version of machina that will support exactly what you're looking for (state separated from behavior defined in states/handlers). I know that doesn't help you now, but it's def going to land as soon as I'm happy with the approaches I've been comparing locally. (it's a big need of mine as well). In the meantime, some stop-gap options could include:

  • an instance for each document (heavier memory-wise, but could work well if we're not talking insane amounts of simultaneous instances. Pros: much less risky, since it doesn't involve changing this.state without transition(). How many simultaneous instances do you think you're looking at?
  • You could store each doc's "state" in a hash/lookup object and pass it into input handlers as an argument (and not really store document state locally on the FSM at all). The question here would be how to update the state passed in.... options could be passing an update callback, or - if the doc's state is hanging off an object, you can mutate the object inside the handler and "take advantage"(?) of shared refs 😄 Pros - less memory overhead, Cons - risky, since it involves mutating state value directly, and it would likely mean monkey-patching the handle call to inspect the incoming state for state prop, set state on the FSM, and then pass the call on thru to machina.Fsm.prototype.handle, etc. to execute the handler. I haven't tried this personally, and it kinda makes me sad to admit I've thought of it (hence my efforts on next version to add first class support). FWIW, I started to put a code snippet here exploring how this could be done and quickly realized I'd not only have to monkey patch handle, but also subscribe to all events on the FSM and ensure that transition events caused the state prop on each "client" state to transition correctly, etc. It's not impossible (I think I've worked it out in my head), but seems fraught with several gotchas.

I have one other thought - will mull it over while I'm out running errands today.

@jeff-collins
Copy link
Author

thanks for the quick response - sounds great that you're working on this case. I happened on one more hack solution that lets me reuse the state machine instance, which is just to have a _isRestoring flag dynamically assigned to the FSM object when I transition to the desired non-initial state based on the status of the Document. It mainly affects only coding the _onEnter methods, because they have to check this flag before doing their business logic - but that's a minor concession for now.

@ifandelse
Copy link
Owner

@jeff-collins good to hear you have a workaround for the moment. I'll keep you posted as I get the bits ready on the new version. Camping this weekend, and then hopefully I'll get some more time on it mid-to-late next week.

@fergusonjason
Copy link

I'm using Machina to control a wizard with Spring MVC as the backend (mostly because I couldn't get Spring Webflow working with AJAX).

What I do is have a state defined for each "page" of the wizard. The _onEnter() handle will call a method in Spring MVC via GET to return the page. Each state has a 'navigate' handle, which the navigation buttons call, passing 'NEXT','PREV','FINISH', or 'CANCEL'. This value is passed via POST, along with the data on the wizard form, to another Spring MVC controller method. That method returns the next state to transition to (I don't trust the Javascript to decide on the next state).

On thing I wish I could do is cancel the transition entirely in the _onExit() handle for a state. Of course, at this point, I've worked around that, so I would have to rewrite code.

@jeff-collins
Copy link
Author

@ifandelse the workaround is more involved than it should be so I'm looking forward to a better solution. I ended up needing to define another "hidden" state called _undefined so that I can reenter the initial state when we restore to a new entity, restored entity, etc

@ifandelse
Copy link
Owner

@jeff-collins when you get a chance, check out the v1.0.0-1 pre-release. The BehavioralFsm constructor lets you utilize the same FSM behavior for multiple "clients". The updated README talks a bit about this change as well as the wiki.

@dartmanx - apologies for the delay in replying to you! I understand the desire to cancel the transition in _onExit. I don't currently have plans to add that, however, similar behavior (if you still need it) could be achieved by using a constraint-based FSM. An example of this can be seen in this discussion. Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants