piotrmurach / finite_machine

A minimal finite state machine with a straightforward syntax.
https://piotrmurach.github.io/finite_machine/
MIT License
808 stars 38 forks source link

Transition does not happen if any_state is used in the definition. #76

Closed domokos closed 1 year ago

domokos commented 2 years ago

Describe the problem

Transition does not happen if any_state is used in the definition

Steps to reproduce the problem

class MySM < FiniteMachine::Definition

    # Does not work - definition accepted but blocks transition
    event :shouldturnon, from: any_state, to: :on

    # These work fine
    event :turnson, to: :on
    event :init, none: :off

    # Log state transitions
    on_before do |event|
        if event.from != event.to
            puts 'Working state change from '\
                "#{event.from} to #{event.to}" 
        else
            puts 'Impossible state change from '\
                "#{event.from} to #{event.to}"
        end
    end

    on_enter(:off) do |event|
      puts 'Off fired OK'
    end

    on_enter(:on) do
      puts 'This is never triggered if any_state is used in definition'
    end

  end

  mymachine = MySM.new

  #Works
  mymachine.init

  #Does not work
  mymachine.shouldturnon

  #Same thing works
  mymachine.turnson

Actual behaviour

Transition does not happen if any_state is used in the from definition

Expected behaviour

Transition should happen

Describe your environment

This worked fine with earlier ruby/finite_machine where I used the

event :turnoff, any: :off

notation. Bit me when I upgraded.

piotrmurach commented 1 year ago

Hi 👋

Thank you for this issue report.

I can confirm that any_state works when a finite machine is created with the new. So the following works fine:

fsm = FiniteMachine.new do
  initial :green
  event :slow, any_state => :yellow
  event :stop, :yellow => :red
end
fsm.slow
fsm.current # => :yellow

When it fails is when using the define or class definition for a finite machine:

FSM = FiniteMachine.define do
  initial :green
  event :slow, any_state => :yellow
  event :stop, :yellow => :red
end
fsm = FSM.new
fsm.slow
fsm.current # => :green

The temporary fix is to use the FiniteMachine::ANY_STATE directly:

FSM = FiniteMachine.define do
  initial :green
  event :slow, FiniteMachine::ANY_STATE => :yellow
  event :stop, :yellow => :red
end
fsm = FSM.new
fsm.slow
fsm.current # => :green

The issue is that the any_state and any_event are converted to Proc objects when called within define block. I'll provide a fix shortly.