Understanding Statemachines, Part 3: Conditional Logic

If you’re doing any significant amount of work with statmachines, you will most certainly encounter some conditional logic in your Statemachines. Take our vending machine.

When ever a coin is inserted, the invoked event will depend on whether the total amount of money inserted is sufficient to buy something.

If enough money has been tendered, the display should suggest that the customer make a selection. If insufficient money has been inserted, the customer should be prompted to insert more.

Conditional logic can be accomplished by using entry actions. See the diagram below.

State Diagram with Conditional Logic

Starting in the Accept Money state, when a coin is inserted, the coin event is fired and the Statemachine transitions into the Coin Inserted state. This is where it gets fun.

Upon entering of the Coin Inserted state its entry event is invoked: count_amount_tendered. This method will count the money and invoke the not_paid_yet or paid event accordingly. This will cause the Statemachine to transition into the appropriate state.

The Coin Inserted state is unique. You wouldn’t expect to find the Statemachine in the Coin Inserted state for any reason except to make this decision. Once the decision is made, the state changes. States like this are called Decision States.

Code

 1 require 'rubygems'
 2 require 'statemachine'
 3 
 4 class VendingMachineContext
 5 
 6   attr_accessor :statemachine
 7 
 8   def initialize
 9     @amount_tendered = 0
10   end
11 
12   def add_coin
13     @amount_tendered = @amount_tendered + 25
14   end
15 
16   def count_amount_tendered
17     if @amount_tendered >= 100
18       @statemachine.paid
19     else
20       @statemachine.not_paid_yet
21     end
22   end
23 
24   def prompt_money
25     puts "$.#{@amount_tendered}: more money please"
26   end
27 
28   def prompt_selection
29     puts "please make a selection"
30   end
31 end
32 
33 vending_machine = Statemachine.build do
34   trans :accept_money, :coin, :coin_inserted, :add_coin
35   state :coin_inserted do
36     event :not_paid_yet, :accept_money, :prompt_money
37     event :paid, :await_selection, :prompt_selection
38     on_entry :count_amount_tendered
39   end
40   context VendingMachineContext.new
41 end
42 vending_machine.context.statemachine = vending_machine
43 
44 vending_machine.coin
45 vending_machine.coin
46 vending_machine.coin
47 vending_machine.coin

Output:

1 $.25: more money please
2 $.50: more money please
3 $.75: more money please
4 please make a selection

Next lesson: Superstates

Micah Martin, Chairman, Founder

Micah Martin is co-founder of 8th Light and is known for his open source work such as FitNesse, Limelight, Joodo, and Speclj.