state_machines 0.6.0 → 0.30.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +205 -14
  3. data/lib/state_machines/branch.rb +20 -17
  4. data/lib/state_machines/callback.rb +13 -12
  5. data/lib/state_machines/core.rb +3 -3
  6. data/lib/state_machines/core_ext/class/state_machine.rb +2 -0
  7. data/lib/state_machines/core_ext.rb +2 -0
  8. data/lib/state_machines/error.rb +7 -4
  9. data/lib/state_machines/eval_helpers.rb +93 -26
  10. data/lib/state_machines/event.rb +41 -29
  11. data/lib/state_machines/event_collection.rb +6 -5
  12. data/lib/state_machines/extensions.rb +7 -5
  13. data/lib/state_machines/helper_module.rb +3 -1
  14. data/lib/state_machines/integrations/base.rb +3 -1
  15. data/lib/state_machines/integrations.rb +13 -14
  16. data/lib/state_machines/machine/action_hooks.rb +53 -0
  17. data/lib/state_machines/machine/callbacks.rb +59 -0
  18. data/lib/state_machines/machine/class_methods.rb +93 -0
  19. data/lib/state_machines/machine/configuration.rb +124 -0
  20. data/lib/state_machines/machine/event_methods.rb +59 -0
  21. data/lib/state_machines/machine/helper_generators.rb +125 -0
  22. data/lib/state_machines/machine/integration.rb +70 -0
  23. data/lib/state_machines/machine/parsing.rb +77 -0
  24. data/lib/state_machines/machine/rendering.rb +17 -0
  25. data/lib/state_machines/machine/scoping.rb +44 -0
  26. data/lib/state_machines/machine/state_methods.rb +101 -0
  27. data/lib/state_machines/machine/utilities.rb +85 -0
  28. data/lib/state_machines/machine/validation.rb +39 -0
  29. data/lib/state_machines/machine.rb +83 -673
  30. data/lib/state_machines/machine_collection.rb +23 -15
  31. data/lib/state_machines/macro_methods.rb +4 -2
  32. data/lib/state_machines/matcher.rb +8 -5
  33. data/lib/state_machines/matcher_helpers.rb +3 -1
  34. data/lib/state_machines/node_collection.rb +23 -18
  35. data/lib/state_machines/options_validator.rb +72 -0
  36. data/lib/state_machines/path.rb +7 -5
  37. data/lib/state_machines/path_collection.rb +7 -4
  38. data/lib/state_machines/state.rb +76 -47
  39. data/lib/state_machines/state_collection.rb +5 -3
  40. data/lib/state_machines/state_context.rb +11 -8
  41. data/lib/state_machines/stdio_renderer.rb +74 -0
  42. data/lib/state_machines/syntax_validator.rb +57 -0
  43. data/lib/state_machines/test_helper.rb +568 -0
  44. data/lib/state_machines/transition.rb +45 -41
  45. data/lib/state_machines/transition_collection.rb +27 -26
  46. data/lib/state_machines/version.rb +3 -1
  47. data/lib/state_machines.rb +4 -1
  48. metadata +32 -16
  49. data/lib/state_machines/assertions.rb +0 -40
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module StateMachines
2
4
  # A transition represents a state change for a specific attribute.
3
5
  #
@@ -31,11 +33,11 @@ module StateMachines
31
33
  # Determines whether the current ruby implementation supports pausing and
32
34
  # resuming transitions
33
35
  def self.pause_supported?
34
- %w(ruby maglev).include?(RUBY_ENGINE)
36
+ %w[ruby maglev].include?(RUBY_ENGINE)
35
37
  end
36
38
 
37
39
  # Creates a new, specific transition
38
- def initialize(object, machine, event, from_name, to_name, read_state = true) #:nodoc:
40
+ def initialize(object, machine, event, from_name, to_name, read_state = true) # :nodoc:
39
41
  @object = object
40
42
  @machine = machine
41
43
  @args = []
@@ -134,7 +136,7 @@ module StateMachines
134
136
  # transition = StateMachines::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
135
137
  # transition.attributes # => {:object => #<Vehicle:0xb7d60ea4>, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
136
138
  def attributes
137
- @attributes ||= {object: object, attribute: attribute, event: event, from: from, to: to}
139
+ @attributes ||= { object: object, attribute: attribute, event: event, from: from, to: to }
138
140
  end
