I’m working on a somewhat complex project that really begs for a state
machine to keep the logic all straight. I’ve been playing with the
state_machine [1] gem the last several days to see if it’s suitable. I
like the syntax and feature set, but I’m having difficulty mapping its
capabilities into code.
The main problem has to do with the relationship between state machines
and classes. While a class can have 1 (or more) state machines declared
inside of it, a state machine cannot be used across 1 (or more)
classes.
If I am correct about that limitation, I am wondering how to structure a
large and complex state machine. I like to keep my classes small and
simple so that they are easy to understand and debug. In the case where
a class has many states and events, the business logic attached to the
transitions (in my case) are rather complex. The size of the code in the
class very quickly balloons (a few hundred lines).
Here’s an example.
Assume I am building an application that connects to a web service. The
web service is a gateway to other services behind it, but it acts as a
broker for all requests. Upon connection to the web service gateway, the
gateway can indicate if it is available to forward requests,
delayed/behind in forwarding requests, or if the back-end services are
offline.
So, I build a machine that has several states along with unique behavior
for each one.
State - Starting
Initialize all libraries for connecting to the web service. Upon
completion, transition to the Connecting state.
State - Connecting
Open a network connection to the web service. Upon timeout, transition
to ConnectionError state. Upon successful connection, transition to the
Connected state.
State - Connected
Ask the web service for its current status. The web service can indicate
it is in 1 of 3 states itself. Okay, Delayed, or Offline. Depending upon
the response, transition to the appropriate state. In the meantime, each
of the Okay/Delayed/Offline states can get events indicating the web
service’s current status. It seems to me like this is appropriately
handled by the “Super state” Connected rather than duplicating this
functionality across multiple sub states.
As you can imagine from the example given above, jamming all of this
functionality into a single class would result in a pretty huge class
(hundreds or even thousands of lines).
I would like to offload some of that logic to other classes and have a
state transition “hand off” to another class while everything is still
controlled by the same overall machine. So, I guess what I want is a
machine to be able to encompass multiple classes.
Does anyone else have any experience with this gem and building machines
with more complex use-cases than the examples? Am I trying to shoe-horn
this gem into a use-case that it isn’t suited to solving? Should I
consider using a different statemachine gem? Suggestions?
cr