circulator 2.1.4 → 2.1.5
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 +4 -18
- data/lib/circulator/flow.rb +42 -6
- data/lib/circulator/version.rb +1 -1
- data/lib/circulator.rb +31 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c511772e424334259794bfff3d3507e5740fc98f0a130c66bf1d77a41862f6a
|
|
4
|
+
data.tar.gz: 7ee239ac23cbd6998a6d1689f8d6729983b42a6d6081e092465483ee19013107
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63e6165e93a276d89ac6c469c3e8b5db41e6b184cde3290a9ff1332992e7f27a06cf1cd8f8b93c32100986c27be3761d0cfbc9ef7c69414b0cd379f32813c018
|
|
7
|
+
data.tar.gz: 586cdd39db8b98b10465e84c02de948a44f39c556ae270ea6a68ea8f90ca8c0b61af37917419dfe599e39e882584101169f932671996edbeeb97d6e9debe806d
|
data/CHANGELOG.md
CHANGED
|
@@ -5,24 +5,10 @@ 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.5] - 2025-11-15
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- available_flow? predicate method (122e2ab)
|
|
16
|
-
- Documentation for query methods (a75507e)
|
|
17
|
-
|
|
18
|
-
### Changed
|
|
19
|
-
|
|
20
|
-
- Validate symbol allow_if at definition time (392eac5)
|
|
21
|
-
|
|
22
|
-
### Removed
|
|
23
|
-
|
|
24
|
-
- Redundant respond_to? check (40ff669)
|
|
25
|
-
|
|
26
|
-
### Fixed
|
|
27
|
-
|
|
28
|
-
- DOT syntax error for states ending with ? (65b503e)
|
|
12
|
+
- Circulator.extension to define changes to existing state machines. (0f0a50f)
|
|
13
|
+
- Circulator.default_flow_proc to allow for custom storage objects. (7ddc442)
|
|
14
|
+
- Test support for custom flows storage with libraries like Contours::BlendedHash. (c7f1f26)
|
data/lib/circulator/flow.rb
CHANGED
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module Circulator
|
|
4
4
|
class Flow
|
|
5
|
-
def initialize(klass, attribute_name, states = Set.new, &block)
|
|
5
|
+
def initialize(klass, attribute_name, states = Set.new, extension: false, flows_proc: Circulator.default_flow_proc, &block)
|
|
6
6
|
@klass = klass
|
|
7
7
|
@attribute_name = attribute_name
|
|
8
8
|
@states = states
|
|
9
9
|
@no_action = ->(attribute_name, action) { raise "No action found for the current state of #{attribute_name} (#{send(attribute_name)}): #{action}" }
|
|
10
|
-
@
|
|
11
|
-
|
|
10
|
+
@flows_proc = flows_proc
|
|
11
|
+
@transition_map = flows_proc.call
|
|
12
|
+
|
|
13
|
+
# Execute the main flow block
|
|
14
|
+
instance_eval(&block) if block
|
|
15
|
+
|
|
16
|
+
# Apply any registered extensions (unless explicitly disabled)
|
|
17
|
+
apply_extensions unless extension
|
|
12
18
|
end
|
|
13
19
|
attr_reader :transition_map
|
|
14
20
|
|
|
@@ -28,7 +34,7 @@ module Circulator
|
|
|
28
34
|
validate_allow_if(allow_if)
|
|
29
35
|
end
|
|
30
36
|
|
|
31
|
-
@transition_map[name] ||=
|
|
37
|
+
@transition_map[name] ||= @flows_proc.call
|
|
32
38
|
selected_state = (from == :__not_specified__) ? @current_state : from
|
|
33
39
|
|
|
34
40
|
# Handle nil case specially - convert to [nil] instead of []
|
|
@@ -48,8 +54,10 @@ module Circulator
|
|
|
48
54
|
@states.add(to_state)
|
|
49
55
|
end
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
# Build transition data hash with all keys at once
|
|
58
|
+
transition_data = {to:, block:}
|
|
59
|
+
transition_data[:allow_if] = allow_if if allow_if
|
|
60
|
+
@transition_map[name][from_state] = transition_data
|
|
53
61
|
end
|
|
54
62
|
end
|
|
55
63
|
|
|
@@ -132,5 +140,33 @@ module Circulator
|
|
|
132
140
|
raise ArgumentError, "allow_if references invalid states #{invalid_states.inspect} for :#{attribute_name}. Valid states: #{referenced_states.to_a.inspect}"
|
|
133
141
|
end
|
|
134
142
|
end
|
|
143
|
+
|
|
144
|
+
def apply_extensions
|
|
145
|
+
# Look up extensions for this class and attribute
|
|
146
|
+
class_name = if @klass.is_a?(Class)
|
|
147
|
+
@klass.name || @klass.to_s
|
|
148
|
+
else
|
|
149
|
+
Circulator.model_key(@klass)
|
|
150
|
+
end
|
|
151
|
+
key = "#{class_name}:#{@attribute_name}"
|
|
152
|
+
extensions = Circulator.extensions[key]
|
|
153
|
+
|
|
154
|
+
# Apply each extension by creating a new Flow and merging its transition_map
|
|
155
|
+
extensions.each do |extension_block|
|
|
156
|
+
extension_flow = Flow.new(@klass, @attribute_name, @states, extension: true, flows_proc: @flows_proc, &extension_block)
|
|
157
|
+
extension_flow.transition_map.each do |action, transitions|
|
|
158
|
+
@transition_map[action] = if @transition_map[action]
|
|
159
|
+
@transition_map[action].merge(transitions)
|
|
160
|
+
else
|
|
161
|
+
transitions
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Merge states from extension
|
|
166
|
+
extension_flow.instance_variable_get(:@states).each do |state|
|
|
167
|
+
@states.add(state)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
135
171
|
end
|
|
136
172
|
end
|
data/lib/circulator/version.rb
CHANGED
data/lib/circulator.rb
CHANGED
|
@@ -2,6 +2,32 @@ require "circulator/version"
|
|
|
2
2
|
require "circulator/flow"
|
|
3
3
|
|
|
4
4
|
module Circulator
|
|
5
|
+
# Global registry for extensions
|
|
6
|
+
@extensions = Hash.new { |h, k| h[k] = [] }
|
|
7
|
+
|
|
8
|
+
@default_flow_proc = ::Hash.method(:new)
|
|
9
|
+
class << self
|
|
10
|
+
attr_reader :extensions
|
|
11
|
+
attr_reader :default_flow_proc
|
|
12
|
+
|
|
13
|
+
# Register an extension for a specific class and attribute
|
|
14
|
+
#
|
|
15
|
+
# Example:
|
|
16
|
+
#
|
|
17
|
+
# Circulator.extension(:Document, :status) do
|
|
18
|
+
# state :pending do
|
|
19
|
+
# action :send_to_legal, to: :legal_review
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# Extensions are automatically applied when the class defines its flow
|
|
24
|
+
def extension(class_name, attribute_name, &block)
|
|
25
|
+
raise ArgumentError, "Block required for extension" unless block_given?
|
|
26
|
+
|
|
27
|
+
key = "#{class_name}:#{attribute_name}"
|
|
28
|
+
@extensions[key] << block
|
|
29
|
+
end
|
|
30
|
+
end
|
|
5
31
|
# Declare a flow for an attribute.
|
|
6
32
|
#
|
|
7
33
|
# Specify the attribute to be used for states and actions.
|
|
@@ -125,11 +151,12 @@ module Circulator
|
|
|
125
151
|
# test_object.flow(:unknown, :status, "signal")
|
|
126
152
|
# # Will raise an UnhandledSignalError
|
|
127
153
|
#
|
|
128
|
-
def flow(attribute_name, model: to_s, &block)
|
|
129
|
-
@flows ||=
|
|
154
|
+
def flow(attribute_name, model: to_s, flows_proc: Circulator.default_flow_proc, &block)
|
|
155
|
+
@flows ||= flows_proc.call
|
|
130
156
|
model_key = Circulator.model_key(model)
|
|
131
|
-
@flows[model_key] ||=
|
|
132
|
-
|
|
157
|
+
@flows[model_key] ||= flows_proc.call
|
|
158
|
+
# Pass the flows_proc to Flow so it can create transition_maps of the same type
|
|
159
|
+
@flows[model_key][attribute_name] = Flow.new(self, attribute_name, flows_proc:, &block)
|
|
133
160
|
|
|
134
161
|
flow_module = ancestors.find { |ancestor|
|
|
135
162
|
ancestor.name.to_s =~ /FlowMethods/
|