simply_fsm 0.1.1 → 0.1.2
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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +6 -0
- data/Rakefile +10 -0
- data/lib/simply_fsm/version.rb +1 -1
- data/lib/simply_fsm.rb +95 -67
- data/simply_fsm.gemspec +1 -0
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e86bd7cca4df06c4ada7a53528e2e0f5853fc9055bf0bb808c4cd029b552ad37
|
|
4
|
+
data.tar.gz: 6ad8c2cfe781b729316f0df6707be5c10c17e0ed4e865a7042c22504e2039e0c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c3dea4db713c9cc75ef314ba8c536d7a0847fa396f12af0e69b951eb78a2bb20c3ed6f620aa1c11cb26f23087c93891ccf6d5cc78fd17abc4e2f0ab130819748
|
|
7
|
+
data.tar.gz: 659bf78f96bf5bd41cc9cf03c50387d165c04148de5cfc6ea89955f18707f72ab2cfec271d36e2424622a528783b6a13dd88222ee695ce65497d0e930f8df603
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
TargetRubyVersion: 2.7
|
|
3
3
|
|
|
4
|
+
Naming/FileName:
|
|
5
|
+
Exclude:
|
|
6
|
+
- Rakefile
|
|
7
|
+
|
|
4
8
|
Style/StringLiterals:
|
|
5
9
|
Enabled: true
|
|
6
10
|
EnforcedStyle: double_quotes
|
|
@@ -11,3 +15,13 @@ Style/StringLiteralsInInterpolation:
|
|
|
11
15
|
|
|
12
16
|
Layout/LineLength:
|
|
13
17
|
Max: 120
|
|
18
|
+
|
|
19
|
+
Metrics/MethodLength:
|
|
20
|
+
Max: 16
|
|
21
|
+
|
|
22
|
+
Metrics/ClassLength:
|
|
23
|
+
Max: 256
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Exclude:
|
|
27
|
+
- 'spec/**/*'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
- None right now
|
|
4
|
+
|
|
5
|
+
## [0.1.2] - 2022-02-28
|
|
6
|
+
|
|
7
|
+
- Cleaned up source with smaller clearer methods
|
|
8
|
+
- Added `rdoc` support, include `rake rdoc` task
|
|
9
|
+
|
|
3
10
|
## [0.1.1] - 2022-02-21
|
|
4
11
|
|
|
5
12
|
- Separated version file, fixed URLs in Gem spec, added badge to README
|
data/Gemfile.lock
CHANGED
|
@@ -11,8 +11,12 @@ GEM
|
|
|
11
11
|
parallel (1.21.0)
|
|
12
12
|
parser (3.1.1.0)
|
|
13
13
|
ast (~> 2.4.1)
|
|
14
|
+
psych (4.0.3)
|
|
15
|
+
stringio
|
|
14
16
|
rainbow (3.1.1)
|
|
15
17
|
rake (13.0.6)
|
|
18
|
+
rdoc (6.4.0)
|
|
19
|
+
psych (>= 4.0.0)
|
|
16
20
|
regexp_parser (2.2.1)
|
|
17
21
|
rexml (3.2.5)
|
|
18
22
|
rspec (3.11.0)
|
|
@@ -40,6 +44,7 @@ GEM
|
|
|
40
44
|
rubocop-ast (1.15.2)
|
|
41
45
|
parser (>= 3.0.1.1)
|
|
42
46
|
ruby-progressbar (1.11.0)
|
|
47
|
+
stringio (3.0.1)
|
|
43
48
|
unicode-display_width (2.1.0)
|
|
44
49
|
|
|
45
50
|
PLATFORMS
|
|
@@ -47,6 +52,7 @@ PLATFORMS
|
|
|
47
52
|
|
|
48
53
|
DEPENDENCIES
|
|
49
54
|
rake (~> 13.0)
|
|
55
|
+
rdoc
|
|
50
56
|
rspec (~> 3.0)
|
|
51
57
|
rubocop (~> 1.21)
|
|
52
58
|
simply_fsm!
|
data/Rakefile
CHANGED
|
@@ -7,6 +7,16 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
7
7
|
|
|
8
8
|
require "rubocop/rake_task"
|
|
9
9
|
|
|
10
|
+
require "rdoc/task"
|
|
11
|
+
# require "simply_fsm/version"
|
|
12
|
+
|
|
13
|
+
Rake::RDocTask.new do |rdoc|
|
|
14
|
+
rdoc.rdoc_dir = "rdoc"
|
|
15
|
+
rdoc.title = "simply_fsm #{SimplyFSM::VERSION}"
|
|
16
|
+
rdoc.rdoc_files.include("README*")
|
|
17
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
|
18
|
+
end
|
|
19
|
+
|
|
10
20
|
RuboCop::RakeTask.new
|
|
11
21
|
|
|
12
22
|
task default: %i[spec rubocop]
|
data/lib/simply_fsm/version.rb
CHANGED
data/lib/simply_fsm.rb
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "simply_fsm/version"
|
|
4
4
|
|
|
5
|
+
##
|
|
6
|
+
# Defines the `SimplyFSM` module
|
|
5
7
|
module SimplyFSM
|
|
6
8
|
def self.included(base)
|
|
7
9
|
base.extend(ClassMethods)
|
|
8
10
|
end
|
|
9
11
|
|
|
12
|
+
##
|
|
13
|
+
# Defines the constructor for defining a state machine
|
|
10
14
|
module ClassMethods
|
|
15
|
+
##
|
|
16
|
+
# Declare a state machine called +name+ which can then be defined
|
|
17
|
+
# by a DSL defined by the methods of `StateMachine`, with the following +opts+:
|
|
18
|
+
# - an optional +fail+ lambda that is called when any event fails to transition)
|
|
11
19
|
def state_machine(name, opts = {}, &block)
|
|
12
20
|
fsm = StateMachine.new(name, self, fail: opts[:fail])
|
|
13
21
|
fsm.instance_eval(&block)
|
|
14
22
|
end
|
|
15
23
|
end
|
|
16
24
|
|
|
25
|
+
##
|
|
26
|
+
# The DSL for defining a state machine
|
|
17
27
|
class StateMachine
|
|
18
28
|
attr_reader :initial_state, :states, :events, :name, :full_name
|
|
19
29
|
|
|
@@ -29,93 +39,111 @@ module SimplyFSM
|
|
|
29
39
|
setup_base_methods
|
|
30
40
|
end
|
|
31
41
|
|
|
42
|
+
##
|
|
43
|
+
# Declare a supported +state_name+, and optionally specify one as the +initial+ state.
|
|
32
44
|
def state(state_name, initial: false)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
return if state_name.nil? || @states.include?(state_name)
|
|
46
|
+
|
|
47
|
+
status = state_name.to_sym
|
|
48
|
+
state_machine_name = @name
|
|
49
|
+
@states << status
|
|
50
|
+
@initial_state = status if initial
|
|
51
|
+
|
|
52
|
+
make_owner_method "#{state_name}?", lambda {
|
|
53
|
+
send(state_machine_name) == status
|
|
54
|
+
}
|
|
43
55
|
end
|
|
44
56
|
|
|
57
|
+
##
|
|
58
|
+
# Define an event by +event_name+ and
|
|
59
|
+
# - its +transition+ as a hash with a +from+ state or array of states and the +to+ state,
|
|
60
|
+
# - an optional +guard+ lambda which must return true for the transition to occur,
|
|
61
|
+
# - an optional +fail+ lambda that is called when the transition fails (overrides top-level fail handler), and
|
|
62
|
+
# - an optional do block that is called +after+ the transition succeeds
|
|
45
63
|
def event(event_name, transition:, guard: nil, fail: nil, &after)
|
|
46
|
-
|
|
47
|
-
@events << event_name
|
|
48
|
-
from = transition[:from]
|
|
49
|
-
to = transition[:to]
|
|
50
|
-
state_machine_name = @name
|
|
51
|
-
var_name = "@#{state_machine_name}"
|
|
52
|
-
may_event_name = "may_#{event_name}?"
|
|
53
|
-
fail = @fail_handler if fail.nil?
|
|
54
|
-
|
|
55
|
-
setup_may_event_method may_event_name, from, to, guard
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
# Setup the event method to attempt to make the state
|
|
59
|
-
# transition or report failure
|
|
60
|
-
make_owner_method event_name, lambda {
|
|
61
|
-
if send(may_event_name)
|
|
62
|
-
instance_variable_set(var_name, to)
|
|
63
|
-
instance_exec(&after) if after
|
|
64
|
-
return true
|
|
65
|
-
end
|
|
66
|
-
# unable to satisfy pre-conditions for the event
|
|
67
|
-
if fail
|
|
68
|
-
if fail.is_a?(String) || fail.is_a?(Symbol)
|
|
69
|
-
send(fail, event_name)
|
|
70
|
-
else
|
|
71
|
-
instance_exec(event_name, &fail)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
false
|
|
75
|
-
}
|
|
64
|
+
return unless event_exists?(event_name) && transition
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
@events << event_name
|
|
67
|
+
to = transition[:to]
|
|
68
|
+
may_event_name = "may_#{event_name}?"
|
|
69
|
+
|
|
70
|
+
setup_may_event_method may_event_name, transition[:from], to, guard
|
|
71
|
+
setup_event_method event_name, var_name: "@#{@name}",
|
|
72
|
+
may_event_name: may_event_name, to: to,
|
|
73
|
+
fail: fail || @fail_handler, &after
|
|
78
74
|
end
|
|
79
75
|
|
|
80
76
|
private
|
|
81
77
|
|
|
78
|
+
def event_exists?(event_name)
|
|
79
|
+
event_name && !@events.include?(event_name)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def setup_event_method(event_name, var_name:, may_event_name:, to:, fail:, &after)
|
|
83
|
+
method_lambda = lambda {
|
|
84
|
+
if send(may_event_name)
|
|
85
|
+
instance_variable_set(var_name, to)
|
|
86
|
+
instance_exec(&after) if after
|
|
87
|
+
return true
|
|
88
|
+
end
|
|
89
|
+
# unable to satisfy pre-conditions for the event
|
|
90
|
+
if fail
|
|
91
|
+
if fail.is_a?(String) || fail.is_a?(Symbol)
|
|
92
|
+
send(fail, event_name)
|
|
93
|
+
else
|
|
94
|
+
instance_exec(event_name, &fail)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
false
|
|
98
|
+
}
|
|
99
|
+
make_owner_method event_name, method_lambda
|
|
100
|
+
end
|
|
101
|
+
|
|
82
102
|
def setup_may_event_method(may_event_name, from, _to, guard)
|
|
83
103
|
state_machine_name = @name
|
|
84
104
|
#
|
|
85
|
-
# Instead of one "may_event?" method that checks all variations
|
|
86
|
-
#
|
|
87
|
-
# define the most optimal lambda to ensure the check is as fast as
|
|
88
|
-
# possible
|
|
105
|
+
# Instead of one "may_event?" method that checks all variations every time it's called, here we check
|
|
106
|
+
# the event definition and define the most optimal lambda to ensure the check is as fast as possible
|
|
89
107
|
method_lambda = if from == :any && !guard
|
|
90
108
|
-> { true } # unguarded transition from any state
|
|
91
109
|
elsif from == :any
|
|
92
|
-
guard
|
|
110
|
+
guard # guarded transition from any state
|
|
93
111
|
elsif !guard
|
|
94
|
-
|
|
95
|
-
lambda { # unguarded transition from choice of states
|
|
96
|
-
current = send(state_machine_name)
|
|
97
|
-
from.include?(current)
|
|
98
|
-
}
|
|
99
|
-
else
|
|
100
|
-
lambda { # unguarded transition from one state
|
|
101
|
-
current = send(state_machine_name)
|
|
102
|
-
from == current
|
|
103
|
-
}
|
|
104
|
-
end
|
|
105
|
-
elsif from.is_a?(Array)
|
|
106
|
-
lambda { # guarded transition from choice of states
|
|
107
|
-
current = send(state_machine_name)
|
|
108
|
-
from.include?(current) && instance_exec(&guard)
|
|
109
|
-
}
|
|
112
|
+
guardless_may_event_lambda(from, state_machine_name)
|
|
110
113
|
else
|
|
111
|
-
|
|
112
|
-
current = send(state_machine_name)
|
|
113
|
-
from == current && instance_exec(&guard)
|
|
114
|
-
}
|
|
114
|
+
guarded_may_event_lambda(from, guard, state_machine_name)
|
|
115
115
|
end
|
|
116
116
|
make_owner_method may_event_name, method_lambda
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
+
def guarded_may_event_lambda(from, guard, state_machine_name)
|
|
120
|
+
if from.is_a?(Array)
|
|
121
|
+
lambda { # guarded transition from choice of states
|
|
122
|
+
current = send(state_machine_name)
|
|
123
|
+
from.include?(current) && instance_exec(&guard)
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
lambda { # guarded transition from one state
|
|
127
|
+
current = send(state_machine_name)
|
|
128
|
+
from == current && instance_exec(&guard)
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def guardless_may_event_lambda(from, state_machine_name)
|
|
134
|
+
if from.is_a?(Array)
|
|
135
|
+
lambda { # unguarded transition from choice of states
|
|
136
|
+
current = send(state_machine_name)
|
|
137
|
+
from.include?(current)
|
|
138
|
+
}
|
|
139
|
+
else
|
|
140
|
+
lambda { # unguarded transition from one state
|
|
141
|
+
current = send(state_machine_name)
|
|
142
|
+
from == current
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
119
147
|
def setup_base_methods
|
|
120
148
|
var_name = "@#{name}"
|
|
121
149
|
fsm = self
|
data/simply_fsm.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simply_fsm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- nogginly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-02-
|
|
11
|
+
date: 2022-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rdoc
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: rspec
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -76,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
76
90
|
- !ruby/object:Gem::Version
|
|
77
91
|
version: '0'
|
|
78
92
|
requirements: []
|
|
79
|
-
rubygems_version: 3.
|
|
93
|
+
rubygems_version: 3.3.7
|
|
80
94
|
signing_key:
|
|
81
95
|
specification_version: 4
|
|
82
96
|
summary: Simple finite state mechine (FSM) data-type mixin for Ruby objects.
|