Module: Hegemon
- Defined in:
- lib/hegemon.rb
Instance Method Summary collapse
-
#block_until_state(s, throttle = 0) ⇒ Object
Sleep the current thread until the current state is equal to
state
. -
#declare_state(state, &block) ⇒ Object
Declare a state in the state machine.
-
#do_state_tasks(iter_num = 0) ⇒ Object
Perform all HegemonState#tasks associated with the current state.
-
#end_hegemon_auto_thread ⇒ Object
Raise a flag to stop the loop inside
hegemon_auto_thread
. -
#impose_state(s) ⇒ Object
Bypass all transition requirements and actions to directly impose state
state
as the current state. -
#join_hegemon_auto_thread ⇒ Object
Block until the
hegemon_auto_thread
is finished. -
#request_state(s, *flags) ⇒ Object
Request a transition from the current state to state
state
. -
#start_hegemon_auto_thread(throttle = nil) ⇒ Object
Run the
hegemon_auto_thread
if it is not already running. -
#state ⇒ Object
Return the current state (as a symbol).
-
#state_obj ⇒ Object
Return the current state (as a HegemonState object).
-
#state_objs ⇒ Object
Return the current state (as a Hash of HegemonState objects keyed by symbol).
-
#states ⇒ Object
Return the list of declared states (as an array of symbols).
-
#update_state(*flags) ⇒ Object
Check the list of possible transitions from the current state and perform the first transition that is found to be ready, if any.
Instance Method Details
#block_until_state(s, throttle = 0) ⇒ Object
Sleep the current thread until the current state is equal to state
state
-
The symbol to compare against the current state against
- throttle = 0
-
The amount of time to sleep between state checks, 0 by default.
189 190 191 192 193 194 195 196 |
# File 'lib/hegemon.rb', line 189 def block_until_state(s, throttle = 0); # :args: state, throttle = 0 raise ArgumentError, "Cannot block until undefined state :#{s}" \ unless @_hegemon_states.keys.include? s @_hegemon_transition_lock ||= Monitor.new sleep 0 until @_hegemon_transition_lock.synchronize { @_hegemon_state==s } nil end |
#declare_state(state, &block) ⇒ Object
Declare a state in the state machine.
Returns the HegemonState object created.
state
-
The state name to use, as a symbol
block
-
The state’s declarative block.
All code within gets evaluated not in its original binding, but in the context of a new HegemonState instance object.
For stable use, only call methods in this block that are listed in HegemonState@Declarative+Methods.
The state and associated state machine data and methods will be associated with the object referred to by self
object in the context in which the declarative method is called.
This means that in typical usage, the declarative methods listed in Hegemon@Declarative+Methods (including declare_state) should be called only from within an instance method, such as initialize
.
The following example creates a skeleton state machine in each MyStateMachine
instance object with two states: :working
and :idle
.
require 'hegemon'
class MyStateMachine
include Hegemon
def initialize
declare_state :working do
# various HegemonState Declarative Methods
end
declare_state :idle do
# various HegemonState Declarative Methods
end
end
end
67 68 69 70 |
# File 'lib/hegemon.rb', line 67 def declare_state(state, &block) @_hegemon_states ||= Hash.new @_hegemon_states[state] = HegemonState.new(self, state, &block) end |
#do_state_tasks(iter_num = 0) ⇒ Object
Perform all HegemonState#tasks associated with the current state.
Tasks are performed in the order they were declared in the HegemonState declarative block.
iter_num
= 0-
Specify the iteration number to be passed to the task block. When called by the
hegemon_auto_thread
(see start_hegemon_auto_thread), this number counts up from zero for each iteration of thehegemon_auto_thread
loop. If no value is specified,0
is used.
209 210 211 212 |
# File 'lib/hegemon.rb', line 209 def do_state_tasks(iter_num = 0) return nil unless @_hegemon_states[@_hegemon_state] @_hegemon_states[@_hegemon_state].do_tasks(iter_num) nil end |
#end_hegemon_auto_thread ⇒ Object
Raise a flag to stop the loop inside hegemon_auto_thread
.
270 271 272 273 274 |
# File 'lib/hegemon.rb', line 270 def end_hegemon_auto_thread @_end_hegemon_auto_thread = true @_hegemon_auto_thread.join unless @_hegemon_auto_thread==Thread.current @_hegemon_auto_thread = nil end |
#impose_state(s) ⇒ Object
Bypass all transition requirements and actions to directly impose state state
as the current state.
This should not be used in the public API except to set the initial state of the state machine, because state changes imposed by impose_state do not obey any rules of the state machine.
state
-
The state to impose, as a symbol
This method should be called in the same scope in which the state was declared with declare_state.
The following example creates a skeleton state machine in each MyStateMachine
instance object with two states: :working
and :idle
and sets :working
as the initial state.
require 'hegemon'
class MyStateMachine
include Hegemon
def initialize
impose_state :working
declare_state :working do
# various HegemonState Declarative Methods
end
declare_state :idle do
# various HegemonState Declarative Methods
end
end
end
107 108 109 |
# File 'lib/hegemon.rb', line 107 def impose_state(s) # :args: state @_hegemon_state = s nil end |
#join_hegemon_auto_thread ⇒ Object
Block until the hegemon_auto_thread
is finished.
263 264 265 |
# File 'lib/hegemon.rb', line 263 def join_hegemon_auto_thread @_hegemon_auto_thread.join if @_hegemon_auto_thread end |
#request_state(s, *flags) ⇒ Object
Request a transition from the current state to state state
.
Returns true
if the transition was performed, else returns false
.
state
-
The state to which transition is desired, as a symbol
flags
-
All subsequent arguments are interpreted as flags, and the only meaningful flag is
:force
(See transition requirements below).
In order for the transition to occur, the transition must have been defined (using HegemonState#transition_to), and the transition rules declared in the HegemonTransition declarative block have been suitably met by any one of the following situations:
-
All HegemonTransition#requirement blocks evaluate as
true
and the:force
flag was included inflags
. -
At least one HegemonTransition#sufficient block and all HegemonTransition#requirement blocks evaluate as
true
-
All HegemonTransition#condition blocks and all HegemonTransition#requirement blocks evaluate as
true
Note that evaluation of the rule blocks stops when a match is found, so rule blocks with code that has “side effects” are discouraged.
143 144 145 146 147 |
# File 'lib/hegemon.rb', line 143 def request_state(s, *flags) # :args: state, *flags return true if @_hegemon_state==s return false unless @_hegemon_states[@_hegemon_state].transitions[s] @_hegemon_states[@_hegemon_state].transitions[s].try(*flags) end |
#start_hegemon_auto_thread(throttle = nil) ⇒ Object
Run the hegemon_auto_thread
if it is not already running.
Returns the Thread object.
The hegemon_auto_thread
continually calls do_state_tasks and update_state, counting up from 0
the value passed to do_state_tasks, until the thread is stopped with end_hegemon_auto_thread.
- throttle = nil
-
The amount of time to sleep between iterations.
If left as nil, the last given throttle value is used. The default throttle value is 0.05 seconds.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/hegemon.rb', line 240 def start_hegemon_auto_thread(throttle = nil) if (not @_hegemon_auto_thread) \ or (not @_hegemon_auto_thread.status) @_end_hegemon_auto_thread = false @_hegemon_auto_thread_throttle ||= throttle @_hegemon_auto_thread_throttle ||= 0.05 @_hegemon_auto_thread = Thread.new do i = 0 until @_end_hegemon_auto_thread iter_hegemon_auto_loop(i) i += 1 sleep @_hegemon_auto_thread_throttle \ unless @_end_hegemon_auto_thread end end end @_hegemon_auto_thread end |
#state ⇒ Object
Return the current state (as a symbol)
12 |
# File 'lib/hegemon.rb', line 12 def state; @_hegemon_state; end |
#state_obj ⇒ Object
Return the current state (as a HegemonState object)
16 |
# File 'lib/hegemon.rb', line 16 def state_obj; @_hegemon_states[@_hegemon_state]; end |
#state_objs ⇒ Object
Return the current state (as a Hash of HegemonState objects keyed by symbol)
18 |
# File 'lib/hegemon.rb', line 18 def state_objs; @_hegemon_states.clone; end |
#states ⇒ Object
Return the list of declared states (as an array of symbols)
14 |
# File 'lib/hegemon.rb', line 14 def states; @_hegemon_states.keys; end |
#update_state(*flags) ⇒ Object
Check the list of possible transitions from the current state and perform the first transition that is found to be ready, if any.
Returns true
if the transition was performed, else returns false
.
flags
-
All subsequent arguments are interpreted as flags, and the only meaningful flag is
:only_auto
.Use of the
:only_auto
flag indicates that all state transitions which have disabled automatic updating with HegemonTransition#auto_update should be ignored.
In order for a transition to occur, the transition rules declared in the HegemonTransition declarative block have been suitably met by any one of the following situations:
-
All HegemonTransition#requirement blocks evaluate as
true
and the:force
flag was included inflags
. -
At least one HegemonTransition#sufficient block and all HegemonTransition#requirement blocks evaluate as
true
-
All HegemonTransition#condition blocks and all HegemonTransition#requirement blocks evaluate as
true
172 173 174 175 176 177 |
# File 'lib/hegemon.rb', line 172 def update_state(*flags) return false unless @_hegemon_states[@_hegemon_state] trans = @_hegemon_states[@_hegemon_state].transitions trans = trans.select{|k,t| t.auto_update} if (flags.include? :only_auto) trans.each {|k,t| return true if t.try} false end |