### Setup TestHelper for Minitest and RSpec Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to require and include the State Machines TestHelper module in Minitest and RSpec test files for setting up state machine tests. ```ruby # For Minitest require 'state_machines/test_helper' class VehicleTest < Minitest::Test include StateMachines::TestHelper def test_initial_state vehicle = Vehicle.new assert_sm_state vehicle, :parked end end # For RSpec require 'state_machines/test_helper' RSpec.describe Vehicle do include StateMachines::TestHelper it "starts in parked state" do vehicle = Vehicle.new assert_sm_state vehicle, :parked end end ``` -------------------------------- ### Ruby State Machine Interaction Example Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to instantiate and interact with the Vehicle state machine. Shows how to check current state, available events, transition details, and how to fire events to change the state. ```ruby vehicle = Vehicle.new # => # vehicle.state # => "parked" vehicle.state_name # => :parked vehicle.human_state_name # => "parked" vehicle.parked? # => true vehicle.can_ignite? # => true vehicle.ignite_transition # => # vehicle.state_events # => [:ignite] vehicle.state_transitions # => [#] vehicle.speed # => 0 vehicle.moving? # => false vehicle.ignite # => true vehicle.parked? # => false vehicle.idling? # => true vehicle.speed # => 10 vehicle # => # vehicle.shift_up # => true vehicle.speed # => 10 vehicle.moving? # => true vehicle # => # # A generic event helper is available to fire without going through the event's instance method vehicle.fire_state_event(:shift_up) # => true ``` -------------------------------- ### RSpec Example for State Machines Source: https://context7.com/state-machines/state_machines/llms.txt Demonstrates how to test state machines using RSpec and the StateMachines::TestHelper. It verifies the initial state and event transitions of a Vehicle object. ```ruby RSpec.describe Vehicle do include StateMachines::TestHelper it "starts in parked state" do vehicle = Vehicle.new assert_sm_state vehicle, :parked end it "can transition through events" do vehicle = Vehicle.new assert_sm_transition vehicle, :ignite, :idling end end ``` -------------------------------- ### Basic State Machine Assertions Source: https://github.com/state-machines/state_machines/blob/master/README.md Provides examples of basic assertions using the TestHelper, including checking states, transition capabilities, and actual transitions for single and multiple state machines. ```ruby vehicle = Vehicle.new # New standardized API (all methods prefixed with assert_sm_) assert_sm_state(vehicle, :parked) # Uses default :state machine assert_sm_state(vehicle, :parked, machine_name: :status) # Specify machine explicitly assert_sm_can_transition(vehicle, :ignite) # Test transition capability assert_sm_cannot_transition(vehicle, :shift_up) # Test transition restriction assert_sm_transition(vehicle, :ignite, :idling) # Test actual transition # Multi-FSM examples assert_sm_state(vehicle, :inactive, machine_name: :insurance_state) # Test insurance state assert_sm_can_transition(vehicle, :buy_insurance, machine_name: :insurance_state) ``` -------------------------------- ### Introspect State Machine States and Paths Source: https://github.com/state-machines/state_machines/blob/master/README.md Provides examples for accessing human-readable names, analyzing available transition paths, and inspecting state machine definitions. ```ruby Vehicle.human_state_name(:first_gear) vehicle.state_paths vehicle.state_paths(:from => :parked, :to => :first_gear) Vehicle.state_machine.states.to_a ``` -------------------------------- ### Interact with state machine instances Source: https://github.com/state-machines/state_machines/blob/master/README.md Examples of interacting with a vehicle state machine, including checking current states, triggering transitions, and inspecting the machine definition. ```ruby vehicle = Vehicle.new vehicle.state # => "parked" vehicle.machine.ignite # => true vehicle.machine.state # => "idling" vehicle.state # => "idling" vehicle.machine.state_transitions # => [#] vehicle.machine.definition.states.keys # => :first_gear, :second_gear, :parked, :idling ``` -------------------------------- ### Install State Machines Gem (Ruby) Source: https://github.com/state-machines/state_machines/blob/master/README.md This snippet shows how to add the 'state_machines' gem to your Ruby application's Gemfile and install it using Bundler. Alternatively, it can be installed directly using the 'gem install' command. ```ruby gem 'state_machines' ``` ```bash bundle ``` ```bash gem install state_machines ``` -------------------------------- ### State Machine Error Handling for Guards in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md Examples of ArgumentErrors raised when referencing undefined state machines or invalid states within guard conditions. ```ruby # Referencing a non-existent state machine event :invalid, if_state: { nonexistent_machine: :some_state } # Referencing a non-existent state event :another_invalid, if_state: { shields: :nonexistent_state } ``` -------------------------------- ### Mongoid Integration for State Machines Source: https://context7.com/state-machines/state_machines/llms.txt Illustrates the integration of State Machines with Mongoid documents for state management in MongoDB. This example defines a state machine for an Order's status. ```ruby # Mongoid integration (requires state_machines-mongoid gem) class Order include Mongoid::Document state_machine :status, initial: :pending do event :submit do transition pending: :submitted end end end ``` -------------------------------- ### ActiveRecord Integration for State Machines Source: https://context7.com/state-machines/state_machines/llms.txt Shows how to integrate State Machines with ActiveRecord models for database persistence. It includes defining states, events, and transitions, along with examples of scope usage and implicit event transitions via attributes. ```ruby # ActiveRecord integration (requires state_machines-activerecord gem) class Vehicle < ActiveRecord::Base state_machine :state, initial: :parked do event :ignite do transition parked: :idling end # Scopes are automatically created # Vehicle.with_state(:parked) # Vehicle.with_states(:parked, :idling) # Vehicle.without_state(:stalled) end end # Implicit event transitions via attributes vehicle = Vehicle.create vehicle.state_event = 'ignite' vehicle.save # => true, state changes to 'idling' ``` -------------------------------- ### State Matchers and Helpers in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Demonstrates the use of 'all', 'any', and 'same' matchers for defining state transitions in Ruby. These helpers allow for flexible and concise transition definitions, including matching all states, all states except specific ones, and looping back to the same state. ```ruby class Vehicle state_machine :state, initial: :parked do # Match all states event :emergency_stop do transition all => :stopped end # Match all states except specific ones event :crash do transition all - [:parked, :stopped] => :stalled end # Alternative syntax with .except event :tow do transition all.except(:parked) => :towed end # Loopback to same state event :honk do transition all => same end # Verbose syntax event :repair do transition from: :stalled, to: :parked, if: :mechanic_available? transition from: :stalled, to: same # Stay stalled if no mechanic end # Multiple from states to single destination before_transition [:idling, :first_gear] => :parked, do: :engage_parking_brake after_transition any => :parked, do: :set_alarm end end ``` -------------------------------- ### Defining after_transition hooks with Ruby Source: https://github.com/state-machines/state_machines/wiki/FAQ-and-Gotchas Demonstrates the correct usage of the :on option versus state transition identifiers in after_transition hooks. It also shows how to pass an array of methods to the :do option for multiple callbacks. ```ruby # Good: after_transition all => :paid, do: :process_payment # Bad (won't work): after_transition on: :paid, do: :process_payment # Multiple callbacks: after_transition all => :paid, do: [:process_payment, :notify_paid] ``` -------------------------------- ### Test Helper for State Machines in Ruby (Minitest) Source: https://context7.com/state-machines/state_machines/llms.txt Demonstrates how to use the `StateMachines::TestHelper` module with Minitest for testing state machines in Ruby. It covers assertions for initial state, transition possibilities, actual transitions, event triggers, callback definitions, multiple state machines, and sync mode. ```ruby require 'state_machines/test_helper' class VehicleTest < Minitest::Test include StateMachines::TestHelper def setup @vehicle = Vehicle.new end def test_initial_state assert_sm_state @vehicle, :parked end def test_can_transition assert_sm_can_transition @vehicle, :ignite assert_sm_cannot_transition @vehicle, :park end def test_transition assert_sm_transition @vehicle, :ignite, :idling end def test_event_triggers @vehicle.ignite assert_sm_triggers_event(@vehicle, :shift_up) { @vehicle.shift_up } end def test_callback_defined assert_before_transition Vehicle, on: :crash, do: :brace_for_impact assert_after_transition Vehicle, from: :stalled, to: :parked, do: :log_repair end # Multiple state machine support def test_multiple_machines assert_sm_state @vehicle, :active, machine_name: :alarm_state assert_sm_can_transition @vehicle, :disable, machine_name: :alarm_state end # Sync mode assertions def test_sync_mode assert_sm_sync_mode @vehicle assert_sm_all_sync @vehicle assert_sm_immediate_execution @vehicle, :ignite end end ``` -------------------------------- ### Generate Dynamic State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to instantiate a state machine at runtime, allowing transitions to be loaded from external sources like databases. ```ruby class Vehicle attr_accessor :state def initialize(*); super; machine; end def transitions [{parked: :idling, on: :ignite}, {idling: :first_gear, first_gear: :second_gear, on: :shift_up}] end def machine vehicle = self @machine ||= Machine.new(vehicle, initial: :parked, action: :save) do vehicle.transitions.each {|attrs| transition(attrs)} end end def save; true; end end ``` -------------------------------- ### Define Verbose Transitions in Ruby State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to use explicit options like :from, :except_to, and callbacks to define granular transition logic within a state machine. ```ruby class Vehicle state_machine initial: :parked do before_transition from: :parked, except_to: :parked, do: :put_on_seatbelt after_transition to: :parked do |vehicle, transition| vehicle.seatbelt = 'off' end event :ignite do transition from: :parked, to: :idling end end end ``` -------------------------------- ### Callback Definition Testing for State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to verify the correct definition of state machine callbacks, including `after_transition` and `before_transition`, for both class and instance-level state machines. ```ruby # Test after_transition callbacks assert_after_transition(Vehicle, on: :crash, do: :tow) assert_after_transition(Vehicle, from: :stalled, to: :parked, do: :log_repair) # Test before_transition callbacks assert_before_transition(Vehicle, from: :parked, do: :put_on_seatbelt) assert_before_transition(Vehicle, on: :ignite, if: :seatbelt_on?) # Works with machine instances too machine = Vehicle.state_machine(:state) assert_after_transition(machine, on: :crash, do: :tow) ``` -------------------------------- ### Handling State Machine Transitions and Errors in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to trigger state transitions, handle invalid transition exceptions, and use state predicates. It also shows how to fire multiple events in parallel and manage namespaced state machines. ```ruby vehicle.park! # Raises StateMachines::InvalidTransition vehicle.state?(:parked) # Returns boolean vehicle.park(meter_number: '12345') # Transition with arguments vehicle.fire_events(:shift_down, :enable_alarm) # Parallel execution ``` -------------------------------- ### Define a State Machine in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Demonstrates how to use the state_machine DSL to define initial states, events, and transitions within a class. It shows how to integrate state logic with instance methods and initialization. ```ruby class Vehicle attr_accessor :seatbelt_on, :time_used state_machine :state, initial: :parked do event :ignite do transition parked: :idling end event :park do transition [:idling, :first_gear] => :parked end event :shift_up do transition idling: :first_gear, first_gear: :second_gear, second_gear: :third_gear end event :shift_down do transition third_gear: :second_gear, second_gear: :first_gear end event :crash do transition all - [:parked, :stalled] => :stalled, if: ->(vehicle) { !vehicle.passed_inspection? } end end def initialize @seatbelt_on = false @time_used = 0 super() # Must call super() to initialize state machine attributes end def passed_inspection? false end end vehicle = Vehicle.new vehicle.state # => "parked" vehicle.state_name # => :parked vehicle.parked? # => true vehicle.can_ignite? # => true vehicle.ignite # => true vehicle.state # => "idling" vehicle.idling? # => true ``` -------------------------------- ### Implement Transition Callbacks in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Illustrates how to use before, after, around, and failure callbacks to manage side effects during state transitions. Supports conditional logic and block-based execution. ```ruby class Vehicle state_machine :state, initial: :parked do before_transition :log_transition_start after_transition do |vehicle, transition| puts "Transitioned from #{transition.from_name} to #{transition.to_name}" end around_transition do |vehicle, transition, block| start = Time.now block.call puts "Transition took #{Time.now - start} seconds" end after_failure on: :ignite, do: :log_start_failure end end ``` -------------------------------- ### Coordinating State Machines with Guards in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md Illustrates how to use state guards like :if_state and :unless_state to create dependencies between multiple state machines. This ensures transitions only occur when related machines are in specific states. ```ruby class TorpedoSystem state_machine :bay_doors, initial: :closed do event :open do transition closed: :open end end state_machine :torpedo_status, initial: :loaded do event :fire_torpedo do transition loaded: :fired, if_state: { bay_doors: :open } end end end ``` -------------------------------- ### Drawing State Machines in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Illustrates how to visualize state machines using the built-in STDIO renderer in Ruby. It shows how to output the diagram to the console or a specific stream, and mentions integration with graphviz for image formats. ```ruby class TrafficLight state_machine :color, initial: :red do event :change do transition red: :green, green: :yellow, yellow: :red end end end # Output state machine diagram to console TrafficLight.state_machine.draw # Output to specific stream TrafficLight.state_machine.draw(io: $stderr) # For graphviz integration (requires state_machines-graphviz gem) # TrafficLight.state_machine.draw(name: 'traffic_light', format: :png) ``` -------------------------------- ### Testing Multiple State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Illustrates how the TestHelper fully supports objects with multiple state machines, including testing states, transitions, event triggering, and callback definitions across different machines. ```ruby # Example: StarfleetShip with 3 state machines ship = StarfleetShip.new # Test states on different machines assert_sm_state(ship, :docked, machine_name: :status) # Main ship status assert_sm_state(ship, :down, machine_name: :shields) # Shield system assert_sm_state(ship, :standby, machine_name: :weapons) # Weapons system # Test transitions on specific machines assert_sm_transition(ship, :undock, :impulse, machine_name: :status) assert_sm_transition(ship, :raise_shields, :up, machine_name: :shields) assert_sm_transition(ship, :arm_weapons, :armed, machine_name: :weapons) # Test event triggering across multiple machines assert_sm_triggers_event(ship, :red_alert, machine_name: :status) do ship.engage_combat_mode # Custom method affecting multiple systems end assert_sm_triggers_event(ship, :raise_shields, machine_name: :shields) do ship.engage_combat_mode end # Test callback definitions on specific machines shields_machine = StarfleetShip.state_machine(:shields) assert_before_transition(shields_machine, from: :down, to: :up, do: :power_up_shields) ``` -------------------------------- ### Transition Object and Arguments in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Explains how to access transition details within callbacks and pass custom arguments to events for use in those callbacks. This allows for dynamic behavior based on event invocation. ```ruby class Vehicle state_machine :state, initial: :parked do before_transition do |vehicle, transition| puts "Event: #{transition.event}" puts "From: #{transition.from_name} (#{transition.from})" puts "To: #{transition.to_name} (#{transition.to})" puts "Loopback: #{transition.loopback?}" end after_transition on: :park do |vehicle, transition| # Access custom arguments passed to event meter_number = transition.args.first vehicle.parking_meter = meter_number if meter_number end event :ignite do transition parked: :idling end event :park do transition idling: :parked end end attr_accessor :parking_meter end vehicle = Vehicle.new vehicle.ignite # Pass arguments to event vehicle.park('METER-123') vehicle.parking_meter # => "METER-123" # Skip the action (useful for testing) vehicle.ignite(false) # Don't run the machine's action vehicle.ignite(run_action: false) # Same with keyword argument ``` -------------------------------- ### Define States and State-Specific Behavior in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Demonstrates how to configure states with custom values, human-readable names, and dynamic behaviors. It shows how to scope methods to specific states and use state queries. ```ruby class Vehicle state_machine :state, initial: :parked do state :parked, value: 'P' state :idling, value: 'I' state :first_gear, value: '1' state :purchased, value: -> { Time.now }, if: ->(value) { !value.nil? } state :first_gear, human_name: 'First Gear' state :parked do def speed; 0; end def honk; "Light honk"; end end state :idling, :first_gear do def speed; 10; end def honk; "HONK!"; end end event :ignite do transition parked: :idling end end end ``` -------------------------------- ### Apply Transition Conditions and Guards in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Shows how to restrict state transitions using :if and :unless options. Guards can be methods, procs, or lambdas to ensure complex business rules are met before allowing a transition. ```ruby class Vehicle state_machine :state, initial: :parked do event :ignite do transition parked: :idling, if: :seatbelt_on? transition parked: :idling, if: ->(v) { v.fuel_level > 0 } transition parked: :idling, unless: :engine_overheated? transition parked: :idling, if: [:seatbelt_on?, :has_fuel?], unless: :engine_overheated? end end end ``` -------------------------------- ### Define and Trigger State Machine Events Source: https://context7.com/state-machines/state_machines/llms.txt Illustrates advanced event definitions including conditional transitions, multiple source states, and loopbacks. It also demonstrates the generated instance methods for checking, firing, and inspecting transitions. ```ruby class Order state_machine :status, initial: :pending do event :submit do transition pending: :submitted end event :process do transition submitted: :processing, if: :payment_received? transition submitted: :awaiting_payment end event :cancel do transition [:pending, :submitted, :awaiting_payment] => :cancelled end event :archive do transition all - [:cancelled, :archived] => :archived end event :refresh do transition processing: same end end def payment_received? @payment_received end end order = Order.new order.can_submit? # => true order.submit_transition # => # order.submit # => true (fires event) order.submit! # => raises InvalidTransition if fails order.fire_state_event(:cancel) # => true (generic event firing) ``` -------------------------------- ### Path Analysis in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Details how to analyze possible transition paths from the current state using the State Machines gem in Ruby. It covers retrieving all possible paths and finding specific paths between states. ```ruby class Vehicle state_machine :state, initial: :parked do event :ignite do transition parked: :idling end event :shift_up do transition idling: :first_gear, first_gear: :second_gear end event :shift_down do transition second_gear: :first_gear, first_gear: :idling end event :park do transition idling: :parked end end end vehicle = Vehicle.new vehicle.state_events # => [:ignite] vehicle.state_transitions # => [#] vehicle.ignite vehicle.state_events # => [:shift_up, :park] # Get all possible paths paths = vehicle.state_paths paths.to_states # => [:first_gear, :second_gear, :parked] paths.events # => [:shift_up, :shift_down, :park] # Find specific paths vehicle.state_paths(from: :idling, to: :second_gear) # => [[ignite -> shift_up]] ``` -------------------------------- ### Multiple State Machines with Namespaces in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Demonstrates how a single Ruby class can manage multiple independent state machines. Namespaces are used to prevent method name collisions between different state machines, ensuring clarity and preventing conflicts. ```ruby class Vehicle state_machine :state, initial: :parked do event :ignite do transition parked: :idling end end # Namespaced state machine state_machine :alarm_state, initial: :active, namespace: 'alarm' do event :enable do transition all => :active end event :disable do transition all => :off end state :active, value: 1 state :off, value: 0 end state_machine :insurance_state, initial: :inactive, namespace: 'insurance' do event :buy do transition inactive: :active end event :cancel do transition active: :inactive end end end vehicle = Vehicle.new vehicle.state # => "parked" vehicle.alarm_state # => 1 vehicle.alarm_state_name # => :active # Namespaced methods vehicle.can_disable_alarm? # => true vehicle.disable_alarm # => true vehicle.alarm_off? # => true vehicle.alarm_active? # => false vehicle.can_enable_alarm? # => true vehicle.enable_alarm # => true # Fire events in parallel vehicle.fire_events(:ignite, :enable_alarm) # => true ``` -------------------------------- ### Test state persistence in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates the use of test helpers to verify that state machine states are correctly persisted across different testing frameworks like Minitest and RSpec. ```ruby assert_sm_state_persisted(ship, "impulse", :status) assert_sm_state_persisted(ship, "up", :shields) assert_sm_state_persisted(ship, "armed", :weapons) ``` -------------------------------- ### Configure Asynchronous State Machines in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md Demonstrates how to enable asynchronous mode in a state machine by setting the async flag. This allows for non-blocking event transitions within an Async context. ```ruby class AutonomousDrone < StarfleetShip state_machine :status, async: true, initial: :docked do event :launch do transition docked: :flying end event :land do transition flying: :docked end end end ``` -------------------------------- ### Define Transitions within State Context Source: https://github.com/state-machines/state_machines/blob/master/README.md Shows how to define transitions scoped to specific states, allowing for inferred 'from' states and cleaner code structure. ```ruby class Vehicle state_machine initial: :parked do state :parked do transition to: :idling, :on => [:ignite, :shift_up], if: :seatbelt_on? def speed; 0; end end state :first_gear do transition to: :second_gear, on: :shift_up def speed; 10; end end state :idling, :first_gear do transition to: :parked, on: :park end end end ``` -------------------------------- ### Coordinated State Management with Guards in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Illustrates how state machines can coordinate transitions based on the state of other state machines using state guards. This allows for complex conditional logic, ensuring transitions only occur when specific conditions across multiple machines are met. ```ruby class TorpedoSystem state_machine :bay_doors, initial: :closed do event :open do transition closed: :open end event :close do transition open: :closed end end state_machine :torpedo_status, initial: :loaded do event :fire_torpedo do # Can only fire if bay doors are open transition loaded: :fired, if_state: { bay_doors: :open } end event :reload do # Can only reload if bay doors are closed transition fired: :loaded, unless_state: { bay_doors: :open } end end end system = TorpedoSystem.new system.fire_torpedo # => false (bay doors closed) system.open system.fire_torpedo # => true (bay doors now open) # Multiple state guards class StarshipBridge state_machine :shields, initial: :down do event :raise_shields do transition down: :up end end state_machine :weapons, initial: :offline do event :arm do transition offline: :armed end end state_machine :alert_status, initial: :green do event :red_alert do # If ANY critical system needs attention transition green: :red, if_any_state: { warp_core: :critical, shields: :down } end event :battle_stations do # Only if ALL combat systems are ready transition green: :battle, if_all_states: { shields: :up, weapons: :armed } end end end ``` -------------------------------- ### Define Global Transitions Source: https://github.com/state-machines/state_machines/blob/master/README.md Illustrates defining transitions outside of specific state or event blocks, useful for machines built from external data sources. ```ruby class Vehicle state_machine initial: :parked do transition parked: :idling, :on => [:ignite, :shift_up] transition first_gear: :second_gear, second_gear: :third_gear, on: :shift_up transition [:idling, :first_gear] => :parked, on: :park transition all - [:parked, :stalled]: :stalled, unless: :auto_shop_busy? end end ``` -------------------------------- ### Implement explicit and implicit state transitions Source: https://github.com/state-machines/state_machines/blob/master/README.md Compares explicit method calls versus implicit attribute assignment for triggering state transitions in ActiveRecord models. ```ruby class Vehicle < ActiveRecord::Base state_machine initial: :parked do event :ignite do transition parked: :idling end end end # Explicit vehicle = Vehicle.create vehicle.ignite # Implicit vehicle = Vehicle.create vehicle.state_event = 'ignite' vehicle.save ``` -------------------------------- ### Configure state and event type consistency Source: https://github.com/state-machines/state_machines/blob/master/README.md Illustrates the requirement for consistent data types (symbols vs strings) when defining state machines and how to store states as symbols. ```ruby # Consistent definition class Vehicle state_machine initial: :parked do event :ignite do transition parked: :idling end end end # Storing states as symbols class Vehicle state_machine initial: :parked do event :ignite do transition parked: :idling end states.each do |state| self.state(state.name, :value => state.name.to_sym) end end end ``` -------------------------------- ### Define Dependencies for Async State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Configuration for the Gemfile to include necessary async and concurrent-ruby dependencies, scoped to supported MRI Ruby platforms. ```ruby platform :ruby do gem 'async', '>= 2.25.0' gem 'concurrent-ruby', '>= 1.3.5' end ``` -------------------------------- ### Visualize State Machines in Console Source: https://github.com/state-machines/state_machines/blob/master/README.md Uses the built-in STDIORenderer to output a visual representation of the state machine to the console or a specified IO stream. ```ruby Vehicle.state_machine.draw # Outputs to console Vehicle.state_machine.draw(io: $stderr) # Outputs to stderr ``` -------------------------------- ### Implementing Multiple State Guards in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md Shows the use of aggregate state guards such as :if_all_states and :if_any_state to manage complex logic across multiple interdependent state machines. ```ruby state_machine :alert_status, initial: :green do event :red_alert do transition green: :red, if_any_state: { warp_core: :critical, shields: :down } end event :battle_stations do transition green: :battle, if_all_states: { shields: :up, weapons: :armed } end end ``` -------------------------------- ### Create dynamic state machines in Ruby Source: https://github.com/state-machines/state_machines/blob/master/README.md This snippet demonstrates how to wrap an object with a dynamically generated state machine class. It uses class_eval to delegate state attributes and actions to the underlying object, allowing for flexible state management. ```ruby class Machine def self.new(object, *args, &block) machine_class = Class.new machine = machine_class.state_machine(*args, &block) attribute = machine.attribute action = machine.action # Delegate attributes machine_class.class_eval do define_method(:definition) { machine } define_method(attribute) { object.send(attribute) } define_method("#{attribute}=") {|value| object.send("#{attribute}=", value) } define_method(action) { object.send(action) } if action end machine_class.new end end ``` -------------------------------- ### Extended State Machine Assertions Source: https://github.com/state-machines/state_machines/blob/master/README.md Details extended assertions for state machine configuration, event behavior, and persistence. Includes checking state lists, initial states, event triggers, and persisted states. ```ruby machine = Vehicle.state_machine(:state) vehicle = Vehicle.new # State configuration assert_sm_states_list machine, [:parked, :idling, :stalled] assert_sm_initial_state machine, :parked # Event behavior assert_sm_event_triggers vehicle, :ignite refute_sm_event_triggers vehicle, :shift_up assert_sm_event_raises_error vehicle, :invalid_event, StateMachines::InvalidTransition # Persistence (with ActiveRecord integration) assert_sm_state_persisted record, expected: :active ``` -------------------------------- ### Asynchronous State Machines with Async Gem in Ruby Source: https://context7.com/state-machines/state_machines/llms.txt Enables asynchronous state machine operations for I/O-bound tasks in high-performance Ruby applications. Requires Ruby 3.2+ and the 'async' gem. Demonstrates async event firing, strict error handling with bang methods, and thread-safe concurrent operations. ```ruby # Gemfile platform :ruby do gem 'async', '>= 2.25.0' gem 'concurrent-ruby', '>= 1.3.5' end class AutonomousDrone state_machine :status, async: true, initial: :docked do event :launch do transition docked: :flying end event :land do transition flying: :docked end end state_machine :teleporter, async: true, initial: :offline do event :power_up do transition offline: :charging end event :teleport do transition ready: :teleporting end end # Synchronous machine (weapons stay sync for safety) state_machine :weapons, initial: :disarmed do event :arm do transition disarmed: :armed end end end drone = AutonomousDrone.new # Async execution within Async context Async do # Async event firing - returns Async::Task task = drone.launch_async result = task.wait # => true # Bang methods for strict error handling drone.power_up_async! # => Async::Task (raises on failure) # Generic async event firing drone.fire_event_async(:teleport) end # Thread-safe concurrent operations threads = 10.times.map do Thread.new do Async do drone.launch_async.wait drone.land_async.wait end end end threads.each(&:join) ``` -------------------------------- ### Execute Asynchronous State Machine Events Source: https://github.com/state-machines/state_machines/blob/master/README.md Shows how to invoke generated async event methods. These methods must be called within an Async block and return an Async::Task. ```ruby Async do task = drone.launch_async result = task.wait drone.power_up_async! drone.fire_event_async(:teleport) end ``` -------------------------------- ### Ensure Thread Safety in Async State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Illustrates how to perform concurrent operations safely using the built-in thread-safe mechanisms provided by the library. ```ruby threads = [] 10.times do threads << Thread.new do Async do drone.launch_async.wait drone.land_async.wait end end end threads.each(&:join) ``` -------------------------------- ### Ruby State Machine Class Definition Source: https://github.com/state-machines/state_machines/blob/master/README.md Defines a Vehicle class with a state machine for its primary state and another for its alarm state. It includes various callbacks, events, transitions, and state definitions. Ensure `super()` is called in `initialize` for proper state machine initialization. ```ruby class Vehicle attr_accessor :seatbelt_on, :time_used, :auto_shop_busy, :parking_meter_number state_machine :state, initial: :parked do before_transition parked: any - :parked, do: :put_on_seatbelt after_transition on: :crash, do: :tow after_transition on: :repair, do: :fix after_transition any => :parked do |vehicle, transition| vehicle.seatbelt_on = false end after_failure on: :ignite, do: :log_start_failure around_transition do |vehicle, transition, block| start = Time.now block.call vehicle.time_used += Time.now - start end event :park do transition [:idling, :first_gear] => :parked end before_transition on: :park do |vehicle, transition| # If using Rails: # options = transition.args.extract_options! options = transition.args.last.is_a?(Hash) ? transition.args.pop : {} meter_number = options[:meter_number] unless meter_number.nil? vehicle.parking_meter_number = meter_number end end event :ignite do transition stalled: same, parked: :idling end event :idle do transition first_gear: :idling end event :shift_up do transition idling: :first_gear, first_gear: :second_gear, second_gear: :third_gear end event :shift_down do transition third_gear: :second_gear, second_gear: :first_gear end event :crash do transition all - [:parked, :stalled] => :stalled, if: ->(vehicle) {!vehicle.passed_inspection?} end event :repair do # The first transition that matches the state and passes its conditions # will be used transition stalled: :parked, unless: :auto_shop_busy transition stalled: same end state :parked do def speed 0 end end state :idling, :first_gear do def speed 10 end end state all - [:parked, :stalled, :idling] do def moving? true end end state :parked, :stalled, :idling do def moving? false end end end state_machine :alarm_state, initial: :active, namespace: :'alarm' do event :enable do transition all => :active end event :disable do transition all => :off end state :active, :value => 1 state :off, :value => 0 end def initialize @seatbelt_on = false @time_used = 0 @auto_shop_busy = true @parking_meter_number = nil super() # NOTE: This *must* be called, otherwise states won't get initialized end def put_on_seatbelt @seatbelt_on = true end def passed_inspection? false end def tow # tow the vehicle end def fix # get the vehicle fixed by a mechanic end def log_start_failure # log a failed attempt to start the vehicle end end ``` -------------------------------- ### Indirect Event Testing for State Machines Source: https://github.com/state-machines/state_machines/blob/master/README.md Shows how to test if custom methods indirectly trigger specific state machine events using Minitest and RSpec styles. Supports testing single or multiple events and specific state machines. ```ruby # Minitest style vehicle = Vehicle.new vehicle.ignite # Put in idling state # Test that a custom method triggers a specific event assert_sm_triggers_event(vehicle, :crash) do vehicle.redline # Custom method that calls crash! internally end # Test multiple events assert_sm_triggers_event(vehicle, [:crash, :emergency]) do vehicle.emergency_stop end # Test on specific state machine (multi-FSM support) assert_sm_triggers_event(vehicle, :disable, machine_name: :alarm) do vehicle.turn_off_alarm end ``` ```ruby # RSpec style (coming soon with proper matcher support) RSpec.describe Vehicle do include StateMachines::TestHelper it "triggers crash when redlining" do vehicle = Vehicle.new vehicle.ignite expect_to_trigger_event(vehicle, :crash) do vehicle.redline end end end ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.