Hi!!!

I'm Arek Turlewicz, software developer with background in Open Source infrastructure, based in Oxford, UK.

Recent Articles

Follow Arek_Turlewicz on Twitter

Finite State Machine in Ruby

2011-01-20

There is excellent ruby statemachine project with very good examples and documentation,

gem install statemachine

but when I was using statemachines in my project there was one annoying thing. When you create statemachine you define its structure in block and it takes good few lines on the screen.

Solution for this problem is very simple, you need to create your own builder class, that will do everything what is needed to setup machine correctly:

class ServerBuilder
    def self.build                
    Statemachine.build do
      trans :stopped, :power_on, :booting, :power_on
      trans :booting, :done, :running
      trans :running, :done, :shutting_down
      trans :shutting_down, :done, :stopped
      context ServerContext.new
    end
  end
end

class ServerContext
  def power_on
    puts "Server is booting..."
  end
end

s = ServerBuilder.build

puts s.state
s.power_on
puts s.state

Now we can easily put class definition to separate file and have something like this

require 'server_description'
s = Server.build
puts s.state
s.power_on
puts s.state

Sometimes is quite handy to keep reference to server inside its context, then we could have one central method in context that decide what to do based on current machine state.

require 'rubygems'
require 'statemachine'

class Server < Statemachine::Statemachine
  def self.build
    @fsm=Statemachine.build do |fsm|
      trans :stopped, :power_on, :booting, :action
      trans :booting, :done, :running, :action
      trans :running, :power_off, :shutting_down, :action
      trans :shutting_down, :done, :stopped, :action
    end
    @fsm.context=ServerContext.new(@fsm)
    @fsm
  end
end

class ServerContext
  def initialize(fsm)
    @fsm = fsm
  end
  def action
    case @fsm.state
      when :stopped then puts "Someone pressed power on button"
      when :booting then puts "Booting finished"
      when :running then puts "Someone pressed shutdown button"
      when :shuting_down then puts "Server is stopped now"
    end
  end
end

s = Server.build
puts s.state
s.power_on
puts s.state
s.done
puts s.state
s.power_off
s.done
puts s.state

After you run this script you should see:

$ ruby server_fsm.rb 
stopped
Someone pressed power on button
booting
Booting finished
Someone pressed shutdown button

That’s all for now.

Comment
blog comments powered by Disqus