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 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 &block; @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 &block; @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,5 @@
1
+ module Lab42
2
+ class StateMachine
3
+ Version = "0.1.0"
4
+ end # class StateMachine
5
+ 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: []