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