lib/simply_fsm.rb in simply_fsm-0.2.1 vs lib/simply_fsm.rb in simply_fsm-0.2.3
- old
+ new
@@ -1,45 +1,50 @@
# frozen_string_literal: true
require "simply_fsm/version"
-##
-# Defines the `SimplyFSM` module
+#
+# Include *SimplyFSM* in a class to be able to defined state machines.
+#
module SimplyFSM
+ #
+ # Provides a +state_machine+ for the including class.
def self.included(base)
base.extend(ClassMethods)
end
- def state_match?(from, current)
- return true if from == :any
- return from.include?(current) if from.is_a?(Array)
-
- from == current
- end
-
- def cannot_transition?(from, cond, current)
- (from && !state_match?(from, current)) || (cond && !instance_exec(&cond))
- end
-
- ##
+ #
# Defines the constructor for defining a state machine
module ClassMethods
- ##
+ #
# Declare a state machine called +name+ which can then be defined
- # by a DSL defined by the methods of `StateMachine`, with the following +opts+:
- # - an optional +fail+ lambda that is called when any event fails to transition)
+ # by a DSL defined by the methods of *StateMachine*.
+ #
+ # @param [String] name of the state machine.
+ # @param [Hash] opts to specify options such as:
+ # - +fail+ lambda that is called with the event name when any event fails to transition
+ #
def state_machine(name, opts = {}, &block)
fsm = StateMachine.new(name, self, fail: opts[:fail])
fsm.instance_eval(&block)
end
end
##
- # The DSL for defining a state machine
+ # The DSL for defining a state machine. These methods are used within the declaration of a +state_machine+.
+ #
+ # @attr_reader [String] initial_state The initial state of the state machine
+ # @attr_reader [Array] states All the states of the state machine
+ # @attr_reader [Array] events All the events of the state machine
+ # @attr_reader [String] name
+ # @attr_reader [String] full_name The name of the owning class combined with the state machine's name
+ #
class StateMachine
attr_reader :initial_state, :states, :events, :name, :full_name
+ #
+ # @!visibility private
def initialize(name, owner_class, fail: nil)
@owner_class = owner_class
@name = name.to_sym
@full_name = "#{owner_class.name}/#{name}"
@states = []
@@ -48,12 +53,16 @@
@fail_handler = fail
setup_base_methods
end
- ##
+ #
# Declare a supported +state_name+, and optionally specify one as the +initial+ state.
+ #
+ # @param [String] state_name
+ # @param [Boolean] initial to indicate if this is the initial state of the state machine
+ #
def state(state_name, initial: false)
return if state_name.nil? || @states.include?(state_name)
status = state_name.to_sym
state_machine_name = @name
@@ -66,14 +75,15 @@
end
##
# Define an event by +event_name+
#
- # - which +transitions+ as a hash with a +from+ state or array of states and the +to+ state,
- # - an optional +guard+ lambda which must return true for the transition to occur,
- # - an optional +fail+ lambda that is called when the transition fails (overrides top-level fail handler), and
- # - an optional do block that is called +after+ the transition succeeds
+ # @param [String] event_name
+ # @param [Hash,Array] transitions either one (Hash) or many (Array of Hashes) transitions +from+ one state +to+ another state.
+ # @param [Lambda] guard if specified must return +true+ before any transitions are attempted
+ # @param [Lambda] fail called with event name if specified when all the attempted transitions fail
+ # @yield when the transition attempt succeeds.
def event(event_name, transitions:, guard: nil, fail: nil, &after)
return unless event_exists?(event_name) && transitions
@events << event_name
may_event_name = "may_#{event_name}?"
@@ -117,11 +127,11 @@
return unless fail
if fail.is_a?(String) || fail.is_a?(Symbol)
->(event_name) { send(fail, event_name) }
else
- ->(event_name) { instance_exec(event_name, &fail) }
+ fail
end
end
def setup_multi_transition_event_method(event_name, transitions:, guard:, var_name:, fail:)
state_machine_name = @name
@@ -251,7 +261,20 @@
end
def make_owner_method(method_name, method_definition)
@owner_class.define_method(method_name, method_definition)
end
+ end
+
+ private
+
+ def state_match?(from, current)
+ return true if from == :any
+ return from.include?(current) if from.is_a?(Array)
+
+ from == current
+ end
+
+ def cannot_transition?(from, cond, current)
+ (from && !state_match?(from, current)) || (cond && !instance_exec(&cond))
end
end