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.
- checksums.yaml +4 -4
- data/README.md +177 -2
- data/lib/state_machines/branch.rb +16 -15
- data/lib/state_machines/callback.rb +11 -12
- data/lib/state_machines/core.rb +1 -3
- data/lib/state_machines/error.rb +5 -4
- data/lib/state_machines/eval_helpers.rb +83 -45
- data/lib/state_machines/event.rb +37 -27
- data/lib/state_machines/event_collection.rb +4 -5
- data/lib/state_machines/extensions.rb +5 -5
- data/lib/state_machines/helper_module.rb +1 -1
- data/lib/state_machines/integrations/base.rb +1 -1
- data/lib/state_machines/integrations.rb +11 -14
- data/lib/state_machines/machine/action_hooks.rb +53 -0
- data/lib/state_machines/machine/callbacks.rb +59 -0
- data/lib/state_machines/machine/class_methods.rb +25 -11
- data/lib/state_machines/machine/configuration.rb +124 -0
- data/lib/state_machines/machine/event_methods.rb +59 -0
- data/lib/state_machines/machine/helper_generators.rb +125 -0
- data/lib/state_machines/machine/integration.rb +70 -0
- data/lib/state_machines/machine/parsing.rb +77 -0
- data/lib/state_machines/machine/rendering.rb +17 -0
- data/lib/state_machines/machine/scoping.rb +44 -0
- data/lib/state_machines/machine/state_methods.rb +101 -0
- data/lib/state_machines/machine/utilities.rb +85 -0
- data/lib/state_machines/machine/validation.rb +39 -0
- data/lib/state_machines/machine.rb +75 -618
- data/lib/state_machines/machine_collection.rb +21 -15
- data/lib/state_machines/macro_methods.rb +2 -2
- data/lib/state_machines/matcher.rb +6 -6
- data/lib/state_machines/matcher_helpers.rb +1 -1
- data/lib/state_machines/node_collection.rb +21 -18
- data/lib/state_machines/options_validator.rb +72 -0
- data/lib/state_machines/path.rb +5 -5
- data/lib/state_machines/path_collection.rb +5 -4
- data/lib/state_machines/state.rb +29 -11
- data/lib/state_machines/state_collection.rb +3 -3
- data/lib/state_machines/state_context.rb +9 -8
- data/lib/state_machines/stdio_renderer.rb +16 -16
- data/lib/state_machines/syntax_validator.rb +57 -0
- data/lib/state_machines/test_helper.rb +568 -0
- data/lib/state_machines/transition.rb +43 -41
- data/lib/state_machines/transition_collection.rb +25 -26
- data/lib/state_machines/version.rb +1 -1
- metadata +25 -10
- 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
|
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)
|
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)
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
280
|
+
"#<#{self.class} #{%w[attribute event from from_name to to_name].map { |attr| "#{attr}=#{send(attr).inspect}" } * ' '}>"
|
283
281
|
end
|
284
282
|
|
285
|
-
|
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)
|
296
|
-
|
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,
|
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
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
32
|
+
raise ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != length
|
32
33
|
|
33
|
-
|
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
|
-
|
78
|
+
protected
|
78
79
|
|
79
|
-
attr_reader :results
|
80
|
+
attr_reader :results # :nodoc:
|
80
81
|
|
81
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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 = {})
|
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
|
-
|
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
|
218
|
+
@success = nil # mimics ActiveRecord.save behavior on rollback
|
220
219
|
raise
|
221
220
|
end
|
222
221
|
|
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.
|
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:
|
28
|
+
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
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: '
|
40
|
+
version: '5.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
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: '
|
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.
|
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.
|
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
|
-
|