Saturday, April 18, 2009

Golem Statemachine

Following a frustrating week with the acts_as_state_machine/AASM plugin, I ended up writing my own finite state machine library for Ruby.

It's called Golem, and it's now on Github, here: http://github.com/zuk/golem_statemachine/tree/master

The code is cleaner than AASM's (or at least I think so), which should make debugging less frustrating. There's also a nice DSL for defining the state machine.

For example, to implement this statemachine:

You would define it like so:

define_statemachine do
  initial_state :HUNGRY

  state :HUNGRY do
    on :eat do
      transition :to => :SATIATED, :if => :likes?
      transition do
        action do |monster| 
          puts "#{monster} says BLAH!!"
        end
      end
    end
  end

  state :SATIATED do
    enter do |monster|
      puts "#{monster} says BURP!!!"
    end
  end
end

The state machine definition goes inside your Ruby class (or ActiveRecord model), endowing the class with finite state machine behavior:

require 'golem'

class Monster
  include Golem

  attr_accessor :state

  def initialize(name)
    @name = name
  end

  def to_s
    @name
  end

  def likes?(food)
    food.kind_of?(String)
  end

  define_statemachine do
    # ...
  end
end

m = Monster.new("Stringosaurus")
puts m.current_state # ==> :HUNGRY
m.eat(12345)         # ==> "Stringosaurus says BLAH!!"
puts m.current_state # ==> :HUNGRY
m.eat("abcde")       # ==> "Stringosaurus says BURP!!"
puts m.current_state # ==> :SATIATED