139
141
 
140
142
  # Runs the actual transition and any before/after callbacks associated
@@ -160,16 +162,14 @@ module StateMachines
160
162
  self.args = args
161
163
 
162
164
  # Run the transition
163
- !!TransitionCollection.new([self], {use_transactions: machine.use_transactions, actions: run_action}).perform
165
+ !!TransitionCollection.new([self], { use_transactions: machine.use_transactions, actions: run_action }).perform
164
166
  end
165
167
 
166
168
  # Runs a block within a transaction for the object being transitioned.
167
169
  # By default, transactions are a no-op unless otherwise defined by the
168
170
  # machine's integration.
169
- def within_transaction
170
- machine.within_transaction(object) do
171
- yield
172
- end
171
+ def within_transaction(&)
172
+ machine.within_transaction(object, &)
173
173
  end
174
174
 
175
175
  # Runs the before / after callbacks for this transition. If a block is
@@ -184,7 +184,7 @@ module StateMachines
184
184
  # This will return true if all before callbacks gets executed. After
185
185
  # callbacks will not have an effect on the result.
186
186
  def run_callbacks(options = {}, &block)
187
- options = {before: true, after: true}.merge(options)
187
+ options = { before: true, after: true }.merge(options)
188
188
  @success = false
189
189
 
190
190
  halted = pausable { before(options[:after], &block) } if options[:before]
@@ -217,10 +217,10 @@ module StateMachines
217
217
  #
218
218
  # vehicle.state # => 'idling'
219
219
  def persist
220
- unless @persisted
221
- machine.write(object, :state, to)
222
- @persisted = true
223
- end
220
+ return if @persisted
221
+
222
+ machine.write(object, :state, to)
223
+ @persisted = true
224
224
  end
225
225
 
226
226
  # Rolls back changes made to the object's state via this transition. This
@@ -263,11 +263,11 @@ module StateMachines
263
263
  # and event involved in the transition are equal
264
264
  def ==(other)
265
265
  other.instance_of?(self.class) &&
266
- other.object == object &&
267
- other.machine == machine &&
268
- other.from_name == from_name &&
269
- other.to_name == to_name &&
270
- other.event == event
266
+ other.object == object &&
267
+ other.machine == machine &&
268
+ other.from_name == from_name &&
269
+ other.to_name == to_name &&
270
+ other.event == event
271
271
  end
272
272
 
273
273
  # Generates a nicely formatted description of this transitions's contents.
@@ -277,10 +277,10 @@ module StateMachines
277
277
  # transition = StateMachines::Transition.new(object, machine, :ignite, :parked, :idling)
278
278
  # transition # => #<StateMachines::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
279
279
  def inspect
280
- "#<#{self.class} #{%w(attribute event from from_name to to_name).map { |attr| "#{attr}=#{send(attr).inspect}" } * ' '}>"
280
+ "#<#{self.class} #{%w[attribute event from from_name to to_name].map { |attr| "#{attr}=#{send(attr).inspect}" } * ' '}>"
281
281
  end
282
282
 
283
- private
283
+ private
284
284
 
285
285
  # Runs a block that may get paused. If the block doesn't pause, then
286
286
  # execution will continue as normal. If the block gets paused, then it
@@ -290,13 +290,16 @@ module StateMachines
290
290
  # getting paused.
291
291
  def pausable
292
292
  begin
293
- halted = !catch(:halt) { yield; true }
294
- rescue => error
293
+ halted = !catch(:halt) do
294
+ yield
295
+ true
296
+ end
297
+ rescue StandardError => e
295
298
  raise unless @resume_block
296
299
  end
297
300
 
298
301
  if @resume_block
299
- @resume_block.call(halted, error)
302
+ @resume_block.call(halted, e)
300
303
  else
301
304
  halted
302
305
  end
@@ -308,12 +311,12 @@ module StateMachines
308
311
  def pause
309
312
  raise ArgumentError, 'around_transition callbacks cannot be called in multiple execution contexts in java implementations of Ruby. Use before/after_transitions instead.' unless self.class.pause_supported?
310
313
 
311
- unless @resume_block
312
- require 'continuation' unless defined?(callcc)
313
- callcc do |block|
314
- @paused_block = block
315
- throw :halt, true
316
- end
314
+ return if @resume_block
315
+
316
+ require 'continuation' unless defined?(callcc)
317
+ callcc do |block|
318
+ @paused_block = block
319
+ throw :halt, true
317
320
  end
