Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
F

finite_machine

> 编程语言
Open source

A minimal finite state machine with a straightforward syntax.

804 stars0 likes3 views
WebsiteGitHub

About

A minimal finite state machine with a straightforward syntax.

FiniteMachine

A minimal finite state machine with a straightforward and intuitive syntax. You can quickly model states and transitions and register callbacks to watch for triggered transitions.

Features

  • plain object state machine
  • easy custom object integration
  • natural DSL for declaring events, callbacks and exception handlers
  • callbacks for state and event changes
  • ability to check reachable state(s)
  • ability to check for terminal state(s)
  • transition guard conditions
  • dynamic choice pseudostates
  • thread safe

Installation

Add this line to your application's Gemfile:

gem "finite_machine"

Then execute:

$ bundle

Or install it yourself as:

$ gem install finite_machine

Contents

  • 1. Usage
  • 2. API
    • 2.1 new
    • 2.2 define
    • 2.3 current
    • 2.4 initial
    • 2.5 terminal
    • 2.6 is?
    • 2.7 trigger
      • 2.7.1 :auto_methods
    • 2.8 can? and cannot?
    • 2.9 target
      • 2.9.1 :alias_target
    • 2.10 restore!
    • 2.11 states
    • 2.12 events
  • 3. States and Transitions
    • 3.1 Triggering transitions
    • 3.2 Dangerous transitions
    • 3.3 Multiple from states
    • 3.4 any_state transitions
    • 3.5 Collapsing transitions
    • 3.6 Silent transitions
    • 3.7 Logging transitions
    • 3.8 Conditional transitions
      • 3.8.1 Using a Proc
      • 3.8.2 Using a Symbol
      • 3.8.3 Using a String
      • 3.8.4 Combining transition conditions
    • 3.9 Choice pseudostates
      • 3.9.1 Dynamic choice conditions
      • 3.9.2 Multiple from states
  • 4. Callbacks
    • 4.1 on_(enter|transition|exit)
    • 4.2 on_(before|after)
    • 4.3 once_on
    • 4.4 Execution sequence
    • 4.5 Callback parameters
    • 4.6 Duplicate callbacks
    • 4.7 Fluid callbacks
    • 4.8 Methods inside callbacks
    • 4.9 Cancelling callbacks
    • 4.10 Asynchronous callbacks
    • 4.11 Instance callbacks
  • 5. Error Handling
    • 5.1 Using target
  • 6. Stand-alone
    • 6.1 Creating a Definition
    • 6.2 Targeting definition
    • 6.3 Definition inheritance
  • 7. Integration
    • 7.1 Plain Ruby Objects
    • 7.2 ActiveRecord
    • 7.3 Transactions
  • 8. Tips

1. Usage

Here is a very simple example of a state machine:

ruby
fm = FiniteMachine.new do
  initial :red

  event :ready, :red    => :yellow
  event :go,    :yellow => :green
  event :stop,  :green  => :red

  on_before(:ready) { |event| ... }
  on_exit(:yellow)  { |event| ... }
  on_enter(:green)  { |event| ... }
  on_after(:stop)   { |event| ... }
end

By calling the new method on FiniteMachine, you gain access to a powerful DSL for expressing transitions and registering callbacks.

Having declared the states and transitions, you can check current state:

ruby
fm.current # => :red

And then trigger transitions using the trigger:

ruby
fm.trigger(:ready)

Or you can use direct method calls:

ruby
fm.ready

Read States and Transitions and Callbacks sections for more details.

Alternatively, you can construct the state machine like a regular object using the same DSL methods. Similar machine could be reimplemented as follows:

ruby
fm = FiniteMachine.new(initial: :red)
fm.event(:ready, :red    => :yellow)
fm.event(:go,    :yellow => :green)
fm.event(:stop,  :green  => :red)
fm.on_before(:ready) { |event| ... }
fm.on_exit(:yellow)  { |event| ... }
fm.on_enter(:green)  { |event| ... }
fm.on_after(:stop)   { |event| ... }

2. API

2.1 new

In most cases you will want to create an instance of FiniteMachine class using the new method. At the bare minimum you need specify the transition events inside a block using the event helper:

ruby
fm = FiniteMachine.new do
  initial :green

  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
  event :ready, :red    => :yellow
  event :go,    :yellow => :green
end

Alternatively, you can skip block definition and instead call DSL methods directly on the state machine instance:

ruby
fm = FiniteMachine.new
fm.initial(:green)
fm.event(:slow, :green  => :yellow)
fm.event(:stop, :yellow => :red)
fm.event(:ready,:red    => :yellow)
fm.event(:go,   :yellow => :green)

