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 +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
|