318
321
  end
319
322
 
@@ -370,8 +373,9 @@ module StateMachines
370
373
  @before_run = true
371
374
  end
372
375
 
373
- action = {success: true}.merge(block_given? ? yield : {})
374
- @result, @success = action[:result], action[:success]
376
+ action = { success: true }.merge(block_given? ? yield : {})
377
+ @result = action[:result]
378
+ @success = action[:success]
375
379
  end
376
380
 
377
381
  # Runs the machine's +after+ callbacks for this transition. Only
@@ -388,17 +392,17 @@ module StateMachines
388
392
  # exception will not bubble up to the caller since +after+ callbacks
389
393
  # should never halt the execution of a +perform+.
390
394
  def after
391
- unless @after_run
392
- # First resume previously paused callbacks
393
- if resume
394
- catch(:halt) do
395
- type = @success ? :after : :failure
396
- machine.callbacks[type].each { |callback| callback.call(object, context, self) }
397
- end
398
- end
395
+ return if @after_run
399
396
 
400
- @after_run = true
397
+ # First resume previously paused callbacks
398
+ if resume
399
+ catch(:halt) do
400
+ type = @success ? :after : :failure
401
+ machine.callbacks[type].each { |callback| callback.call(object, context, self) }
402
+ end
401
403
  end
404
+
405
+ @after_run = true
402
406
  end
403
407
 
404
408
  # Gets a hash of the context defining this unique transition (including
@@ -410,7 +414,7 @@ module StateMachines
410
414
  # transition = StateMachines::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
411
415
  # transition.context # => {:on => :ignite, :from => :parked, :to => :idling}
412
416
  def context
413
- @context ||= {on: event, from: from_name, to: to_name}
417
+ @context ||= { on: event, from: from_name, to: to_name }
414
418
  end
415
419
  end
416
420
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options_validator'
4
+
1
5
  module StateMachines
2
6
  # Represents a collection of transitions in a state machine
3
7
  class TransitionCollection < Array
4
-
5
8
  # Whether to skip running the action for each transition's machine
6
9
  attr_reader :skip_actions
7
10
 
@@ -26,10 +29,10 @@ module StateMachines
26
29
  reject! { |transition| !transition }
27
30
 
28
31
  attributes = map { |transition| transition.attribute }.uniq
29
- fail ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != length
32
+ raise ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != length
30
33
 
31
- options.assert_valid_keys(:actions, :after, :use_transactions)
32
- options = {actions: true, after: true, use_transactions: true}.merge(options)
34
+ StateMachines::OptionsValidator.assert_valid_keys!(options, :actions, :after, :use_transactions)
35
+ options = { actions: true, after: true, use_transactions: true }.merge(options)
33
36
  @skip_actions = !options[:actions]
34
37
  @skip_after = !options[:after]
35
38
  @use_transactions = options[:use_transactions]
@@ -72,11 +75,11 @@ module StateMachines
72
75
  end
73
76
  end
74
77
 
75
- protected
78
+ protected
76
79
 
77
- attr_reader :results #:nodoc:
80
+ attr_reader :results # :nodoc:
78
81
 
79
- private
82
+ private
80
83
 
81
84
  # Is this a valid set of transitions? If the collection was creating with
82
85
  # any +false+ values for transitions, then the the collection will be
@@ -127,7 +130,7 @@ module StateMachines
127
130
  if transition = self[index]
128
131
  throw :halt unless transition.run_callbacks(after: !skip_after) do
129
132
  run_callbacks(index + 1, &block)
130
- {result: results[transition.action], success: success?}
133
+ { result: results[transition.action], success: success? }
131
134
  end
132
135
  else
133
136
  persist
@@ -148,13 +151,13 @@ module StateMachines
148
151
  def run_actions
149
152
  catch_exceptions do
150
153
  @success = if block_given?
151
- result = yield
152
- actions.each { |action| results[action] = result }
153
- !!result
154
- else
155
- actions.compact.each { |action| !skip_actions && (results[action] = object.send(action)) }
156
- results.values.all?
157
- end
154
+ result = yield
155
+ actions.each { |action| results[action] = result }
156
+ !!result
157
+ else
158
+ actions.compact.each { |action| !skip_actions && (results[action] = object.send(action)) }
159
+ results.values.all?
160
+ end
158
161
  end