As a guiding rule, any method exposed via DSL is available as a regular method call on the state machine instance.

2.2 define

To create a reusable definition for a state machine use define method. By calling define you're creating an anonymous class that can act as a factory for state machines. For example, below we create a TrafficLights class that contains our state machine definition:

ruby
TrafficLights = FiniteMachine.define do
  initial :green

  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
  event :ready, :red    => :yellow
  event :go,    :yellow => :green
end

Then we can create however many instance of above class:

ruby
lights_fm_a = TrafficLights.new
lights_fm_b = TrafficLights.new

Each instance will start in consistent state:

ruby
lights_fm_a.current # => :green
lights_fm_b.current # => :green

We can then trigger event for one instance and not the other:

ruby
lights_fm_a.slow
lights_fm_a.current # => :yellow
lights_fm_b.current # => :green

2.3 current

The FiniteMachine allows you to query the current state by calling the current method.

ruby
fm.current  # => :red

2.4 initial

There are number of ways to provide the initial state in FiniteMachine depending on your requirements.

By default the FiniteMachine will be in the :none state and you will need to provide an explicit event to transition out of this state.

ruby
fm = FiniteMachine.new do
  event :init,  :none   => :green
  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
end

fm.current # => :none
fm.init    # => true
fm.current # => :green

If you specify initial state using the initial helper, then the state machine will be created already in that state and an implicit init event will be created for you and automatically triggered upon the state machine initialization.

ruby
fm = FiniteMachine.new do
  initial :green   # fires init event that transitions from :none to :green state

  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
end

fm.current # => :green

Or by passing named argument :initial like so:

ruby
fm = FiniteMachine.new(initial: :green) do
  ...
end

If you want to defer setting the initial state, pass the :defer option to the initial helper. By default FiniteMachine will create init event that will allow to transition from :none state to the new state.

ruby
fm = FiniteMachine.new do
  initial :green, defer: true # Defer calling :init event

  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
end
fm.current # => :none
fm.init    # execute initial transition
fm.current # => :green

If your target object already has init method or one of the events names redefines init, you can use different name by passing :event option to initial helper.

ruby
fm = FiniteMachine.new do
  initial :green, event: :start, defer: true # Rename event from :init to :start

  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
end

fm.current # => :none
fm.start   # => call the renamed event
fm.current # => :green

By default the initial does not trigger any callbacks. If you need to fire callbacks and any event associated actions on initial transition, pass the silent option set to false like so:

ruby
fm = FiniteMachine.new do
  initial :green, silent: false  # callbacks are triggered

  event :slow,  :green  => :yellow
  event :stop,  :yellow => :red
end

2.5 terminal

To specify a final state FiniteMachine uses the terminal method.

ruby
fm = FiniteMachine.new do
  initial :green

  terminal :red

  event :slow, :green  => :yellow
  event :stop, :yellow => :red
  event :go,   :red    => :green
end

When the terminal state has been specified, you can use terminated? method on the state machine instance to verify if the terminal state has been reached or not.

ruby
fm.terminated?  # => false
fm.slow         # => true
fm.terminated?  # => false
fm.stop         # => true
fm.terminated?  # => true

The terminal can accept more than one state.

ruby
fm = FiniteMachine.new do
  initial :open

  terminal :close, :canceled

  event :resolve, :open => :close
  event :decline, :open => :canceled
end

And the terminal state can be checked using terminated?:

ruby
fm.decline
fm.terminated? # => true

2.6 is?

To verify whether or not a state machine is in a given state, FiniteMachine uses is? method. It returns true if the machine is found to be in the given state, or false otherwise.

ruby
fm.is?(:red)    # => true
fm.is?(:yellow) # => false

Moreover, you can use helper methods to check for current state using the state name itself like so

ruby
fm.red?     # => true
fm.yellow?  # => false

2.7 trigger

Transition events can be fired by calling the trigger method with the event name and remaining arguments as data. The return value is either true or false depending whether the transition succeeded or not:

ruby
fm.trigger(:ready) # => true
fm.trigger(:ready, "one", "two", "three") # => true

By default, the FiniteMachine automatically converts all the transition event names into methods:

ruby
fm.ready # => true
fm.ready("one", "two", "three") # => true

Please see States and Transitions for in-depth treatment of firing transitions.

2.7.1 :auto_methods

By default, all event names will be converted by FiniteMachine into method names. This also means that you won't be able to use event names such as :fail or :trigger as these are already defined on the machine instance. In situations when you wish to use any event name for your event names use :auto_methods keyword to disable automatic methods generation. F

Issues· 4 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rubyconditional-transitionsfinite-machineruby-gemstate-machine

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言