lab42_state_machine 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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