159
162
  end
160
163
 
@@ -167,12 +170,10 @@ module StateMachines
167
170
  # occur will automatically result in the transition rolling back any changes
168
171
  # that were made to the object involved.
169
172
  def catch_exceptions
170
- begin
171
- yield
172
- rescue
173
- rollback
174
- raise
175
- end
173
+ yield
174
+ rescue StandardError
175
+ rollback
176
+ raise
176
177
  end
177
178
 
178
179
  # Runs a block within a transaction for the object being transitioned. If
@@ -192,11 +193,11 @@ module StateMachines
192
193
  # Represents a collection of transitions that were generated from attribute-
193
194
  # based events
194
195
  class AttributeTransitionCollection < TransitionCollection
195
- def initialize(transitions = [], options = {}) #:nodoc:
196
- super(transitions, {use_transactions: false, actions: false}.merge(options))
196
+ def initialize(transitions = [], options = {}) # :nodoc:
197
+ super(transitions, { use_transactions: false, actions: false }.merge(options))
197
198
  end
198
199
 
199
- private
200
+ private
200
201
 
201
202
  # Hooks into running transition callbacks so that event / event transition
202
203
  # attributes can be properly updated
@@ -212,9 +213,9 @@ module StateMachines
212
213
  # Rollback only if exceptions occur during before callbacks
213
214
  begin
214
215
  super
215
- rescue
216
+ rescue StandardError
216
217
  rollback unless @before_run
217
- @success = nil # mimics ActiveRecord.save behavior on rollback
218
+ @success = nil # mimics ActiveRecord.save behavior on rollback
218
219
  raise
219
220
  end
220
221
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module StateMachines
2
- VERSION = '0.6.0'
4
+ VERSION = '0.30.0'
3
5
  end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'state_machines/version'
2
4
  require 'state_machines/core'
3
- require 'state_machines/core_ext'
5
+ require 'state_machines/core_ext'
6
+ require 'state_machines/stdio_renderer'
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machines
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  - Aaron Pfeifer
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2023-06-30 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
@@ -26,37 +25,36 @@ dependencies:
26
25
  - !ruby/object:Gem::Version
27
26
  version: 1.7.6
28
27
  - !ruby/object:Gem::Dependency
29
- name: rake
28
+ name: minitest
30
29
  requirement: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - ">="
33
32
  - !ruby/object:Gem::Version
34
- version: '0'
33
+ version: '5.4'
35
34
  type: :development
36
35
  prerelease: false
37
36
  version_requirements: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - ">="
40
39
  - !ruby/object:Gem::Version
41
- version: '0'
40
+ version: '5.4'
42
41
  - !ruby/object:Gem::Dependency
43
- name: minitest
42
+ name: rake
44
43
  requirement: !ruby/object:Gem::Requirement
45
44
  requirements:
46
45
  - - ">="
47
46
  - !ruby/object:Gem::Version
48
- version: '5.4'
47
+ version: '0'
49
48
  type: :development
50
49
  prerelease: false
51
50
  version_requirements: !ruby/object:Gem::Requirement
52
51
  requirements:
53
52
  - - ">="
54
53
  - !ruby/object:Gem::Version
55
- version: '5.4'
54
+ version: '0'
56
55
  description: Adds support for creating state machines for attributes on any Ruby class
57
56
  email:
58
57
  - terminale@gmail.com
59
- - aaron@pluginaweek.org
60
58
  executables: []
61
59
  extensions: []
62
60
  extra_rdoc_files: []
@@ -64,7 +62,6 @@ files:
64
62
  - LICENSE.txt
65
63
  - README.md
66
64
  - lib/state_machines.rb
67
- - lib/state_machines/assertions.rb
68
65
  - lib/state_machines/branch.rb
69
66
  - lib/state_machines/callback.rb
70
67
  - lib/state_machines/core.rb
@@ -79,24 +76,44 @@ files:
79
76
  - lib/state_machines/integrations.rb
80
77
  - lib/state_machines/integrations/base.rb
81
78
  - lib/state_machines/machine.rb
