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