Class: StateFu::Lathe
Overview
A Lathe parses and a Machine definition and returns a freshly turned Machine.
It provides the means to define the arrangement of StateFu objects ( eg States and Events) which comprise a workflow, process, lifecycle, circuit, syntax, etc.
Instance Attribute Summary collapse
-
#machine ⇒ Object
readonly
or contain a State or Event (a child lathe for a nested block).
-
#options ⇒ Object
readonly
or contain a State or Event (a child lathe for a nested block).
-
#state_or_event ⇒ Object
(also: #context)
readonly
or contain a State or Event (a child lathe for a nested block).
Instance Method Summary collapse
- #accepted(*a, &b) ⇒ Object (also: #on_change)
- #after(*a, &b) ⇒ Object
- #after_all(*a) ⇒ Object
- #after_everything ⇒ Object
-
#before(*a, &b) ⇒ Object
Bunch of silly little methods for defining events :nodoc.
- #before_all(*a, &b) ⇒ Object (also: #before_everything)
-
#chain(string) ⇒ Object
define chained events and states succinctly usage: chain ‘state1 -event1-> state2 -event2-> state3’.
-
#connect_states(*array) ⇒ Object
(also: #connect)
chain_states :a => [:b,:c], :c => :d, :c => :d chain_states :a,:b,:c,:d, :a => :c.
- #context_event ⇒ Object
- #context_state ⇒ Object
-
#cycle(name = nil, options = {}, &block) ⇒ Object
create an event from and to the current state.
-
#define(method_name, &block) ⇒ Object
(also: #named_proc)
define a named proc.
-
#event(name, options = {}, &block) ⇒ Object
Defines an event.
-
#events(*args, &block) ⇒ Object
(also: #all_events, #each_event)
Define a series of events at once, or return and iterate over all events yet defined.
- #execute(*a, &b) ⇒ Object
-
#from(*args, &block) ⇒ Object
set the origin state(s) of an event (or, given a hash of symbols / arrays of symbols, set both the origins and targets) from :my_origin from [:column_a, :column_b] from :eden => :armageddon from [:beginning, :prelogue] => [:ende, :prologue].
-
#helper(*modules) ⇒ Object
helpers are mixed into all binding / transition contexts.
-
#initial_state(*args, &block) ⇒ Object
define the initial_state (otherwise defaults to the first state mentioned).
-
#initialize(machine, state_or_event = nil, options = {}, &block) ⇒ Lathe
constructor
you don’t need to call this directly.
-
#master? ⇒ Boolean
is this the toplevel lathe for a machine?.
-
#master_lathe ⇒ Object
get the top level Lathe for the machine.
-
#nested? ⇒ Boolean
(also: #child?)
a ‘child’ lathe is created by apply_to, to deal with nested blocks for states / events ( which are state_or_events ).
- #on_entry(*a, &b) ⇒ Object
- #on_exit(*a, &b) ⇒ Object
-
#requires(*args, &block) ⇒ Object
(also: #guard, #must, #must_be, #needs)
define an event or state requirement.
-
#state(name, options = {}, &block) ⇒ Object
define a state; given a block, apply the block to a Lathe for the state.
- #state_event(name, options = {}, &block) ⇒ Object
-
#states(*args, &block) ⇒ Object
(also: #all_states, #each_state)
Define a series of states at once, or return and iterate over all states yet defined.
-
#to(*args, &block) ⇒ Object
set the target state(s) of an event to :destination to :target_a, :target_b to [:end, :finale, :intermission].
-
#tool(*modules, &block) ⇒ Object
(also: #extend_dsl)
helpers are mixed into all binding / transition contexts.
-
#transitions(options = {}) ⇒ Object
compatibility methods for activemodel state machine ##############.
- #will(*a, &b) ⇒ Object (also: #fire, #fires, #firing, #cause, #causes, #triggers, #trigger)
Constructor Details
#initialize(machine, state_or_event = nil, options = {}, &block) ⇒ Lathe
you don’t need to call this directly.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/lathe.rb', line 16 def initialize( machine, state_or_event = nil, ={}, &block ) @machine = machine @state_or_event = state_or_event @options = .symbolize_keys! # extend ourself with any previously defined tools machine.tools.inject_into( self ) if state_or_event state_or_event.apply!( ) end if block_given? if block.arity == 1 if state_or_event yield state_or_event else raise ArgumentError end else instance_eval( &block ) end end end |
Instance Attribute Details
#machine ⇒ Object (readonly)
or contain a State or Event (a child lathe for a nested block)
13 14 15 |
# File 'lib/lathe.rb', line 13 def machine @machine end |
#options ⇒ Object (readonly)
or contain a State or Event (a child lathe for a nested block)
13 14 15 |
# File 'lib/lathe.rb', line 13 def @options end |
#state_or_event ⇒ Object (readonly) Also known as: context
or contain a State or Event (a child lathe for a nested block)
13 14 15 |
# File 'lib/lathe.rb', line 13 def state_or_event @state_or_event end |
Instance Method Details
#accepted(*a, &b) ⇒ Object Also known as: on_change
367 |
# File 'lib/lathe.rb', line 367 def accepted *a, &b; valid_in_context State; define_hook :accepted, *a, &b; end |
#after(*a, &b) ⇒ Object
366 |
# File 'lib/lathe.rb', line 366 def after *a, &b; valid_in_context Event; define_hook :after, *a, &b; end |
#after_all(*a) ⇒ Object
370 |
# File 'lib/lathe.rb', line 370 def after_all *a, &b; valid_in_context nil; define_hook :after_all, *a, &b; end |
#after_everything ⇒ Object
372 |
# File 'lib/lathe.rb', line 372 def after_all *a, &b; valid_in_context nil; define_hook :after_all, *a, &b; end |
#before(*a, &b) ⇒ Object
Bunch of silly little methods for defining events :nodoc
362 |
# File 'lib/lathe.rb', line 362 def before *a, &b; valid_in_context Event; define_hook :before, *a, &b; end |
#before_all(*a, &b) ⇒ Object Also known as: before_everything
369 |
# File 'lib/lathe.rb', line 369 def before_all *a, &b; valid_in_context nil; define_hook :before_all, *a, &b; end |
#chain(string) ⇒ Object
define chained events and states succinctly usage: chain ‘state1 -event1-> state2 -event2-> state3’
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/lathe.rb', line 293 def chain string rx_word = /([a-zA-Z0-9_]+)/ rx_state = /^#{rx_word}$/ rx_event = /^(?:-|>)#{rx_word}-?>$/ previous = nil string.split.each do |chunk| case chunk when rx_state current = state $1 if previous.is_a? Event previous.to current end when rx_event current = event $1 if previous.is_a? State current.from previous end else raise ArgumentError, "'#{chunk}' is not a valid token" end previous = current end end |
#connect_states(*array) ⇒ Object Also known as: connect
chain_states :a => [:b,:c], :c => :d, :c => :d chain_states :a,:b,:c,:d, :a => :c
319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/lathe.rb', line 319 def connect_states *array array.flatten! hash = array..symbolize_keys! array.inject(nil) do |origin, target| state target if origin event "#{origin.to_sym}_to_#{target.to_sym}", :from => origin, :to => target end origin = target end hash.each do |origin, target| event "#{origin.to_sym}_to_#{target.to_sym}", :from => origin, :to => target end end |
#context_event ⇒ Object
67 68 69 |
# File 'lib/lathe.rb', line 67 def context_event state_or_event if state_or_event.is_a?(Event) end |
#context_state ⇒ Object
63 64 65 |
# File 'lib/lathe.rb', line 63 def context_state state_or_event if state_or_event.is_a?(State) end |
#cycle(name = nil, options = {}, &block) ⇒ Object
create an event from and to the current state. Creates a loop, useful (only) for hooking behaviours onto.
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/lathe.rb', line 223 def cycle name=nil, ={}, &block _state = nil if name.is_a?(Hash) && .empty? = name name = nil end if _state = .delete(:state) valid_unless_nested("when :state is supplied") else _state = state_or_event valid_in_context( State, "unless :state is supplied" ) end name ||= .delete :on name ||= "cycle_#{_state.to_sym}" evt = define_event( name, , &block ) evt.from _state evt.to _state evt end |
#define(method_name, &block) ⇒ Object Also known as: named_proc
define a named proc
261 262 263 |
# File 'lib/lathe.rb', line 261 def define method_name, &block machine.named_procs[method_name] = block end |
#event(name, options = {}, &block) ⇒ Object
Defines an event. Any options supplied will be added to the event, except :from and :to which are used to define the origin / target states. Successive invocations will update (not replace) previously defined events; origin / target states and options are always accumulated, not clobbered.
Several different styles of definition are available. Consult the specs / features for examples.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/lathe.rb', line 107 def event( name, ={}, &block ) .symbolize_keys! valid_in_context( State, nil ) if nested? && state_or_event.is_a?(State) # in state block targets = .delete(:to) || .delete(:transitions_to) evt = define_event( name, , &block ) evt.from state_or_event unless state_or_event.nil? evt.to( targets ) unless targets.nil? evt else # in master lathe origins = .delete( :from ) targets = .delete( :to ) || .delete(:transitions_to) evt = define_event( name, , &block ) evt.from origins unless origins.nil? evt.to targets unless targets.nil? evt end end |
#events(*args, &block) ⇒ Object Also known as: all_events, each_event
Define a series of events at once, or return and iterate over all events yet defined
352 353 354 355 |
# File 'lib/lathe.rb', line 352 def events *args, &block valid_in_context nil, State each_state_or_event 'event', *args, &block end |
#execute(*a, &b) ⇒ Object
364 |
# File 'lib/lathe.rb', line 364 def execute *a, &b; valid_in_context Event; define_hook :execute, *a, &b; end |
#from(*args, &block) ⇒ Object
set the origin state(s) of an event (or, given a hash of symbols / arrays of symbols, set both the origins and targets) from :my_origin from [:column_a, :column_b] from :eden => :armageddon from [:beginning, :prelogue] => [:ende, :prologue]
276 277 278 279 |
# File 'lib/lathe.rb', line 276 def from *args, &block valid_in_context Event state_or_event.from( *args, &block ) end |
#helper(*modules) ⇒ Object
helpers are mixed into all binding / transition contexts
77 78 79 |
# File 'lib/lathe.rb', line 77 def helper( *modules ) machine.helper *modules end |
#initial_state(*args, &block) ⇒ Object
define the initial_state (otherwise defaults to the first state mentioned)
249 250 251 252 |
# File 'lib/lathe.rb', line 249 def initial_state *args, &block valid_unless_nested() machine.initial_state= state( *args, &block) end |
#master? ⇒ Boolean
is this the toplevel lathe for a machine?
52 53 54 |
# File 'lib/lathe.rb', line 52 def master? !nested? end |
#master_lathe ⇒ Object
get the top level Lathe for the machine
57 58 59 |
# File 'lib/lathe.rb', line 57 def master_lathe machine.lathe end |
#nested? ⇒ Boolean Also known as: child?
a ‘child’ lathe is created by apply_to, to deal with nested blocks for states / events ( which are state_or_events )
46 47 48 |
# File 'lib/lathe.rb', line 46 def nested? !!state_or_event end |
#on_entry(*a, &b) ⇒ Object
365 |
# File 'lib/lathe.rb', line 365 def on_entry *a, &b; valid_in_context State; define_hook :entry, *a, &b; end |
#on_exit(*a, &b) ⇒ Object
363 |
# File 'lib/lathe.rb', line 363 def on_exit *a, &b; valid_in_context State; define_hook :exit, *a, &b; end |
#requires(*args, &block) ⇒ Object Also known as: guard, must, must_be, needs
define an event or state requirement. options:
:on => :entry|:exit|array (state only) - check requirement on state entry, exit or both?
default = :entry
:message => string|proc|proc_name_symbol - message to be returned on requirement failure.
if a proc or symbol (named proc identifier), evaluated at runtime; a proc should
take one argument, which is a StateFu::Transition.
:msg => alias for :message, for the morbidly terse
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/lathe.rb', line 188 def requires( *args, &block ) valid_in_context Event, State = args..symbolize_keys! .assert_valid_keys :on, :message, :msg names = args if block_given? && args.length > 1 raise ArgumentError.new("cannot supply a block for multiple requirements") end on = nil names.each do |name| raise ArgumentError.new(name.inspect) unless name.is_a?(Symbol) case state_or_event when State on ||= [(.delete(:on) || [:entry])].flatten state_or_event.entry_requirements << name if on.include?( :entry ) state_or_event.exit_requirements << name if on.include?( :exit ) when Event state_or_event.requirements << name end if block_given? machine.named_procs[name] = block end if msg = .delete(:message) || .delete(:msg) raise ArgumentError, msg.inspect unless [String, Symbol, Proc].include?(msg.class) machine.[name] = msg end end end |
#state(name, options = {}, &block) ⇒ Object
define a state; given a block, apply the block to a Lathe for the state
255 256 257 258 |
# File 'lib/lathe.rb', line 255 def state name, ={}, &block valid_unless_nested() define_state( name, , &block ) end |
#state_event(name, options = {}, &block) ⇒ Object
149 150 151 152 153 154 155 156 157 158 |
# File 'lib/lathe.rb', line 149 def state_event name, ={}, &block valid_in_context State .symbolize_keys! state = state_or_event targets = .delete(:to) || .delete(:transitions_to) evt = define_state_or_event( Event, state.own_events, name, , &block) evt.from state evt.to(targets) unless targets.nil? evt end |
#states(*args, &block) ⇒ Object Also known as: all_states, each_state
Define a series of states at once, or return and iterate over all states yet defined
states :a, :b, :c, :colour => “purple” states(:ALL) do
end
342 343 344 345 |
# File 'lib/lathe.rb', line 342 def states *args, &block valid_unless_nested() each_state_or_event 'state', *args, &block end |
#to(*args, &block) ⇒ Object
set the target state(s) of an event to :destination to :target_a, :target_b to [:end, :finale, :intermission]
285 286 287 288 |
# File 'lib/lathe.rb', line 285 def to *args, &block valid_in_context Event state_or_event.to( *args, &block ) end |
#tool(*modules, &block) ⇒ Object Also known as: extend_dsl
helpers are mixed into all binding / transition contexts
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/lathe.rb', line 82 def tool( *modules, &block ) machine.tool *modules if block_given? tool = Module.new tool.module_eval &block machine.tools << tool end # inject them into self for immediate use modules.flatten.extend( ToolArray ).inject_into( self ) end |
#transitions(options = {}) ⇒ Object
compatibility methods for activemodel state machine ##############
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/lathe.rb', line 127 def transitions(={}) valid_in_context(Event) .symbolize_keys! target = [:to] origins = [:from] hook = [:on_transition] evt = state_or_event if hook evt.lathe() { triggers hook } end # # TODO do some type checking # if origins && target evt.add_to_sequence origins, target end evt end |
#will(*a, &b) ⇒ Object Also known as: fire, fires, firing, cause, causes, triggers, trigger
378 379 380 381 382 383 384 385 386 |
# File 'lib/lathe.rb', line 378 def will *a, &b valid_in_context State, Event case state_or_event when State define_hook :entry, *a, &b when Event define_hook :execute, *a, &b end end |