snacktime wrote:
> So I'm refactoring a very ugly piece of client code that needs to
> implement some fairly complicated error correction over a line based
> tcp protocol.  There are about 10 different error scenarios I have to
> detect and respond differently to.  Think error correction over serial
> lines, that's actually where this was derived from and now it's
> layered over a tcp connection.  Most of the scenarios involve several
> back and forth messages between client and host.  Most of the
> scenarios are very similar, so if you think of it as a tree I might
> not know what scenario I am dealing with until I've gotten 2-3 levels
> deep, and at any point in the tree I need to know where I am and what
> the possible branches are I can take based on the next response from
> the host.
>
> Any suggestions on how to implement this cleanly?

A state machine is fundamentally a set of states with associated
actions (if any) and transitions. A transition consists of a criteria
and a state to transition to. How you model that depends on how much
flexibility you need. If the state machine is fairly fixed you could
just as well implement it as a class. Something like this for example:

class StateMachine
   def initialize
       @state = :some_state     # Starting state of the machine


   end

   def some_state
     # Carry out actions for this state here.


     result = :foo  # Just a dummy result

     # Transitions:


     case result
     when :foo : @state = :some_other_state
     # more transitions ...


     else @state = :stop
     end
   end

   def some_other_state
     # Action goes here
     # Transition
     @state = :stop
   end

   # Doesn't have to do this, but it might be
   # nice to have a simple way of passing data
   # between the state machine and a block.
   # Perhaps use the block to do any IO etc.
   # so the state machine doesn't need to know
   # anything about it's environment
   def each
     while @state != :stop
       send @state
       yield self,@state
     end
   end
end

StateMachine.new.each { |sm,state| p state }

If you need something more dynamic, it would be easy enough to store
the transitions in a Hash of hash'es { :state_1 => {
:transition_criteria_1 => :state_2, :transition_criteria_2 => :state_3
} }.

Vidar