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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b86e3a94184036d2ba485db8452bcf2c4ad3c8314e71b56b481f1a428d253ad
4
- data.tar.gz: 445b68adba6dc1d0376473288af06a04026bdeb055616c0ef3a31acb6f1b068d
3
+ metadata.gz: e86bd7cca4df06c4ada7a53528e2e0f5853fc9055bf0bb808c4cd029b552ad37
4
+ data.tar.gz: 6ad8c2cfe781b729316f0df6707be5c10c17e0ed4e865a7042c22504e2039e0c
5
5
  SHA512:
6
- metadata.gz: c754997ecccc818fab23c9079cf6f4f274cd8ab484ea56af7eee49ebc69ce97985814cb0ef0910088e339e8839b6fc4ab8f07848b014313c1106f8f8f310cdb0
7
- data.tar.gz: e11e94844f8a0e6dcad77b4cc8dc642afeced63c31678d1ac0278d19825e7e194062fa3d569381bac36be9465f85ccf1f500355a08681372cf455a43f03dbbf9
6
+ metadata.gz: c3dea4db713c9cc75ef314ba8c536d7a0847fa396f12af0e69b951eb78a2bb20c3ed6f620aa1c11cb26f23087c93891ccf6d5cc78fd17abc4e2f0ab130819748
7
+ data.tar.gz: 659bf78f96bf5bd41cc9cf03c50387d165c04148de5cfc6ea89955f18707f72ab2cfec271d36e2424622a528783b6a13dd88222ee695ce65497d0e930f8df603
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  /_yardoc/
4
4
  /coverage/
5
5
  /doc/
6
+ /rdoc/
6
7
  /pkg/
7
8
  /spec/reports/
8
9
  /tmp/
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]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimplyFSM
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/simply_fsm.rb CHANGED
@@ -1,19 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'simply_fsm/version'
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
- unless state_name.nil? || @states.include?(state_name)
34
- status = state_name.to_sym
35
- state_machine_name = @name
36
- @states << status
37
- @initial_state = status if initial
38
-
39
- make_owner_method "#{state_name}?", lambda {
40
- send(state_machine_name) == status
41
- }
42
- end
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
- if event_name && transition
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
- end
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
- # every time it's called, here we check the event definition and
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 # guarded transition from any state
110
+ guard # guarded transition from any state
93
111
  elsif !guard
94
- if from.is_a?(Array)
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
- lambda { # guarded transition from one state
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
@@ -26,5 +26,6 @@ Gem::Specification.new do |spec|
26
26
  # no deployment dependencies
27
27
 
28
28
  # development
29
+ spec.add_development_dependency "rdoc"
29
30
  spec.add_development_dependency "rspec", "~> 3.0"
30
31
  end
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.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-21 00:00:00.000000000 Z
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.1.6
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.