circulator 2.1.9 → 2.1.11
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/CHANGELOG.md +8 -3
- data/README.md +97 -6
- data/Rakefile +1 -0
- data/lib/circulator/flow.rb +107 -4
- data/lib/circulator/version.rb +1 -1
- data/lib/circulator.rb +64 -73
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9232620710fbfe075e26c0ae3bcf1dea0794cdca188b0a9a0ff8529362573ede
|
|
4
|
+
data.tar.gz: c45d7ac1bdf89fe21107905811d36d0ef4dca37370ea89686d5433cef1aeaaf5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bfbc391566c29b7dd18005c56c37c21c9d976eeebc2705f92ea726074b93807daf47528f34e07bb449be3a38bca797067742abc64e1d862162cc18924049b47d
|
|
7
|
+
data.tar.gz: 9edd59a4985b266ad4c590d3e4d05bbe41f381e85a149b84de08adeabf9e1ff141d0a2b5d5dee759e1e1ba8d4dd740c8e33036331694614951994fddab6d4448
|
data/CHANGELOG.md
CHANGED
|
@@ -5,12 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [2.1.
|
|
8
|
+
## [2.1.11] - 2026-02-09
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- action_missing alias for no_action to clarify intent (c576a7c)
|
|
13
13
|
|
|
14
14
|
### Changed
|
|
15
15
|
|
|
16
|
-
-
|
|
16
|
+
- Improved inline documentation for no_action and around methods (c576a7c)
|
|
17
|
+
- Added method documentation for Flow DSL methods (2546989)
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
- @no_action internal instance_variable and switched to @action_missing Version: minor (c576a7c)
|
data/README.md
CHANGED
|
@@ -10,6 +10,7 @@ A lightweight and flexible state machine implementation for Ruby that allows you
|
|
|
10
10
|
- **Conditional Transitions**: Support for guards and conditional logic
|
|
11
11
|
- **Nested State Dependencies**: State machines can depend on the state of other attributes
|
|
12
12
|
- **Transition Callbacks**: Execute code before, during, or after transitions
|
|
13
|
+
- **Around Wrapping**: Wrap all transitions in a flow with shared logic (e.g., `with_lock`)
|
|
13
14
|
- **Multiple State Machines**: Define multiple independent state machines per class
|
|
14
15
|
- **Framework Agnostic**: Works with plain Ruby objects, no Rails or ActiveRecord required
|
|
15
16
|
- **100% Test Coverage**: Thoroughly tested with comprehensive test suite
|
|
@@ -293,6 +294,101 @@ class Payment
|
|
|
293
294
|
end
|
|
294
295
|
```
|
|
295
296
|
|
|
297
|
+
#### Handling Missing Transitions with `action_missing`
|
|
298
|
+
|
|
299
|
+
By default, if you call an action on an object whose current state doesn't define a transition for that action, Circulator raises an error. Use `action_missing` (aliased as `no_action`) to customize this behavior.
|
|
300
|
+
|
|
301
|
+
This is triggered when, for example, you call `order.status_ship` but the object is in `:pending` and `:ship` only has a transition defined from `:processing`.
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
class Order
|
|
305
|
+
extend Circulator
|
|
306
|
+
|
|
307
|
+
attr_accessor :status
|
|
308
|
+
|
|
309
|
+
flow :status do
|
|
310
|
+
action_missing do |attribute_name, action|
|
|
311
|
+
Rails.logger.warn "No #{action} transition from #{send(attribute_name)} for #{attribute_name}"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
state :pending do
|
|
315
|
+
action :process, to: :processing
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
state :processing do
|
|
319
|
+
action :ship, to: :shipped
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
order = Order.new
|
|
325
|
+
order.status = :pending
|
|
326
|
+
|
|
327
|
+
order.status_ship # => logs warning instead of raising
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Wrapping Transitions with `around`
|
|
331
|
+
|
|
332
|
+
Use the `around` block to wrap all transitions in a flow with shared logic. The block receives a `transition` proc that you must call for the transition to execute:
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
class Order
|
|
336
|
+
extend Circulator
|
|
337
|
+
|
|
338
|
+
attr_accessor :status
|
|
339
|
+
|
|
340
|
+
flow :status do
|
|
341
|
+
around do |transition|
|
|
342
|
+
puts "before transition"
|
|
343
|
+
transition.call
|
|
344
|
+
puts "after transition"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
state :pending do
|
|
348
|
+
action :approve, to: :approved
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The `around` block is `instance_exec`'d on the instance (consistent with transition blocks and `allow_if` procs), so `self` is the object being transitioned.
|
|
355
|
+
|
|
356
|
+
**Transactional safety with ActiveRecord:**
|
|
357
|
+
|
|
358
|
+
This is particularly useful for wrapping transitions in a database lock to prevent race conditions:
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
class Order < ApplicationRecord
|
|
362
|
+
extend Circulator
|
|
363
|
+
|
|
364
|
+
flow :status do
|
|
365
|
+
around do |transition|
|
|
366
|
+
with_lock { transition.call }
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
state :pending do
|
|
370
|
+
action :approve, to: :approved do
|
|
371
|
+
self.approved_at = Time.current
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
state :approved do
|
|
376
|
+
action :ship, to: :shipped
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# The guard check, transition block, state change, and caller block
|
|
382
|
+
# all execute inside the lock — no wrapper methods needed
|
|
383
|
+
order.status_approve
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Key behaviors:**
|
|
387
|
+
|
|
388
|
+
- The state read, guard checks (`allow_if`), and state change all run **inside** the wrapper, so the entire check-then-act sequence is atomic
|
|
389
|
+
- If `transition.call` is never called, the transition does not execute
|
|
390
|
+
- Each flow can have its own `around` block (or none) — flows without one behave exactly as before
|
|
391
|
+
|
|
296
392
|
#### Extending Flows
|
|
297
393
|
|
|
298
394
|
You can extend existing flows using `Circulator.extension`. This is useful for plugins, multi-tenant applications, or conditional feature enhancement. Extensions are registered globally and automatically applied when a class defines its flow.
|
|
@@ -497,12 +593,7 @@ To install this gem onto your local machine, run bundle exec rake install.
|
|
|
497
593
|
|
|
498
594
|
This project is managed with [Reissue](https://github.com/SOFware/reissue).
|
|
499
595
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
To release a new version:
|
|
503
|
-
|
|
504
|
-
bundle exec rake build:checksum
|
|
505
|
-
bundle exec rake release
|
|
596
|
+
Releases are automated via the [shared release workflow](https://github.com/SOFware/reissue/blob/main/.github/workflows/SHARED_WORKFLOW_README.md). Trigger a release by running the "Release gem to RubyGems.org" workflow from the Actions tab.
|
|
506
597
|
|
|
507
598
|
## Contributing
|
|
508
599
|
|
data/Rakefile
CHANGED
data/lib/circulator/flow.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Circulator
|
|
|
6
6
|
@klass = klass
|
|
7
7
|
@attribute_name = attribute_name
|
|
8
8
|
@states = states
|
|
9
|
-
@
|
|
9
|
+
@action_missing = ->(attribute_name, action) { raise "No action found for the current state of #{attribute_name} (#{send(attribute_name)}): #{action}" }
|
|
10
10
|
@flows_proc = flows_proc
|
|
11
11
|
@transition_map = flows_proc.call
|
|
12
12
|
|
|
@@ -18,6 +18,18 @@ module Circulator
|
|
|
18
18
|
end
|
|
19
19
|
attr_reader :transition_map
|
|
20
20
|
|
|
21
|
+
# Declares a state in the flow. The block is evaluated in the context
|
|
22
|
+
# of the flow, allowing you to define actions that transition from
|
|
23
|
+
# this state. A state with no block acts as a terminal state.
|
|
24
|
+
#
|
|
25
|
+
# flow(:status) do
|
|
26
|
+
# state :pending do
|
|
27
|
+
# action :approve, to: :approved
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# state :approved # terminal state
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
21
33
|
def state(name, &block)
|
|
22
34
|
name = name.to_sym if name.respond_to?(:to_sym)
|
|
23
35
|
@states.add(name)
|
|
@@ -26,6 +38,34 @@ module Circulator
|
|
|
26
38
|
remove_instance_variable(:@current_state)
|
|
27
39
|
end
|
|
28
40
|
|
|
41
|
+
# Declares a transition from one or more states to a destination state.
|
|
42
|
+
# Must be called inside a +state+ block, or with an explicit +from:+ option.
|
|
43
|
+
#
|
|
44
|
+
# Generates a method named +<attribute>_<action>+ on the class that
|
|
45
|
+
# performs the transition when called.
|
|
46
|
+
#
|
|
47
|
+
# ==== Options
|
|
48
|
+
#
|
|
49
|
+
# +to+:: The destination state (Symbol) or a callable that returns
|
|
50
|
+
# the destination state at runtime.
|
|
51
|
+
# +from+:: One or more source states. Defaults to the enclosing
|
|
52
|
+
# +state+ block. Pass an Array to define the same action
|
|
53
|
+
# from multiple states.
|
|
54
|
+
# +allow_if+:: A guard condition that must be truthy for the transition
|
|
55
|
+
# to proceed. Accepts a Proc, Symbol (method name), Hash
|
|
56
|
+
# (checks another flow's state), or Array of those.
|
|
57
|
+
# +&block+:: A block executed on the instance during the transition,
|
|
58
|
+
# before the state is changed.
|
|
59
|
+
#
|
|
60
|
+
# state :pending do
|
|
61
|
+
# action :approve, to: :approved, allow_if: :reviewer? do
|
|
62
|
+
# self.approved_at = Time.now
|
|
63
|
+
# end
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# # Or with an explicit from:
|
|
67
|
+
# action :cancel, from: [:pending, :processing], to: :cancelled
|
|
68
|
+
#
|
|
29
69
|
def action(name, to:, from: :__not_specified__, allow_if: nil, &block)
|
|
30
70
|
raise "You must be in a state block or have a `from` option to declare an action" unless defined?(@current_state) || from != :__not_specified__
|
|
31
71
|
|
|
@@ -61,6 +101,22 @@ module Circulator
|
|
|
61
101
|
end
|
|
62
102
|
end
|
|
63
103
|
|
|
104
|
+
# Attaches an +allow_if+ guard to an existing action after it has been
|
|
105
|
+
# defined. This is useful when the guard logic needs to be defined
|
|
106
|
+
# separately from the action itself, such as in an extension.
|
|
107
|
+
#
|
|
108
|
+
# Must be called inside a +state+ block, or with an explicit +from:+ option.
|
|
109
|
+
# The action must already exist in the transition map.
|
|
110
|
+
#
|
|
111
|
+
# Example:
|
|
112
|
+
#
|
|
113
|
+
# flow(:status) do
|
|
114
|
+
# state :pending do
|
|
115
|
+
# action :approve, to: :approved
|
|
116
|
+
# action_allowed(:approve) { current_user.admin? }
|
|
117
|
+
# end
|
|
118
|
+
# end
|
|
119
|
+
#
|
|
64
120
|
def action_allowed(name, from: :__not_specified__, &block)
|
|
65
121
|
raise "You must be in a state block or have a `from` option to declare an action" unless defined?(@current_state) || from != :__not_specified__
|
|
66
122
|
|
|
@@ -80,11 +136,58 @@ module Circulator
|
|
|
80
136
|
end
|
|
81
137
|
end
|
|
82
138
|
|
|
83
|
-
|
|
139
|
+
# Called when an action is invoked but no transition is defined for the
|
|
140
|
+
# current state. For example, if an object is in :approved and you call
|
|
141
|
+
# an action that only has a transition from :pending, this block runs.
|
|
142
|
+
#
|
|
143
|
+
# By default, raises an error. Override to silently ignore, log, or
|
|
144
|
+
# handle the missing transition however you like.
|
|
145
|
+
#
|
|
146
|
+
# The block receives two arguments: the attribute name and the action name.
|
|
147
|
+
#
|
|
148
|
+
# Example:
|
|
149
|
+
#
|
|
150
|
+
# flow(:status) do
|
|
151
|
+
# action_missing do |attribute_name, action|
|
|
152
|
+
# Rails.logger.warn("No transition for #{attribute_name} in #{action}")
|
|
153
|
+
# end
|
|
154
|
+
# end
|
|
155
|
+
def action_missing(&block)
|
|
156
|
+
if block_given?
|
|
157
|
+
@action_missing = block
|
|
158
|
+
else
|
|
159
|
+
@action_missing
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
alias_method :no_action, :action_missing
|
|
163
|
+
|
|
164
|
+
# Wraps every transition in this flow. The block receives a lambda that
|
|
165
|
+
# executes the transition logic (guard check, state change, and any
|
|
166
|
+
# transition block). You must call +transition.call+ for the transition
|
|
167
|
+
# to actually happen — omitting it prevents the state change entirely.
|
|
168
|
+
#
|
|
169
|
+
# Useful for wrapping transitions in database transactions, logging,
|
|
170
|
+
# instrumentation, or any before/after behavior.
|
|
171
|
+
#
|
|
172
|
+
# Example:
|
|
173
|
+
#
|
|
174
|
+
# flow(:status) do
|
|
175
|
+
# around do |transition|
|
|
176
|
+
# ActiveRecord::Base.transaction do
|
|
177
|
+
# transition.call
|
|
178
|
+
# end
|
|
179
|
+
# end
|
|
180
|
+
#
|
|
181
|
+
# state :pending do
|
|
182
|
+
# action :approve, to: :approved
|
|
183
|
+
# end
|
|
184
|
+
# end
|
|
185
|
+
#
|
|
186
|
+
def around(&block)
|
|
84
187
|
if block_given?
|
|
85
|
-
@
|
|
188
|
+
@around = block
|
|
86
189
|
else
|
|
87
|
-
@
|
|
190
|
+
@around
|
|
88
191
|
end
|
|
89
192
|
end
|
|
90
193
|
|
data/lib/circulator/version.rb
CHANGED
data/lib/circulator.rb
CHANGED
|
@@ -116,14 +116,14 @@ module Circulator
|
|
|
116
116
|
flow_module.remove_method(method_name)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
klass.send(:define_flow_method, attribute_name
|
|
119
|
+
klass.send(:define_flow_method, attribute_name:, action:, transitions:, object:, owner: flow_module)
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
# Define predicate methods for any new states
|
|
123
123
|
states = flow.instance_variable_get(:@states)
|
|
124
124
|
states.each do |state|
|
|
125
125
|
next if state.nil?
|
|
126
|
-
klass.send(:define_state_method, attribute_name
|
|
126
|
+
klass.send(:define_state_method, attribute_name:, state:, object:, owner: flow_module)
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
129
|
end
|
|
@@ -253,6 +253,21 @@ module Circulator
|
|
|
253
253
|
# test_object.flow(:unknown, :status, "signal")
|
|
254
254
|
# # Will raise an UnhandledSignalError
|
|
255
255
|
#
|
|
256
|
+
# You can also provide an around block to wrap the flow logic.
|
|
257
|
+
#
|
|
258
|
+
# Example:
|
|
259
|
+
#
|
|
260
|
+
# flow(:status) do
|
|
261
|
+
# around do |flow_logic|
|
|
262
|
+
# with_logging do
|
|
263
|
+
# flow_logic.call
|
|
264
|
+
# end
|
|
265
|
+
# end
|
|
266
|
+
# end
|
|
267
|
+
#
|
|
268
|
+
# test_object.status_approve
|
|
269
|
+
# # Will log the flow logic according to the with_logging block behavior
|
|
270
|
+
#
|
|
256
271
|
def flow(attribute_name, model: to_s, flows_proc: Circulator.default_flow_proc, &block)
|
|
257
272
|
@flows ||= flows_proc.call
|
|
258
273
|
model_key = Circulator.model_key(model)
|
|
@@ -311,65 +326,62 @@ module Circulator
|
|
|
311
326
|
raise ArgumentError, "Method already defined: #{object_attribute_method}" if owner.method_defined?(object_attribute_method)
|
|
312
327
|
|
|
313
328
|
owner.define_method(object_attribute_method) do |*args, flow_target: self, **kwargs, &block|
|
|
314
|
-
|
|
329
|
+
flow_logic = -> {
|
|
330
|
+
current_value = flow_target.send(attribute_name)
|
|
315
331
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
332
|
+
transition = if current_value.respond_to?(:to_sym)
|
|
333
|
+
transitions[current_value.to_sym]
|
|
334
|
+
else
|
|
335
|
+
transitions[current_value]
|
|
336
|
+
end
|
|
321
337
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
338
|
+
unless transition
|
|
339
|
+
flow_target.instance_exec(attribute_name, action, &flows.dig(Circulator.model_key(flow_target), attribute_name).no_action)
|
|
340
|
+
return
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
return if transition[:allow_if] && !Circulator.evaluate_guard(flow_target, transition[:allow_if], *args, **kwargs)
|
|
344
|
+
|
|
345
|
+
flow_target.instance_exec(*args, **kwargs, &transition[:block]) if transition[:block]
|
|
326
346
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if transition[:allow_if].is_a?(Array)
|
|
330
|
-
return unless transition[:allow_if].all? do |guard|
|
|
331
|
-
case guard
|
|
332
|
-
when Symbol
|
|
333
|
-
flow_target.send(guard, *args, **kwargs)
|
|
334
|
-
when Proc
|
|
335
|
-
flow_target.instance_exec(*args, **kwargs, &guard)
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
# Handle hash-based allow_if (checking other attribute states)
|
|
339
|
-
elsif transition[:allow_if].is_a?(Hash)
|
|
340
|
-
attribute_name_to_check, valid_states = transition[:allow_if].first
|
|
341
|
-
current_state = flow_target.send(attribute_name_to_check)
|
|
342
|
-
|
|
343
|
-
# Convert current state to symbol if possible
|
|
344
|
-
current_state = current_state.to_sym if current_state.respond_to?(:to_sym)
|
|
345
|
-
|
|
346
|
-
# Convert valid_states to array of symbols
|
|
347
|
-
valid_states_array = Array(valid_states).map { |s| s.respond_to?(:to_sym) ? s.to_sym : s }
|
|
348
|
-
|
|
349
|
-
# Return early if current state is not in the valid states
|
|
350
|
-
return unless valid_states_array.include?(current_state)
|
|
351
|
-
elsif transition[:allow_if].is_a?(Symbol)
|
|
352
|
-
# Handle symbol-based allow_if (method name)
|
|
353
|
-
return unless flow_target.send(transition[:allow_if])
|
|
347
|
+
if transition[:to].respond_to?(:call)
|
|
348
|
+
flow_target.send("#{attribute_name}=", flow_target.instance_exec(*args, **kwargs, &transition[:to]))
|
|
354
349
|
else
|
|
355
|
-
#
|
|
356
|
-
|
|
350
|
+
flow_target.send("#{attribute_name}=", transition[:to])
|
|
351
|
+
end.tap do
|
|
352
|
+
flow_target.instance_exec(*args, **kwargs, &block) if block
|
|
357
353
|
end
|
|
358
|
-
|
|
354
|
+
}
|
|
359
355
|
|
|
360
|
-
|
|
361
|
-
flow_target.instance_exec(*args, **kwargs, &transition[:block])
|
|
362
|
-
end
|
|
356
|
+
around_block = flows.dig(Circulator.model_key(flow_target), attribute_name)&.around
|
|
363
357
|
|
|
364
|
-
if
|
|
365
|
-
flow_target.
|
|
358
|
+
if around_block
|
|
359
|
+
flow_target.instance_exec(flow_logic, &around_block)
|
|
366
360
|
else
|
|
367
|
-
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
|
|
361
|
+
flow_logic.call
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
module_function def evaluate_guard(target, allow_if, *args, **kwargs)
|
|
367
|
+
case allow_if
|
|
368
|
+
when Array
|
|
369
|
+
allow_if.all? do |guard|
|
|
370
|
+
case guard
|
|
371
|
+
when Symbol then target.send(guard, *args, **kwargs)
|
|
372
|
+
when Proc then target.instance_exec(*args, **kwargs, &guard)
|
|
371
373
|
end
|
|
372
374
|
end
|
|
375
|
+
when Hash
|
|
376
|
+
attribute_name, valid_states = allow_if.first
|
|
377
|
+
current_state = target.send(attribute_name)
|
|
378
|
+
current_state = current_state.to_sym if current_state.respond_to?(:to_sym)
|
|
379
|
+
valid_states_array = Array(valid_states).map { |s| s.respond_to?(:to_sym) ? s.to_sym : s }
|
|
380
|
+
valid_states_array.include?(current_state)
|
|
381
|
+
when Symbol
|
|
382
|
+
target.send(allow_if, *args, **kwargs)
|
|
383
|
+
else
|
|
384
|
+
target.instance_exec(*args, **kwargs, &allow_if)
|
|
373
385
|
end
|
|
374
386
|
end
|
|
375
387
|
|
|
@@ -499,28 +511,7 @@ module Circulator
|
|
|
499
511
|
end
|
|
500
512
|
|
|
501
513
|
def check_allow_if(allow_if, *args, **kwargs)
|
|
502
|
-
|
|
503
|
-
when Array
|
|
504
|
-
# All guards in array must be true (AND logic)
|
|
505
|
-
allow_if.all? do |guard|
|
|
506
|
-
case guard
|
|
507
|
-
when Symbol
|
|
508
|
-
send(guard, *args, **kwargs)
|
|
509
|
-
when Proc
|
|
510
|
-
instance_exec(*args, **kwargs, &guard)
|
|
511
|
-
end
|
|
512
|
-
end
|
|
513
|
-
when Hash
|
|
514
|
-
attribute_name, valid_states = allow_if.first
|
|
515
|
-
current_state = send(attribute_name)
|
|
516
|
-
current_state = current_state.to_sym if current_state.respond_to?(:to_sym)
|
|
517
|
-
valid_states_array = Array(valid_states).map { |s| s.respond_to?(:to_sym) ? s.to_sym : s }
|
|
518
|
-
valid_states_array.include?(current_state)
|
|
519
|
-
when Symbol
|
|
520
|
-
send(allow_if, *args, **kwargs)
|
|
521
|
-
else # Proc
|
|
522
|
-
instance_exec(*args, **kwargs, &allow_if)
|
|
523
|
-
end
|
|
514
|
+
Circulator.evaluate_guard(self, allow_if, *args, **kwargs)
|
|
524
515
|
end
|
|
525
516
|
end
|
|
526
517
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: circulator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jim Gay
|
|
@@ -46,7 +46,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: '0'
|
|
48
48
|
requirements: []
|
|
49
|
-
rubygems_version:
|
|
49
|
+
rubygems_version: 3.6.9
|
|
50
50
|
specification_version: 4
|
|
51
51
|
summary: Simple state machine
|
|
52
52
|
test_files: []
|