lab42_state_machine 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +75 -0
- data/lib/lab42/state_machine/controller.rb +99 -0
- data/lib/lab42/state_machine/version.rb +5 -0
- data/lib/lab42/state_machine.rb +47 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9a37c4012f2237fa5f1091a526fdeeede4beae5
|
4
|
+
data.tar.gz: df6284a690961697d3a57c592a1c2518966b44b4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1c8f0bffc1f5a0b16104f742d99390cb159df8ba7f4a3b5377b39f8eae5c9df0b36cd9bf1778e3e4b2f4be69ee2235c5a33f2cb2e3cf568874ffdb8d36753d4
|
7
|
+
data.tar.gz: 2671e4029d32fb74b8a7ad887ed28fcb0d61a8068024870984d9b1c4356dcbe7523b2345c6d2c90904d7f518c9a2ce8997e65db5a9c60e2d8073fdc314c7a105
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Robert Dober
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# lab42_state_machine
|
2
|
+
|
3
|
+
A simple State Machine
|
4
|
+
|
5
|
+
## Design Principles
|
6
|
+
|
7
|
+
* A minimalistic but powerful API
|
8
|
+
|
9
|
+
* Protection of the SM's inner state through delegation from the API to the implementation
|
10
|
+
|
11
|
+
* Run SM against Enumerables or Enumerators
|
12
|
+
|
13
|
+
* Define a `subject` in the SM's contsructor and run will return ths `subject`, defaulting to nil, in case you are aiming for side effects, but you are surely not, right ;)?
|
14
|
+
|
15
|
+
|
16
|
+
## Examples
|
17
|
+
|
18
|
+
See also spec/acceptance/readme\_spec.rb
|
19
|
+
|
20
|
+
|
21
|
+
A counter
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'lab42/state_machine'
|
25
|
+
|
26
|
+
Lab42::StateMachine.new 0 do # self.subject <-- 0
|
27
|
+
state :init do
|
28
|
+
self.subject += 1
|
29
|
+
end
|
30
|
+
end.run %w{a b c} # ===> 3 (self.subject)
|
31
|
+
```
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
SM = Lab42::StateMachine
|
35
|
+
|
36
|
+
SM.new do
|
37
|
+
# Before anything is run
|
38
|
+
setup do
|
39
|
+
@index = 0
|
40
|
+
self.subject = Hash.new{ |h, k| h[k]=[] }
|
41
|
+
end
|
42
|
+
# After each transition
|
43
|
+
after do
|
44
|
+
@index += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
# Before each transition
|
48
|
+
before do | input |
|
49
|
+
state input # sets state to input {true, false} in our case
|
50
|
+
end
|
51
|
+
|
52
|
+
# In this case input and new_state are known to be true of course
|
53
|
+
transition init: true, false => true do | input, old_state, new_state |
|
54
|
+
subject[true] << [@index]
|
55
|
+
subject[false].last << @index if subject[false].last
|
56
|
+
end
|
57
|
+
|
58
|
+
# We show a different variation for the to false transitions here
|
59
|
+
# much more elegant IMHO
|
60
|
+
transition init: false do
|
61
|
+
subject[false] << [0]
|
62
|
+
end
|
63
|
+
transition true => false do
|
64
|
+
subject[false] << [@index]
|
65
|
+
subject[true].last << @index
|
66
|
+
end
|
67
|
+
|
68
|
+
# There is no transition towards the end state (yet?).
|
69
|
+
teardown do | last_state |
|
70
|
+
subject[last_state].last << @index.succ if subject[last_state].last # Take care of empty input here
|
71
|
+
end
|
72
|
+
|
73
|
+
end.run [true, false, false, true, true, true, false]
|
74
|
+
# ===> { true => [[0,1],[3,6]], false => [[1,3], [6,7]] }
|
75
|
+
```
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Lab42
|
2
|
+
class StateMachine
|
3
|
+
class Controller
|
4
|
+
|
5
|
+
def after &block
|
6
|
+
@afters << block
|
7
|
+
end
|
8
|
+
|
9
|
+
def before &block
|
10
|
+
@befores << block
|
11
|
+
end
|
12
|
+
|
13
|
+
def run input
|
14
|
+
@current_input = input
|
15
|
+
run_befores
|
16
|
+
run_states
|
17
|
+
run_transitions
|
18
|
+
run_afters
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_afters
|
22
|
+
run_state_blocks @afters
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_befores
|
26
|
+
run_state_blocks @befores
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_setup_blocks blox
|
30
|
+
blox.each do | block |
|
31
|
+
@sm.instance_exec( @state, &block )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_state_blocks blox
|
36
|
+
blox.each do | block |
|
37
|
+
@sm.instance_exec( @current_input, @old_state, @state, &block )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_setups
|
42
|
+
run_setup_blocks @setups
|
43
|
+
end
|
44
|
+
|
45
|
+
def run_states
|
46
|
+
run_state_blocks @handlers[@state]
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def run_teardowns
|
51
|
+
run_setup_blocks @teardowns
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_transitions
|
55
|
+
run_state_blocks @transitions[current_transition]
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_state! st
|
59
|
+
@old_state = @state
|
60
|
+
@state = st
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup █ @setups << block end
|
64
|
+
|
65
|
+
def state *args, &block
|
66
|
+
raise ArgumentError, "args.size = #{args.size} instead of 0..1" if args.size > 1
|
67
|
+
return @state if block.nil? && args.empty?
|
68
|
+
return set_state! args.first unless block
|
69
|
+
@handlers[ args.first ] << block
|
70
|
+
end
|
71
|
+
|
72
|
+
def teardown █ @teardowns << block end
|
73
|
+
|
74
|
+
def transition trs_hshes, &block
|
75
|
+
trs_hshes.each do | from, to |
|
76
|
+
@transitions[[from, to]] << block
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def current_transition; [@old_state, @state] end
|
82
|
+
|
83
|
+
def mk_ary_hash; Hash.new{ |h,k| h[k] = [] } end
|
84
|
+
|
85
|
+
def initialize state_machine
|
86
|
+
@sm = state_machine
|
87
|
+
@old_state = nil
|
88
|
+
@state = :init
|
89
|
+
|
90
|
+
@setups = []
|
91
|
+
@teardowns = []
|
92
|
+
@afters = []
|
93
|
+
@befores = []
|
94
|
+
@handlers = mk_ary_hash
|
95
|
+
@transitions = mk_ary_hash
|
96
|
+
end
|
97
|
+
end # class Controller
|
98
|
+
end # class StateMachine
|
99
|
+
end # module Lab42
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'lab42/state_machine/controller'
|
2
|
+
require 'forwarder'
|
3
|
+
|
4
|
+
module Lab42
|
5
|
+
class StateMachine
|
6
|
+
extend Forwarder
|
7
|
+
attr_accessor :subject
|
8
|
+
|
9
|
+
forward_all :after, :before, :setup, :state, :step, :teardown, :transition, to: :controller
|
10
|
+
|
11
|
+
def run input=[]
|
12
|
+
inputs = make_inputs input
|
13
|
+
controller.run_setups
|
14
|
+
inputs.each do | input |
|
15
|
+
controller.run input
|
16
|
+
end
|
17
|
+
controller.run_teardowns
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize subject=nil, &define_machine
|
24
|
+
self.subject = subject
|
25
|
+
__make_new_controller__
|
26
|
+
|
27
|
+
instance_eval( &define_machine ) if define_machine
|
28
|
+
end
|
29
|
+
|
30
|
+
def __make_new_controller__
|
31
|
+
controller = Controller.new self
|
32
|
+
class << self; self end.module_eval do
|
33
|
+
define_method( :controller ){ controller }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def make_inputs input
|
38
|
+
case input
|
39
|
+
when Enumerable, Enumerator
|
40
|
+
input
|
41
|
+
else
|
42
|
+
raise ArgumentError, "cannot enumerate on object #{input}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end # class StateMachine
|
47
|
+
end # module Lab42
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lab42_state_machine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Dober
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: forwarder19
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.12
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.9.12
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.13.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.13.0
|
55
|
+
description: Filters, Transitions
|
56
|
+
email: robert.dober@gmail.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- lib/lab42/state_machine/version.rb
|
62
|
+
- lib/lab42/state_machine/controller.rb
|
63
|
+
- lib/lab42/state_machine.rb
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
homepage: https://github.com/RobertDober/lab42_state_machine
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.0.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.0.3
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: A Simple State Machine
|
90
|
+
test_files: []
|