79
+ - lib/state_machines/machine/action_hooks.rb
80
+ - lib/state_machines/machine/callbacks.rb
81
+ - lib/state_machines/machine/class_methods.rb
82
+ - lib/state_machines/machine/configuration.rb
83
+ - lib/state_machines/machine/event_methods.rb
84
+ - lib/state_machines/machine/helper_generators.rb
85
+ - lib/state_machines/machine/integration.rb
86
+ - lib/state_machines/machine/parsing.rb
87
+ - lib/state_machines/machine/rendering.rb
88
+ - lib/state_machines/machine/scoping.rb
89
+ - lib/state_machines/machine/state_methods.rb
90
+ - lib/state_machines/machine/utilities.rb
91
+ - lib/state_machines/machine/validation.rb
82
92
  - lib/state_machines/machine_collection.rb
83
93
  - lib/state_machines/macro_methods.rb
84
94
  - lib/state_machines/matcher.rb
85
95
  - lib/state_machines/matcher_helpers.rb
86
96
  - lib/state_machines/node_collection.rb
97
+ - lib/state_machines/options_validator.rb
87
98
  - lib/state_machines/path.rb
88
99
  - lib/state_machines/path_collection.rb
89
100
  - lib/state_machines/state.rb
90
101
  - lib/state_machines/state_collection.rb
91
102
  - lib/state_machines/state_context.rb
103
+ - lib/state_machines/stdio_renderer.rb
104
+ - lib/state_machines/syntax_validator.rb
105
+ - lib/state_machines/test_helper.rb
92
106
  - lib/state_machines/transition.rb
93
107
  - lib/state_machines/transition_collection.rb
94
108
  - lib/state_machines/version.rb
95
109
  homepage: https://github.com/state-machines/state_machines
96
110
  licenses:
97
111
  - MIT
98
- metadata: {}
99
- post_install_message:
112
+ metadata:
113
+ changelog_uri: https://github.com/state-machines/state_machines/blob/master/CHANGELOG.md
114
+ homepage_uri: https://github.com/state-machines/state_machines
115
+ source_code_uri: https://github.com/state-machines/state_machines
116
+ rubygems_mfa_required: 'true'
100
117
  rdoc_options: []
101
118
  require_paths:
102
119
  - lib
@@ -104,15 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
121
  requirements:
105
122
  - - ">="
106
123
  - !ruby/object:Gem::Version
107
- version: 3.0.0
124
+ version: 3.2.0
108
125
  required_rubygems_version: !ruby/object:Gem::Requirement
109
126
  requirements:
110
127
  - - ">="
111
128
  - !ruby/object:Gem::Version
112
129
  version: '0'
113
130
  requirements: []
114
- rubygems_version: 3.4.10
115
- signing_key:
131
+ rubygems_version: 3.6.9
116
132
  specification_version: 4
117
133
  summary: State machines for attributes
118
134
  test_files: []
@@ -1,40 +0,0 @@
1
- class Hash
2
- # Provides a set of helper methods for making assertions about the content
3
- # of various objects
4
-
5
- unless respond_to?(:assert_valid_keys)
6
- # Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
7
- # on a mismatch. Note that keys are NOT treated indifferently, meaning if you
8
- # use strings for keys but assert symbols as keys, this will fail.
9
- #
10
- # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
11
- # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
12
- # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
13
- # Code from ActiveSupport
14
- def assert_valid_keys(*valid_keys)
15
- valid_keys.flatten!
16
- each_key do |k|
17
- unless valid_keys.include?(k)
18
- raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
19
- end
20
- end
21
- end
22
- end
23
-
24
- # Validates that the given hash only includes at *most* one of a set of
25
- # exclusive keys. If more than one key is found, an ArgumentError will be
26
- # raised.
27
- #
28
- # == Examples
29
- #
30
- # options = {:only => :on, :except => :off}
31
- # options.assert_exclusive_keys(:only) # => nil
32
- # options.assert_exclusive_keys(:except) # => nil
33
- # options.assert_exclusive_keys(:only, :except) # => ArgumentError: Conflicting keys: only, except
34
- # options.assert_exclusive_keys(:only, :except, :with) # => ArgumentError: Conflicting keys: only, except
35
- def assert_exclusive_keys(*exclusive_keys)
36
- conflicting_keys = exclusive_keys & keys
37
- raise ArgumentError, "Conflicting keys: #{conflicting_keys.join(', ')}" unless conflicting_keys.length <= 1
38
- end
39
- end
40
-