cmdx 1.12.0 → 1.14.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/CHANGELOG.md +88 -71
- data/LICENSE.txt +3 -20
- data/README.md +8 -7
- data/lib/cmdx/attribute.rb +21 -5
- data/lib/cmdx/chain.rb +18 -4
- data/lib/cmdx/context.rb +18 -0
- data/lib/cmdx/executor.rb +35 -30
- data/lib/cmdx/result.rb +45 -2
- data/lib/cmdx/task.rb +22 -1
- data/lib/cmdx/version.rb +1 -1
- data/mkdocs.yml +67 -37
- metadata +3 -57
- data/.cursor/prompts/docs.md +0 -12
- data/.cursor/prompts/llms.md +0 -8
- data/.cursor/prompts/rspec.md +0 -24
- data/.cursor/prompts/yardoc.md +0 -15
- data/.cursor/rules/cursor-instructions.mdc +0 -68
- data/.irbrc +0 -18
- data/.rspec +0 -4
- data/.rubocop.yml +0 -95
- data/.ruby-version +0 -1
- data/.yard-lint.yml +0 -174
- data/.yardopts +0 -7
- data/docs/.DS_Store +0 -0
- data/docs/assets/favicon.ico +0 -0
- data/docs/assets/favicon.svg +0 -1
- data/docs/attributes/coercions.md +0 -155
- data/docs/attributes/defaults.md +0 -77
- data/docs/attributes/definitions.md +0 -283
- data/docs/attributes/naming.md +0 -68
- data/docs/attributes/transformations.md +0 -63
- data/docs/attributes/validations.md +0 -336
- data/docs/basics/chain.md +0 -108
- data/docs/basics/context.md +0 -121
- data/docs/basics/execution.md +0 -96
- data/docs/basics/setup.md +0 -84
- data/docs/callbacks.md +0 -157
- data/docs/configuration.md +0 -314
- data/docs/deprecation.md +0 -145
- data/docs/getting_started.md +0 -126
- data/docs/index.md +0 -134
- data/docs/internationalization.md +0 -126
- data/docs/interruptions/exceptions.md +0 -52
- data/docs/interruptions/faults.md +0 -169
- data/docs/interruptions/halt.md +0 -216
- data/docs/logging.md +0 -94
- data/docs/middlewares.md +0 -191
- data/docs/outcomes/result.md +0 -194
- data/docs/outcomes/states.md +0 -66
- data/docs/outcomes/statuses.md +0 -65
- data/docs/retries.md +0 -121
- data/docs/stylesheets/extra.css +0 -42
- data/docs/tips_and_tricks.md +0 -157
- data/docs/workflows.md +0 -226
- data/examples/active_record_database_transaction.md +0 -27
- data/examples/active_record_query_tagging.md +0 -46
- data/examples/flipper_feature_flags.md +0 -50
- data/examples/paper_trail_whatdunnit.md +0 -39
- data/examples/redis_idempotency.md +0 -71
- data/examples/sentry_error_tracking.md +0 -46
- data/examples/sidekiq_async_execution.md +0 -29
- data/examples/stoplight_circuit_breaker.md +0 -36
- data/src/cmdx-dark-logo.png +0 -0
- data/src/cmdx-favicon.svg +0 -1
- data/src/cmdx-light-logo.png +0 -0
- data/src/cmdx-logo.svg +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 620c2c00dd483c6a4122c7b5acbbe6cf7a07d22260d0331d6f1d54b571ef7648
|
|
4
|
+
data.tar.gz: aff47b340a77b052bc4cd2e201068044a443c1d8f4609163fa45025bdba25d00
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8f6fec277ff0198937013c25a1a1c753d4cf5100ff4920c834dc92fba8df7d2adc1ef3fbcff91c999f1af8c4676c3e8702c71a29a3402efcf36d4a0236163ba
|
|
7
|
+
data.tar.gz: 2233517633c8d9afaafb7a44878b6b3fe0939dabcbe647ceafd9e7ca04450ee1e5e9f6b6f5adb2de58c305b0c2c0940e74ee2d7290f0eb202bda429d76b66992
|
data/CHANGELOG.md
CHANGED
|
@@ -4,166 +4,183 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [
|
|
7
|
+
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Added
|
|
10
|
+
- Add Ruby 4.0 compatibility
|
|
11
|
+
- Add `Context#clear!` method to remove all context data
|
|
12
|
+
- Add `Task.attribute_schema` class method for attribute introspection
|
|
13
|
+
- Add `#to_h` method for attribute serialization
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **BREAKING**: Switch license from MIT to LGPLv3
|
|
17
|
+
- Replace `instance_eval` with `define_singleton_method` for attribute method definitions
|
|
18
|
+
- Move retry count from metadata to result object
|
|
19
|
+
- Exclude non-essential files from gem package
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- Remove public `Result#rolled_back!` method to hide internal implementation
|
|
23
|
+
|
|
24
|
+
## [1.13.0] - 2025-12-23
|
|
10
25
|
|
|
11
26
|
### Added
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
27
|
+
- Add rollback tracking and logging for task execution
|
|
28
|
+
- Add `dry_run` execution option with inheritance support for nested tasks
|
|
29
|
+
- Add `Context#delete` alias for `Context#delete!`
|
|
30
|
+
- Add `Context#merge` alias for `Context#merge!`
|
|
31
|
+
|
|
32
|
+
## [1.12.0] - 2025-12-18
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- Optimize logging ancestor chain lookup performance
|
|
36
|
+
- Use `String#chop` instead of range indexing for improved string performance
|
|
37
|
+
- Make boolean coercion `TRUTHY` and `FALSEY` patterns case-insensitive
|
|
38
|
+
- Enhance YARD documentation using `yard-lint` validation
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
- Remove `handle_*` callback methods in favor of `on(*states_or_statuses)` for flexible state handling
|
|
23
42
|
|
|
24
43
|
## [1.11.0] - 2025-11-08
|
|
25
44
|
|
|
26
|
-
###
|
|
27
|
-
-
|
|
28
|
-
- Update specs to use new `cmdx-rspec` matcher
|
|
45
|
+
### Changed
|
|
46
|
+
- Add conditional requirement support for attribute validation
|
|
47
|
+
- Update specs to use new `cmdx-rspec` matcher naming conventions
|
|
29
48
|
|
|
30
49
|
## [1.10.1] - 2025-11-06
|
|
31
50
|
|
|
32
51
|
### Added
|
|
33
|
-
-
|
|
34
|
-
- Added YARDoc documentation to docs site
|
|
52
|
+
- Add YARDoc documentation to documentation site
|
|
35
53
|
|
|
36
54
|
### Removed
|
|
37
|
-
-
|
|
55
|
+
- Remove unused `Executor#repeator` method
|
|
38
56
|
|
|
39
57
|
## [1.10.0] - 2025-10-26
|
|
40
58
|
|
|
41
59
|
### Added
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
- Added Stoplight circuit breaker integration example
|
|
60
|
+
- Add `rollback` capability to undo operations based on status
|
|
61
|
+
- Add retry mechanism documentation
|
|
45
62
|
|
|
46
|
-
###
|
|
47
|
-
-
|
|
63
|
+
### Changed
|
|
64
|
+
- Extend `retry_jitter` option to accept symbols, procs, and callable objects
|
|
48
65
|
|
|
49
66
|
## [1.9.1] - 2025-10-22
|
|
50
67
|
|
|
51
68
|
### Added
|
|
52
|
-
-
|
|
53
|
-
-
|
|
69
|
+
- Add RBS inline type signatures
|
|
70
|
+
- Add YARDocs for `attr_reader` and `attr_accessor` methods
|
|
54
71
|
|
|
55
72
|
## [1.9.0] - 2025-10-21
|
|
56
73
|
|
|
57
74
|
### Added
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
75
|
+
- Add `transform` option for attribute value transformations
|
|
76
|
+
- Add optional failure backtrace output
|
|
77
|
+
- Add exception handling for non-bang execution methods
|
|
78
|
+
- Add automatic retry mechanism for execution durability
|
|
79
|
+
- Add `to_h` hash coercion support
|
|
80
|
+
- Add MkDocs configuration with Material theme
|
|
64
81
|
|
|
65
82
|
### Changed
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
83
|
+
- Improve task settings initialization performance
|
|
84
|
+
- Improve exception error message clarity
|
|
85
|
+
- Improve parent settings inheritance behavior
|
|
86
|
+
- Clean halt backtrace frames for better readability
|
|
70
87
|
|
|
71
88
|
### Removed
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
89
|
+
- Remove `Freezer` module; consolidate logic into `Executor#freeze_execution!`
|
|
90
|
+
- Remove `task` parameter from callback method signatures
|
|
91
|
+
- Remove `task` and `workflow` arguments from conditional checks
|
|
92
|
+
- Remove chain persistence after execution in specs
|
|
76
93
|
|
|
77
94
|
## [1.8.0] - 2025-09-22
|
|
78
95
|
|
|
79
96
|
### Changed
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
97
|
+
- Generalize locale values for `invalid` and `unspecified` faults
|
|
98
|
+
- Nest attribute error messages under `error` key in metadata
|
|
99
|
+
- Reorder Logstash formatter keys for consistency
|
|
100
|
+
- Improve error messaging for duplicate item definitions
|
|
101
|
+
- Return empty hash `{}` for `nil` hash coercion
|
|
85
102
|
|
|
86
103
|
## [1.7.5] - 2025-09-10
|
|
87
104
|
|
|
88
105
|
### Added
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
106
|
+
- Add `Context#fetch_or_store` method for atomic get-or-set operations
|
|
107
|
+
- Add `Result#ctx` alias for `Result#context`
|
|
108
|
+
- Add `Result#ok?` alias for `Result#good?`
|
|
109
|
+
- Add result deconstruction support for pattern matching
|
|
93
110
|
|
|
94
111
|
## [1.7.4] - 2025-09-03
|
|
95
112
|
|
|
96
113
|
### Added
|
|
97
|
-
-
|
|
98
|
-
-
|
|
114
|
+
- Add errors delegation from result object
|
|
115
|
+
- Add `Errors#full_messages` and `Errors#to_hash` methods
|
|
99
116
|
|
|
100
117
|
## [1.7.3] - 2025-09-03
|
|
101
118
|
|
|
102
119
|
### Changed
|
|
103
|
-
-
|
|
104
|
-
-
|
|
120
|
+
- Use generic validation reason values
|
|
121
|
+
- Move validation full message to `:full_message` key in metadata
|
|
105
122
|
|
|
106
123
|
## [1.7.2] - 2025-09-03
|
|
107
124
|
|
|
108
125
|
### Changed
|
|
109
|
-
-
|
|
126
|
+
- Set correlation ID before proceeding to subsequent execution steps
|
|
110
127
|
|
|
111
128
|
## [1.7.1] - 2025-08-26
|
|
112
129
|
|
|
113
130
|
### Added
|
|
114
|
-
-
|
|
131
|
+
- Add block yielding support to `execute` and `execute!` methods
|
|
115
132
|
|
|
116
133
|
## [1.7.0] - 2025-08-25
|
|
117
134
|
|
|
118
135
|
### Added
|
|
119
|
-
-
|
|
136
|
+
- Add workflow generator
|
|
120
137
|
|
|
121
138
|
### Changed
|
|
122
|
-
-
|
|
123
|
-
-
|
|
139
|
+
- Integrate `cmdx-parallel` functionality into core
|
|
140
|
+
- Integrate `cmdx-i18n` functionality into core
|
|
124
141
|
|
|
125
142
|
## [1.6.2] - 2025-08-24
|
|
126
143
|
|
|
127
144
|
### Changed
|
|
128
|
-
-
|
|
129
|
-
-
|
|
145
|
+
- Prefix railtie I18n with `::` for `CMDx::I18n` compatibility
|
|
146
|
+
- Switch to `cmdx-rspec` for matcher support
|
|
130
147
|
|
|
131
148
|
## [1.6.1] - 2025-08-23
|
|
132
149
|
|
|
133
150
|
### Changed
|
|
134
|
-
-
|
|
135
|
-
-
|
|
151
|
+
- Log task results before freezing execution state
|
|
152
|
+
- Rename `execute_tasks_sequentially` to `execute_tasks_in_sequence`
|
|
136
153
|
|
|
137
154
|
## [1.6.0] - 2025-08-22
|
|
138
155
|
|
|
139
156
|
### Added
|
|
140
|
-
-
|
|
157
|
+
- Add workflow task `:breakpoints` support
|
|
141
158
|
|
|
142
159
|
### Changed
|
|
143
|
-
-
|
|
144
|
-
-
|
|
160
|
+
- Rename `Worker` class to `Executor`
|
|
161
|
+
- Extract workflow execution logic into `Pipeline` class
|
|
145
162
|
|
|
146
163
|
## [1.5.2] - 2025-08-22
|
|
147
164
|
|
|
148
165
|
### Changed
|
|
149
|
-
-
|
|
166
|
+
- Rename workflow `execution_groups` attribute to `pipeline`
|
|
150
167
|
|
|
151
168
|
## [1.5.1] - 2025-08-21
|
|
152
169
|
|
|
153
170
|
### Changed
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
171
|
+
- Prefix locale I18n with `::` for `CMDx::I18n` compatibility
|
|
172
|
+
- Add safe navigation to length and numeric validators
|
|
173
|
+
- Fix railtie file path to reference correct directory
|
|
157
174
|
|
|
158
175
|
## [1.5.0] - 2025-08-21
|
|
159
176
|
|
|
160
177
|
### Changed
|
|
161
|
-
- **BREAKING**:
|
|
178
|
+
- **BREAKING**: Complete architecture redesign for improved clarity, transparency, and performance
|
|
162
179
|
|
|
163
180
|
## [1.1.2] - 2025-07-20
|
|
164
181
|
|
|
165
182
|
### Changed
|
|
166
|
-
-
|
|
183
|
+
- See git tags for changes between versions 0.1.0 and 1.1.2
|
|
167
184
|
|
|
168
185
|
## [0.1.0] - 2025-03-07
|
|
169
186
|
|
data/LICENSE.txt
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
Copyright (c) Drexed
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|
|
3
|
+
CMDx is an Open Source project licensed under the terms of the LGPLv3 license.
|
|
4
|
+
Please see <http://www.gnu.org/licenses/lgpl-3.0.html> for license text.
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="./src/cmdx-light-logo.png#gh-light-mode-only" width="200" alt="CMDx Logo">
|
|
3
|
-
<img src="./src/cmdx-dark-logo.png#gh-dark-mode-only" width="200" alt="CMDx Logo">
|
|
2
|
+
<img src="./src/cmdx-light-logo.png#gh-light-mode-only" width="200" alt="CMDx Light Logo">
|
|
3
|
+
<img src="./src/cmdx-dark-logo.png#gh-dark-mode-only" width="200" alt="CMDx Dark Logo">
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<img alt="Version" src="https://img.shields.io/gem/v/cmdx">
|
|
12
12
|
<img alt="Build" src="https://github.com/drexed/cmdx/actions/workflows/ci.yml/badge.svg">
|
|
13
|
-
<img alt="License" src="https://img.shields.io/
|
|
13
|
+
<img alt="License" src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg">
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
# CMDx
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
Say goodbye to messy service objects. CMDx helps you design business logic with clarity and consistency—build faster, debug easier, and ship with confidence.
|
|
19
19
|
|
|
20
20
|
> [!NOTE]
|
|
21
|
-
> Documentation reflects the latest code on `main`. For version-specific documentation, please refer to the `docs/` directory within that version's tag.
|
|
21
|
+
> [Documentation](https://drexed.github.io/cmdx/getting_started/) reflects the latest code on `main`. For version-specific documentation, please refer to the `docs/` directory within that version's tag.
|
|
22
22
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
|
-
- Ruby: MRI 3.1+ or JRuby 9.4
|
|
25
|
+
- Ruby: MRI 3.1+ or JRuby 9.4+
|
|
26
|
+
- Dependencies: None
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
Rails support is built-in, but it's framework-agnostic at its core.
|
|
28
29
|
|
|
29
30
|
## Installation
|
|
30
31
|
|
|
@@ -125,4 +126,4 @@ Bug reports and pull requests are welcome at <https://github.com/drexed/cmdx>. W
|
|
|
125
126
|
|
|
126
127
|
## License
|
|
127
128
|
|
|
128
|
-
The gem is available as open source under the terms of the [
|
|
129
|
+
The gem is available as open source under the terms of the [LGPLv3 License](https://www.gnu.org/licenses/lgpl-3.0.html).
|
data/lib/cmdx/attribute.rb
CHANGED
|
@@ -235,6 +235,23 @@ module CMDx
|
|
|
235
235
|
end
|
|
236
236
|
end
|
|
237
237
|
|
|
238
|
+
# @return [Hash] A hash representation of the attribute
|
|
239
|
+
#
|
|
240
|
+
# @example
|
|
241
|
+
# attribute.to_h # => { name: :user_id, method_name: :user_id, required: true, types: [:integer], options: {}, children: [] }
|
|
242
|
+
#
|
|
243
|
+
# @rbs () -> Hash[Symbol, untyped]
|
|
244
|
+
def to_h
|
|
245
|
+
{
|
|
246
|
+
name: name,
|
|
247
|
+
method_name: method_name,
|
|
248
|
+
required: required?,
|
|
249
|
+
types: types,
|
|
250
|
+
options: options.except(:if, :unless),
|
|
251
|
+
children: children.map(&:to_h)
|
|
252
|
+
}
|
|
253
|
+
end
|
|
254
|
+
|
|
238
255
|
private
|
|
239
256
|
|
|
240
257
|
# Creates nested attributes as children of this attribute.
|
|
@@ -312,11 +329,10 @@ module CMDx
|
|
|
312
329
|
attribute_value.generate
|
|
313
330
|
attribute_value.validate
|
|
314
331
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
RUBY
|
|
332
|
+
name_of_method = method_name
|
|
333
|
+
task.define_singleton_method(name_of_method) do
|
|
334
|
+
attributes[name_of_method]
|
|
335
|
+
end
|
|
320
336
|
end
|
|
321
337
|
|
|
322
338
|
end
|
data/lib/cmdx/chain.rb
CHANGED
|
@@ -39,9 +39,10 @@ module CMDx
|
|
|
39
39
|
# @return [Chain] A new chain instance
|
|
40
40
|
#
|
|
41
41
|
# @rbs () -> void
|
|
42
|
-
def initialize
|
|
42
|
+
def initialize(dry_run: false)
|
|
43
43
|
@id = Identifier.generate
|
|
44
44
|
@results = []
|
|
45
|
+
@dry_run = !!dry_run
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
class << self
|
|
@@ -102,16 +103,28 @@ module CMDx
|
|
|
102
103
|
# puts "Chain size: #{chain.size}"
|
|
103
104
|
#
|
|
104
105
|
# @rbs (Result result) -> Chain
|
|
105
|
-
def build(result)
|
|
106
|
+
def build(result, dry_run: false)
|
|
106
107
|
raise TypeError, "must be a CMDx::Result" unless result.is_a?(Result)
|
|
107
108
|
|
|
108
|
-
self.current ||= new
|
|
109
|
+
self.current ||= new(dry_run:)
|
|
109
110
|
current.results << result
|
|
110
111
|
current
|
|
111
112
|
end
|
|
112
113
|
|
|
113
114
|
end
|
|
114
115
|
|
|
116
|
+
# Returns whether the chain is running in dry-run mode.
|
|
117
|
+
#
|
|
118
|
+
# @return [Boolean] Whether the chain is running in dry-run mode
|
|
119
|
+
#
|
|
120
|
+
# @example
|
|
121
|
+
# chain.dry_run? # => true
|
|
122
|
+
#
|
|
123
|
+
# @rbs () -> bool
|
|
124
|
+
def dry_run?
|
|
125
|
+
!!@dry_run
|
|
126
|
+
end
|
|
127
|
+
|
|
115
128
|
# Converts the chain to a hash representation.
|
|
116
129
|
#
|
|
117
130
|
# @option return [String] :id The chain identifier
|
|
@@ -127,7 +140,8 @@ module CMDx
|
|
|
127
140
|
# @rbs () -> Hash[Symbol, untyped]
|
|
128
141
|
def to_h
|
|
129
142
|
{
|
|
130
|
-
id
|
|
143
|
+
id:,
|
|
144
|
+
dry_run: dry_run?,
|
|
131
145
|
results: results.map(&:to_h)
|
|
132
146
|
}
|
|
133
147
|
end
|
data/lib/cmdx/context.rb
CHANGED
|
@@ -164,6 +164,7 @@ module CMDx
|
|
|
164
164
|
args.to_h.each { |key, value| self[key.to_sym] = value }
|
|
165
165
|
self
|
|
166
166
|
end
|
|
167
|
+
alias merge merge!
|
|
167
168
|
|
|
168
169
|
# Deletes a key-value pair from the context.
|
|
169
170
|
#
|
|
@@ -182,6 +183,23 @@ module CMDx
|
|
|
182
183
|
def delete!(key, &)
|
|
183
184
|
table.delete(key.to_sym, &)
|
|
184
185
|
end
|
|
186
|
+
alias delete delete!
|
|
187
|
+
|
|
188
|
+
# Clears all key-value pairs from the context.
|
|
189
|
+
#
|
|
190
|
+
# @return [Context] self for method chaining
|
|
191
|
+
#
|
|
192
|
+
# @example
|
|
193
|
+
# context = Context.new(name: "John")
|
|
194
|
+
# context.clear!
|
|
195
|
+
# context.to_h # => {}
|
|
196
|
+
#
|
|
197
|
+
# @rbs () -> self
|
|
198
|
+
def clear!
|
|
199
|
+
table.clear
|
|
200
|
+
self
|
|
201
|
+
end
|
|
202
|
+
alias clear clear!
|
|
185
203
|
|
|
186
204
|
# Compares this context with another object for equality.
|
|
187
205
|
#
|
data/lib/cmdx/executor.rb
CHANGED
|
@@ -8,6 +8,8 @@ module CMDx
|
|
|
8
8
|
# and proper error handling for different types of failures.
|
|
9
9
|
class Executor
|
|
10
10
|
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
11
13
|
# Returns the task being executed.
|
|
12
14
|
#
|
|
13
15
|
# @return [Task] The task instance
|
|
@@ -18,6 +20,8 @@ module CMDx
|
|
|
18
20
|
# @rbs @task: Task
|
|
19
21
|
attr_reader :task
|
|
20
22
|
|
|
23
|
+
def_delegators :task, :result
|
|
24
|
+
|
|
21
25
|
# @param task [CMDx::Task] The task to execute
|
|
22
26
|
#
|
|
23
27
|
# @return [CMDx::Executor] A new executor instance
|
|
@@ -65,13 +69,13 @@ module CMDx
|
|
|
65
69
|
rescue UndefinedMethodError => e
|
|
66
70
|
raise(e) # No need to clear the Chain since exception is not being re-raised
|
|
67
71
|
rescue Fault => e
|
|
68
|
-
|
|
72
|
+
result.throw!(e.result, halt: false, cause: e)
|
|
69
73
|
rescue StandardError => e
|
|
70
74
|
retry if retry_execution?(e)
|
|
71
|
-
|
|
75
|
+
result.fail!("[#{e.class}] #{e.message}", halt: false, cause: e)
|
|
72
76
|
task.class.settings[:exception_handler]&.call(task, e)
|
|
73
77
|
ensure
|
|
74
|
-
|
|
78
|
+
result.executed!
|
|
75
79
|
post_execution!
|
|
76
80
|
end
|
|
77
81
|
|
|
@@ -96,14 +100,14 @@ module CMDx
|
|
|
96
100
|
rescue UndefinedMethodError => e
|
|
97
101
|
raise_exception(e)
|
|
98
102
|
rescue Fault => e
|
|
99
|
-
|
|
103
|
+
result.throw!(e.result, halt: false, cause: e)
|
|
100
104
|
halt_execution?(e) ? raise_exception(e) : post_execution!
|
|
101
105
|
rescue StandardError => e
|
|
102
106
|
retry if retry_execution?(e)
|
|
103
|
-
|
|
107
|
+
result.fail!("[#{e.class}] #{e.message}", halt: false, cause: e)
|
|
104
108
|
raise_exception(e)
|
|
105
109
|
else
|
|
106
|
-
|
|
110
|
+
result.executed!
|
|
107
111
|
post_execution!
|
|
108
112
|
end
|
|
109
113
|
|
|
@@ -134,17 +138,17 @@ module CMDx
|
|
|
134
138
|
#
|
|
135
139
|
# @rbs (Exception exception) -> bool
|
|
136
140
|
def retry_execution?(exception)
|
|
137
|
-
available_retries = (task.class.settings[:retries] || 0)
|
|
141
|
+
available_retries = Integer(task.class.settings[:retries] || 0)
|
|
138
142
|
return false unless available_retries.positive?
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
remaining_retries = available_retries -
|
|
144
|
+
current_retry = result.retries
|
|
145
|
+
remaining_retries = available_retries - current_retry
|
|
142
146
|
return false unless remaining_retries.positive?
|
|
143
147
|
|
|
144
148
|
exceptions = Array(task.class.settings[:retry_on] || StandardError)
|
|
145
149
|
return false unless exceptions.any? { |e| exception.class <= e }
|
|
146
150
|
|
|
147
|
-
|
|
151
|
+
result.retries += 1
|
|
148
152
|
|
|
149
153
|
task.logger.warn do
|
|
150
154
|
reason = "[#{exception.class}] #{exception.message}"
|
|
@@ -154,13 +158,13 @@ module CMDx
|
|
|
154
158
|
jitter = task.class.settings[:retry_jitter]
|
|
155
159
|
jitter =
|
|
156
160
|
if jitter.is_a?(Symbol)
|
|
157
|
-
task.send(jitter,
|
|
161
|
+
task.send(jitter, current_retry)
|
|
158
162
|
elsif jitter.is_a?(Proc)
|
|
159
|
-
task.instance_exec(
|
|
163
|
+
task.instance_exec(current_retry, &jitter)
|
|
160
164
|
elsif jitter.respond_to?(:call)
|
|
161
|
-
jitter.call(task,
|
|
165
|
+
jitter.call(task, current_retry)
|
|
162
166
|
else
|
|
163
|
-
jitter.to_f *
|
|
167
|
+
jitter.to_f * current_retry
|
|
164
168
|
end
|
|
165
169
|
|
|
166
170
|
sleep(jitter) if jitter.positive?
|
|
@@ -208,7 +212,7 @@ module CMDx
|
|
|
208
212
|
task.class.settings[:attributes].define_and_verify(task)
|
|
209
213
|
return if task.errors.empty?
|
|
210
214
|
|
|
211
|
-
|
|
215
|
+
result.fail!(
|
|
212
216
|
Locale.t("cmdx.faults.invalid"),
|
|
213
217
|
errors: {
|
|
214
218
|
full_message: task.errors.to_s,
|
|
@@ -223,7 +227,7 @@ module CMDx
|
|
|
223
227
|
def execution!
|
|
224
228
|
invoke_callbacks(:before_execution)
|
|
225
229
|
|
|
226
|
-
|
|
230
|
+
result.executing!
|
|
227
231
|
task.work
|
|
228
232
|
end
|
|
229
233
|
|
|
@@ -231,12 +235,12 @@ module CMDx
|
|
|
231
235
|
#
|
|
232
236
|
# @rbs () -> void
|
|
233
237
|
def post_execution!
|
|
234
|
-
invoke_callbacks(:"on_#{
|
|
235
|
-
invoke_callbacks(:on_executed) if
|
|
238
|
+
invoke_callbacks(:"on_#{result.state}")
|
|
239
|
+
invoke_callbacks(:on_executed) if result.executed?
|
|
236
240
|
|
|
237
|
-
invoke_callbacks(:"on_#{
|
|
238
|
-
invoke_callbacks(:on_good) if
|
|
239
|
-
invoke_callbacks(:on_bad) if
|
|
241
|
+
invoke_callbacks(:"on_#{result.status}")
|
|
242
|
+
invoke_callbacks(:on_good) if result.good?
|
|
243
|
+
invoke_callbacks(:on_bad) if result.bad?
|
|
240
244
|
end
|
|
241
245
|
|
|
242
246
|
# Finalizes execution by freezing the task, logging results, and rolling back work.
|
|
@@ -246,26 +250,25 @@ module CMDx
|
|
|
246
250
|
log_execution!
|
|
247
251
|
log_backtrace! if task.class.settings[:backtrace]
|
|
248
252
|
|
|
253
|
+
rollback_execution!
|
|
249
254
|
freeze_execution!
|
|
250
255
|
clear_chain!
|
|
251
|
-
|
|
252
|
-
rollback_execution!
|
|
253
256
|
end
|
|
254
257
|
|
|
255
258
|
# Logs the execution result at the configured log level.
|
|
256
259
|
#
|
|
257
260
|
# @rbs () -> void
|
|
258
261
|
def log_execution!
|
|
259
|
-
task.logger.info {
|
|
262
|
+
task.logger.info { result.to_h }
|
|
260
263
|
end
|
|
261
264
|
|
|
262
265
|
# Logs the backtrace of the exception if the task failed.
|
|
263
266
|
#
|
|
264
267
|
# @rbs () -> void
|
|
265
268
|
def log_backtrace!
|
|
266
|
-
return unless
|
|
269
|
+
return unless result.failed?
|
|
267
270
|
|
|
268
|
-
exception =
|
|
271
|
+
exception = result.caused_failure.cause
|
|
269
272
|
return if exception.is_a?(Fault)
|
|
270
273
|
|
|
271
274
|
task.logger.error do
|
|
@@ -287,11 +290,11 @@ module CMDx
|
|
|
287
290
|
return if Coercions::Boolean.call(skip_freezing)
|
|
288
291
|
|
|
289
292
|
task.freeze
|
|
290
|
-
|
|
293
|
+
result.freeze
|
|
291
294
|
|
|
292
295
|
# Freezing the context and chain can only be done
|
|
293
296
|
# once the outer-most task has completed.
|
|
294
|
-
return unless
|
|
297
|
+
return unless result.index.zero?
|
|
295
298
|
|
|
296
299
|
task.context.freeze
|
|
297
300
|
task.chain.freeze
|
|
@@ -301,7 +304,7 @@ module CMDx
|
|
|
301
304
|
#
|
|
302
305
|
# @rbs () -> void
|
|
303
306
|
def clear_chain!
|
|
304
|
-
return unless
|
|
307
|
+
return unless result.index.zero?
|
|
305
308
|
|
|
306
309
|
Chain.clear
|
|
307
310
|
end
|
|
@@ -310,12 +313,14 @@ module CMDx
|
|
|
310
313
|
#
|
|
311
314
|
# @rbs () -> void
|
|
312
315
|
def rollback_execution!
|
|
316
|
+
return if result.rolled_back?
|
|
313
317
|
return unless task.respond_to?(:rollback)
|
|
314
318
|
|
|
315
319
|
statuses = task.class.settings[:rollback_on]
|
|
316
320
|
statuses = Array(statuses).map(&:to_s).uniq
|
|
317
|
-
return unless statuses.include?(
|
|
321
|
+
return unless statuses.include?(result.status)
|
|
318
322
|
|
|
323
|
+
result.rolled_back = true
|
|
319
324
|
task.rollback
|
|
320
325
|
end
|
|
321
326
|
|