lab42_state_machine 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9a37c4012f2237fa5f1091a526fdeeede4beae5
4
- data.tar.gz: df6284a690961697d3a57c592a1c2518966b44b4
3
+ metadata.gz: 40cee82db2d88428e2e52f115faa98ccb571020d
4
+ data.tar.gz: 8a716c44053c53ebbdc2aabfa590c8c9f0543aab
5
5
  SHA512:
6
- metadata.gz: d1c8f0bffc1f5a0b16104f742d99390cb159df8ba7f4a3b5377b39f8eae5c9df0b36cd9bf1778e3e4b2f4be69ee2235c5a33f2cb2e3cf568874ffdb8d36753d4
7
- data.tar.gz: 2671e4029d32fb74b8a7ad887ed28fcb0d61a8068024870984d9b1c4356dcbe7523b2345c6d2c90904d7f518c9a2ce8997e65db5a9c60e2d8073fdc314c7a105
6
+ metadata.gz: a88d6d79918585acd61f4fed0dd0274cbe61c22cf3fd4900d145fd5844dd36231e2f3ec6cf9cddf8ebc66402e238c363050ace782ae999b11421f943bf798c58
7
+ data.tar.gz: 9f7dc40f0c3a0cd3e887d9048b1574834d5cccdb85c2d81a778b9882fb823f3aa8df9a49f91cebc301a4c79046ed3c412480cd11215e32ac29af6f24feb59bde
data/README.md CHANGED
@@ -30,6 +30,17 @@ A counter
30
30
  end.run %w{a b c} # ===> 3 (self.subject)
31
31
  ```
32
32
 
33
+ This simple example demonstrates the following essential rules:
34
+
35
+ * An easy way to setup the State Machine is by providing a block to its constructor. This block
36
+ is instance evalled and thus has access to the whole API (see below). But of course the public API
37
+ methods can be accessed in a more detached style too:
38
+ `state\_machine.state :reset do @counter = 0 end`
39
+
40
+ * The initial state of the State Machine defaults to `:init`
41
+
42
+ The next example will demonstrate more API methods
43
+
33
44
  ```ruby
34
45
  SM = Lab42::StateMachine
35
46
 
@@ -73,3 +84,77 @@ A counter
73
84
  end.run [true, false, false, true, true, true, false]
74
85
  # ===> { true => [[0,1],[3,6]], false => [[1,3], [6,7]] }
