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
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.
-
#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 [:end, :finale, :intermission].
-
#tool(*modules) ⇒ Object
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 = .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 end |
#state_or_event ⇒ 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 state_or_event @state_or_event end |
Instance Method Details
#accepted(*a, &b) ⇒ Object Also known as: on_change
352 |
# File 'lib/lathe.rb', line 352 def accepted *a, &b; valid_in_context State; define_hook :accepted, *a, &b; end |
#after(*a, &b) ⇒ Object
351 |
# File 'lib/lathe.rb', line 351 def after *a, &b; valid_in_context Event; define_hook :after, *a, &b; end |
#after_all(*a) ⇒ Object
355 |
# File 'lib/lathe.rb', line 355 def after_all *a, &b; valid_in_context nil; define_hook :after_all, *a, &b; end |
#after_everything ⇒ Object
357 |
# File 'lib/lathe.rb', line 357 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
347 |
# File 'lib/lathe.rb', line 347 def before *a, &b; valid_in_context Event; define_hook :before, *a, &b; end |
#before_all(*a, &b) ⇒ Object Also known as: before_everything
354 |
# File 'lib/lathe.rb', line 354 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’
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/lathe.rb', line 278 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
304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/lathe.rb', line 304 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 |
#cycle(name = nil, options = {}, &block) ⇒ Object
create an event from and to the current state. Creates a loop, useful (only) for hooking behaviours onto.
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/lathe.rb', line 209 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
247 248 249 |
# File 'lib/lathe.rb', line 247 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.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/lathe.rb', line 90 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
337 338 339 340 |
# File 'lib/lathe.rb', line 337 def events *args, &block valid_in_context nil, State each_state_or_event 'event', *args, &block end |
#execute(*a, &b) ⇒ Object
349 |
# File 'lib/lathe.rb', line 349 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]
262 263 264 265 |
# File 'lib/lathe.rb', line 262 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
66 67 68 |
# File 'lib/lathe.rb', line 66 def helper( *modules ) machine.helper *modules end |
#initial_state(*args, &block) ⇒ Object
define the initial_state (otherwise defaults to the first state mentioned)
235 236 237 238 |
# File 'lib/lathe.rb', line 235 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
350 |
# File 'lib/lathe.rb', line 350 def on_entry *a, &b; valid_in_context State; define_hook :entry, *a, &b; end |
#on_exit(*a, &b) ⇒ Object
348 |
# File 'lib/lathe.rb', line 348 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 - 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
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/lathe.rb', line 174 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
241 242 243 244 |
# File 'lib/lathe.rb', line 241 def state name, ={}, &block valid_unless_nested() define_state( name, , &block ) end |
#state_event(name, options = {}, &block) ⇒ Object
132 133 134 135 136 137 138 139 140 141 |
# File 'lib/lathe.rb', line 132 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
327 328 329 330 |
# File 'lib/lathe.rb', line 327 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 [:end, :finale, :intermission]
270 271 272 273 |
# File 'lib/lathe.rb', line 270 def to *args, &block valid_in_context Event state_or_event.to( *args, &block ) end |
#tool(*modules) ⇒ Object
helpers are mixed into all binding / transition contexts
71 72 73 74 75 |
# File 'lib/lathe.rb', line 71 def tool( *modules ) machine.tool *modules # inject them into self for immediate use modules.flatten.extend( ToolArray ).inject_into( self ) end |
#transitions(options = {}) ⇒ Object
compatibility methods for activemodel state machine ##############
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/lathe.rb', line 110 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
363 364 365 366 367 368 369 370 371 |
# File 'lib/lathe.rb', line 363 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 |