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 +4 -4
- data/README.md +85 -0
- data/lib/lab42/state_machine/controller.rb +15 -16
- data/lib/lab42/state_machine/version.rb +1 -1
- data/lib/lab42/state_machine.rb +14 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40cee82db2d88428e2e52f115faa98ccb571020d
|
4
|
+
data.tar.gz: 8a716c44053c53ebbdc2aabfa590c8c9f0543aab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
-
@
|
95
|
-
@
|
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
|
data/lib/lab42/state_machine.rb
CHANGED
@@ -6,15 +6,22 @@ module Lab42
|
|
6
6
|
extend Forwarder
|
7
7
|
attr_accessor :subject
|
8
8
|
|
9
|
-
forward_all :after, :before, :setup, :state, :
|
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 =
|
13
|
-
|
14
|
-
|
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
|
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.
|
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-
|
11
|
+
date: 2013-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: forwarder19
|