mouse_organ 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/.hgignore +15 -0
- data/.rspec +3 -0
- data/Gemfile +14 -0
- data/LICENSE.md +21 -0
- data/README.md +70 -0
- data/lib/mouse_organ.rb +70 -0
- data/mouse_organ.gemspec +28 -0
- data/spec/mouse_organ_spec.rb +124 -0
- data/spec/spec_helper.rb +25 -0
- metadata +57 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 641094f0c65c8fbee427c96d520549086475873b
|
4
|
+
data.tar.gz: b22183796cb160f016e6d215c5a0ea98d2cc846b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 49314c17a6e28eea2e114bfeb68dae62185180bbf68a0bb79d04e960eb848e4e537abd02cfe00f0492008658271f8e12ae4a7f8a4f7249576ffaa9c1c6532079
|
7
|
+
data.tar.gz: b4f2da325915322c469846f81a95375b9395b874efa28bc6f25dc5515a5866ef68de2fbb52fa6f05ba30a4ce453cc4e2aec1550f6ff0499dc5c7de93e35e77c0
|
data/.hgignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Andrew Jones
|
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
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, 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,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
Mouse Organ
|
2
|
+
===========
|
3
|
+
|
4
|
+
Ladies and gentlemen, the Marvelous -- Mechanical -- Mouse Organ!
|
5
|
+
|
6
|
+
This is a _very_ small mixin that turns a class into a basic state machine.
|
7
|
+
|
8
|
+
|
9
|
+
Installation
|
10
|
+
------------
|
11
|
+
|
12
|
+
Just download lib/mouse_organ.rb into your code. Or you could just use it as a pattern of sorts,
|
13
|
+
and roll your own in the same vein.
|
14
|
+
|
15
|
+
I mean, sure, you can also `gem install mouse_organ`. But honestly, it might be better to have the
|
16
|
+
class where you can see it, for documentation purposes? Your call.
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
Example
|
21
|
+
-------
|
22
|
+
|
23
|
+
```{.ruby}
|
24
|
+
class Foo
|
25
|
+
include MouseOrgan
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
machine_start(:one)
|
29
|
+
end
|
30
|
+
|
31
|
+
def state_one
|
32
|
+
transition_to :two
|
33
|
+
sleep 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def state_two
|
37
|
+
transition_to :three
|
38
|
+
sleep 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def state_three
|
42
|
+
throw :machine_stop
|
43
|
+
end
|
44
|
+
|
45
|
+
def transition_to(state)
|
46
|
+
puts state
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
|
54
|
+
What You Get
|
55
|
+
------------
|
56
|
+
|
57
|
+
Each of your methods that start `state_` define a state of the state machine. To start the machine,
|
58
|
+
pass `#machine_start` the initial state name (e.g., `:two` for the state defined by `#state_two`).
|
59
|
+
|
60
|
+
Inside a state method, you can change to a new state using `transition_to(:statename)`. You can
|
61
|
+
stop the machine using `throw :machine_stop`, or you can override `#machine_stop?` to return true
|
62
|
+
if the machine should stop.
|
63
|
+
|
64
|
+
Passing an invalid state to `#machine_start` or `#transition_to` raises ArgumentError.
|
65
|
+
|
66
|
+
The attribute @state holds the current state.
|
67
|
+
|
68
|
+
It might be useful to override `#transition_to` as I have above -- for example, in order to log
|
69
|
+
every change of state.
|
70
|
+
|
data/lib/mouse_organ.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
##
|
2
|
+
# A tiny mixin to turn a class into a state machine.
|
3
|
+
# Example:
|
4
|
+
#
|
5
|
+
# class Foo
|
6
|
+
# include MouseOrgan
|
7
|
+
#
|
8
|
+
# def initialize
|
9
|
+
# machine_start(:one)
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# def state_one
|
13
|
+
# transition_to :two
|
14
|
+
# sleep 1
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def state_two
|
18
|
+
# transition_to :three
|
19
|
+
# sleep 1
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def state_three
|
23
|
+
# throw :machine_stop
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def transition_to(state)
|
27
|
+
# puts state
|
28
|
+
# super
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
module MouseOrgan
|
34
|
+
VERSION = "0.1.0"
|
35
|
+
|
36
|
+
def machine_start(initial)
|
37
|
+
transition_to initial
|
38
|
+
|
39
|
+
catch :machine_stop do
|
40
|
+
loop do
|
41
|
+
send state_method(@state)
|
42
|
+
throw :machine_stop if machine_stop?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def transition_to(state)
|
50
|
+
if self.respond_to? state_method(state)
|
51
|
+
@state = state
|
52
|
+
else
|
53
|
+
fail ArgumentError, "Unknown state '#{state}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def machine_stop?
|
60
|
+
false
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def state_method(state)
|
66
|
+
"state_#{state}".to_sym
|
67
|
+
end
|
68
|
+
|
69
|
+
end # of MouseOrgan
|
70
|
+
|
data/mouse_organ.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'mouse_organ'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "mouse_organ"
|
7
|
+
spec.version = MouseOrgan::VERSION
|
8
|
+
spec.authors = ["Andy Jones"]
|
9
|
+
spec.email = ["andy.jones@twosticksconsulting.co.uk"]
|
10
|
+
spec.summary = %q|Tiny state machine|
|
11
|
+
spec.description = <<-DESC.gsub(/^\s+/, "")
|
12
|
+
A tiny mixin to turn a class into a state machine.
|
13
|
+
DESC
|
14
|
+
|
15
|
+
spec.homepage = "https://bitbucket.org/andy-twosticks/mouse_organ"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `hg status -macn0`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.extra_rdoc_files = spec.files.grep(%r{^md/})
|
24
|
+
|
25
|
+
#spec.add_runtime_dependency "devnull", '~>0.1'
|
26
|
+
#spec.add_runtime_dependency "octothorpe", '~>0.4'
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "mouse_organ"
|
2
|
+
|
3
|
+
|
4
|
+
RSpec.describe "MouseOrgan" do
|
5
|
+
|
6
|
+
let(:class_organ1) do
|
7
|
+
Class.new do
|
8
|
+
include MouseOrgan
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:class_organ2) do
|
13
|
+
Class.new do
|
14
|
+
include MouseOrgan
|
15
|
+
attr_reader :state
|
16
|
+
|
17
|
+
def go; machine_start(:one); end
|
18
|
+
|
19
|
+
def state_one; throw :machine_stop; end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:class_organ3) do
|
24
|
+
Class.new do
|
25
|
+
include MouseOrgan
|
26
|
+
|
27
|
+
def go; machine_start(:one); end
|
28
|
+
|
29
|
+
def state_one; transition_to :two; end
|
30
|
+
def state_two; transition_to :three; end
|
31
|
+
def state_three; throw :machine_stop; end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:class_organ4) do
|
36
|
+
Class.new do
|
37
|
+
include MouseOrgan
|
38
|
+
|
39
|
+
def go; machine_start(:one); end
|
40
|
+
|
41
|
+
def state_one; transition_to :two; end
|
42
|
+
def state_two; transition_to :three; end
|
43
|
+
def state_three; transition_to :one; end
|
44
|
+
|
45
|
+
def machine_stop?; @state == :three; end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:class_organ5) do
|
50
|
+
Class.new do
|
51
|
+
include MouseOrgan
|
52
|
+
attr_reader :state_history
|
53
|
+
|
54
|
+
def initialize; @state_history = []; end
|
55
|
+
|
56
|
+
def go; machine_start(:one); end
|
57
|
+
|
58
|
+
def state_one; transition_to :two; end
|
59
|
+
def state_two; transition_to :three; end
|
60
|
+
def state_three; throw :machine_stop; end
|
61
|
+
|
62
|
+
def transition_to(state)
|
63
|
+
@state_history << state
|
64
|
+
super
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:organ1) { class_organ1.new }
|
70
|
+
let(:organ2) { class_organ2.new }
|
71
|
+
let(:organ3) { class_organ3.new }
|
72
|
+
let(:organ4) { class_organ4.new }
|
73
|
+
let(:organ5) { class_organ5.new }
|
74
|
+
|
75
|
+
|
76
|
+
describe "#machine_start" do
|
77
|
+
|
78
|
+
it "raises ArgumentError if given an invalid state" do
|
79
|
+
expect{ organ1.machine_start(:wrong) }.to raise_error ArgumentError
|
80
|
+
end
|
81
|
+
|
82
|
+
it "transitions to the initial state" do
|
83
|
+
expect( organ2 ).to receive(:transition_to).with(:one).and_call_original
|
84
|
+
organ2.go
|
85
|
+
end
|
86
|
+
|
87
|
+
it "transits through the given states and stops when :machine_stop is thrown" do
|
88
|
+
expect( organ3 ).to receive(:transition_to).with(:one).and_call_original.ordered
|
89
|
+
expect( organ3 ).to receive(:transition_to).with(:two).and_call_original.ordered
|
90
|
+
expect( organ3 ).to receive(:transition_to).with(:three).and_call_original.ordered
|
91
|
+
organ3.go
|
92
|
+
end
|
93
|
+
|
94
|
+
it "transits through the given states and stops when #machine_stop? is true" do
|
95
|
+
expect( organ4 ).to receive(:transition_to).with(:one).and_call_original.ordered
|
96
|
+
expect( organ4 ).to receive(:transition_to).with(:two).and_call_original.ordered
|
97
|
+
expect( organ4 ).to receive(:transition_to).with(:three).and_call_original.ordered
|
98
|
+
organ4.go
|
99
|
+
end
|
100
|
+
|
101
|
+
end # of #machine_start
|
102
|
+
|
103
|
+
|
104
|
+
describe "#transition_to" do
|
105
|
+
|
106
|
+
it "raises ArgumentError if given an invalid state" do
|
107
|
+
expect{ organ2.transition_to(:two) }.to raise_error ArgumentError
|
108
|
+
end
|
109
|
+
|
110
|
+
it "sets @state to the new state" do
|
111
|
+
organ2.transition_to(:one)
|
112
|
+
expect( organ2.state ).to eq :one
|
113
|
+
end
|
114
|
+
|
115
|
+
it "will allow overriding" do
|
116
|
+
organ5.go
|
117
|
+
expect( organ5.state_history ).to eq(%i|one two three|)
|
118
|
+
end
|
119
|
+
|
120
|
+
end # of #transition_to
|
121
|
+
|
122
|
+
|
123
|
+
end
|
124
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'pry' #always useful when debugging
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
|
5
|
+
config.expect_with :rspec do |expectations|
|
6
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
7
|
+
end
|
8
|
+
|
9
|
+
config.mock_with :rspec do |mocks|
|
10
|
+
mocks.verify_partial_doubles = true
|
11
|
+
end
|
12
|
+
|
13
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
14
|
+
config.filter_run_when_matching :focus
|
15
|
+
config.disable_monkey_patching!
|
16
|
+
config.warnings = true
|
17
|
+
config.order = :random
|
18
|
+
|
19
|
+
if config.files_to_run.one?
|
20
|
+
config.default_formatter = 'doc'
|
21
|
+
end
|
22
|
+
|
23
|
+
Kernel.srand config.seed
|
24
|
+
end
|
25
|
+
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mouse_organ
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andy Jones
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-31 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: 'A tiny mixin to turn a class into a state machine.
|
14
|
+
|
15
|
+
'
|
16
|
+
email:
|
17
|
+
- andy.jones@twosticksconsulting.co.uk
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- ".hgignore"
|
23
|
+
- ".rspec"
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE.md
|
26
|
+
- README.md
|
27
|
+
- lib/mouse_organ.rb
|
28
|
+
- mouse_organ.gemspec
|
29
|
+
- spec/mouse_organ_spec.rb
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
homepage: https://bitbucket.org/andy-twosticks/mouse_organ
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.5.2
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Tiny state machine
|
55
|
+
test_files:
|
56
|
+
- spec/mouse_organ_spec.rb
|
57
|
+
- spec/spec_helper.rb
|