75
86
  ```
87
+
88
+ In particular we are allowed to use instance variables (and their sexier accessor implementations) to transport
89
+ state between the states of the State Machine. This is without danger as the API hides all behind the controller
90
+ and does not contain *any* instance variable (not even `@controller`).
91
+
92
+ Of course it might be worthwile to consider closing over some local variables like in the following example if appropriate.
93
+
94
+ ```ruby
95
+ # Sometime is is preferble to use the State Machine with a state implementing object
96
+ result = SomeObject.new
97
+ StateMachine.new do
98
+ before do | input |
99
+ state = some_function_of input
100
+ end
101
+ transition a: :b do | input |
102
+ result.update from: :a, to: :b, with: :input
103
+ end
104
+ teardown do | last_input, old_state |
105
+ result.update from: old_state, to: result.end, with: last_input
106
+ end
107
+ end.run ...
108
+ ```
109
+
110
+ ## API
111
+
112
+ ### Setup Methods and Handler Definitions
113
+
114
+ The following methods can be used inside the constructor block without explicit revceiver, or just invoked on a `StateMachine` instance.
115
+
116
+ #### after
117
+
118
+ `after do | input, old_state, new_state |` will be run after each input record has been processed. As all handlers they will be executed
119
+ in order of definition.
120
+
121
+ #### before
122
+
123
+ `before do | input, old_state, new_state |` will be run before each input record has been processed. As all handlers they will be executed
124
+ in order of definition.
125
+
126
+
127
+ #### setup
128
+
129
+ `setup do |input|` The StateMachine peeks into the lazy enumerator and provides the first value (or nil for empty) as parameter. These handlers are the first to be run before any other and *before* the first input record will be
130
+ fetched. Yet they give access to the Runtime API, notably they allow to set the initial state to something else as `:init` by means of the one parameter form to the state call
131
+ ```state :new_initial_state```
132
+
133
+ #### state
134
+
135
+ This is the (overloaded - for the sake of a slim API) workerbee of the State Machine, it comes in three forms:
136
+
137
+ ##### state querying form (0 params)
138
+
139
+ Really part of the *Runtime API*
140
+
141
+ `state` currrent_state of the StateMachine
142
+
143
+ ##### state setter form (1 param)
144
+
145
+ Really part of the *Runtime API*
146
+
147
+ `state new_state` You'll never guess what this one does.
148
+
149
+ ##### state handler definition form (1 param and block)
150
+
151
+ `state some_state do |input, old_state, current_state| ...` These blocks will be called for each input record processed in some\_state
152
+
153
+ #### teardown
154
+
155
+ `teardown do | last_input, last_state |` Will be called at the very end of the processing cycle.
156
+
157
+
158
+ #### transition
159
+
160
+ `transition a: :b, b: :c do |input, old_state, new_state|` These are execute when the state changes from :a to :b, or :b to :a. Of course only one transition can be indicated (and mostly will be). These handlers are executed *after* the state handlers fot the new states, that is :b or :c in our case.
@@ -26,20 +26,16 @@ module Lab42
26
26
  run_state_blocks @befores
27
27
  end
28
28
 
29
- def run_setup_blocks blox
30
- blox.each do | block |
31
- @sm.instance_exec( @state, &block )
32
- end
33
- end
34
-
35
29
  def run_state_blocks blox
36
30
  blox.each do | block |
37
31
  @sm.instance_exec( @current_input, @old_state, @state, &block )
38
32
  end
39
33
  end
40
34
 
41
- def run_setups
42
- run_setup_blocks @setups
35
+ def run_setups input
36
+ @setups.each do | block |
37
+ @sm.instance_exec( input, &block )
38
+ end
43
39
  end
44
40
 
45
41
  def run_states
@@ -47,8 +43,10 @@ module Lab42
47
43
  end
48
44
 
49
45
 
50
- def run_teardowns
51
- run_setup_blocks @teardowns
46
+ def run_teardowns input
47
+ @teardowns.each do | teardown |
48
+ @sm.instance_exec( input, @state, &teardown )
49
+ end
52
50
  end
53
51
 
54
52
  def run_transitions
@@ -87,12 +85,13 @@ module Lab42
87
85
  @old_state = nil
88
86
  @state = :init
89
87
 
90
- @setups = []
91
- @teardowns = []
92
- @afters = []
93
- @befores = []
94
- @handlers = mk_ary_hash
95
- @transitions = mk_ary_hash
88
+ @setups = []
89
+ @teardowns = []
90
+ @afters = []
91
+ @befores = []
92
+ @before_first = []
93
+ @handlers = mk_ary_hash
94
+ @transitions = mk_ary_hash
96
95
  end
97
96
  end # class Controller
98
97
  end # class StateMachine
@@ -1,5 +1,5 @@
1
1
  module Lab42
2
2
  class StateMachine
3
- Version = "0.1.0"
3
+ Version = "0.1.1"
4
4
  end # class StateMachine
5
5
  end # module Lab42
@@ -6,15 +6,22 @@ module Lab42
6
6
  extend Forwarder
7
7
  attr_accessor :subject
8
8
 
9
- forward_all :after, :before, :setup, :state, :step, :teardown, :transition, to: :controller
9
+ forward_all :after, :before, :setup, :state, :teardown, :transition, to: :controller
10
+
11
+ def halt_machine
12
+ raise StopIteration, "#{self} halted"
13
+ end
10
14
 
11
15
  def run input=[]
12
- inputs = make_inputs input
13
- controller.run_setups
14
- inputs.each do | input |
16
+ inputs = __make_inputs__ input
17
+ input = nil
18
+
19
+ controller.run_setups( (inputs.peek rescue nil) )
20
+ loop do
21
+ input = inputs.next
15
22
  controller.run input
16
23
  end
17
- controller.run_teardowns
24
+ controller.run_teardowns input
18
25
  subject
19
26
  end
20
27
 
@@ -34,10 +41,10 @@ module Lab42
34
41
  end
35
42
  end
36
43
 
37
- def make_inputs input
44
+ def __make_inputs__ input
38
45
  case input
39
46
  when Enumerable, Enumerator
40
- input
47
+ input.lazy
41
48
  else
42
49
  raise ArgumentError, "cannot enumerate on object #{input}"
43
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lab42_state_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-09 00:00:00.000000000 Z
11
+ date: 2013-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: forwarder19