cmdx 1.9.1 → 1.10.1

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.
@@ -9,6 +9,9 @@ module CMDx
9
9
  # @rbs DEFAULT_BREAKPOINTS: Array[String]
10
10
  DEFAULT_BREAKPOINTS = %w[failed].freeze
11
11
 
12
+ # @rbs DEFAULT_ROLLPOINTS: Array[String]
13
+ DEFAULT_ROLLPOINTS = %w[failed].freeze
14
+
12
15
  # Returns the middleware registry for task execution.
13
16
  #
14
17
  # @return [MiddlewareRegistry] The middleware registry
@@ -110,6 +113,16 @@ module CMDx
110
113
  # @rbs @exception_handler: (Proc | nil)
111
114
  attr_accessor :exception_handler
112
115
 
116
+ # Returns the statuses that trigger a task execution rollback.
117
+ #
118
+ # @return [Array<String>] Array of status names that trigger rollback
119
+ #
120
+ # @example
121
+ # config.rollback_on = ["failed", "skipped"]
122
+ #
123
+ # @rbs @rollback_on: Array[String]
124
+ attr_accessor :rollback_on
125
+
113
126
  # Initializes a new Configuration instance with default values.
114
127
  #
115
128
  # Creates new registry instances for middlewares, callbacks, coercions, and
@@ -131,6 +144,7 @@ module CMDx
131
144
 
132
145
  @task_breakpoints = DEFAULT_BREAKPOINTS
133
146
  @workflow_breakpoints = DEFAULT_BREAKPOINTS
147
+ @rollback_on = DEFAULT_ROLLPOINTS
134
148
 
135
149
  @backtrace = false
136
150
  @backtrace_cleaner = nil
@@ -162,6 +176,7 @@ module CMDx
162
176
  validators: @validators,
163
177
  task_breakpoints: @task_breakpoints,
164
178
  workflow_breakpoints: @workflow_breakpoints,
179
+ rollback_on: @rollback_on,
165
180
  backtrace: @backtrace,
166
181
  backtrace_cleaner: @backtrace_cleaner,
167
182
  exception_handler: @exception_handler,
data/lib/cmdx/executor.rb CHANGED
@@ -118,15 +118,12 @@ module CMDx
118
118
  #
119
119
  # @return [Boolean] Whether execution should halt
120
120
  #
121
- # @example
122
- # halt_execution?(fault_exception)
123
- #
124
121
  # @rbs (Exception exception) -> bool
125
122
  def halt_execution?(exception)
126
- breakpoints = task.class.settings[:breakpoints] || task.class.settings[:task_breakpoints]
127
- breakpoints = Array(breakpoints).map(&:to_s).uniq
123
+ statuses = task.class.settings[:breakpoints] || task.class.settings[:task_breakpoints]
124
+ statuses = Array(statuses).map(&:to_s).uniq
128
125
 
129
- breakpoints.include?(exception.result.status)
126
+ statuses.include?(exception.result.status)
130
127
  end
131
128
 
132
129
  # Determines if execution should be retried based on retry configuration.
@@ -135,9 +132,6 @@ module CMDx
135
132
  #
136
133
  # @return [Boolean] Whether execution should be retried
137
134
  #
138
- # @example
139
- # retry_execution?(standard_error)
140
- #
141
135
  # @rbs (Exception exception) -> bool
142
136
  def retry_execution?(exception)
143
137
  available_retries = (task.class.settings[:retries] || 0).to_i
@@ -157,7 +151,18 @@ module CMDx
157
151
  task.to_h.merge!(reason:, remaining_retries:)
158
152
  end
159
153
 
160
- jitter = task.class.settings[:retry_jitter].to_f * current_retries
154
+ jitter = task.class.settings[:retry_jitter]
155
+ jitter =
156
+ if jitter.is_a?(Symbol)
157
+ task.send(jitter, current_retries)
158
+ elsif jitter.is_a?(Proc)
159
+ task.instance_exec(current_retries, &jitter)
160
+ elsif jitter.respond_to?(:call)
161
+ jitter.call(task, current_retries)
162
+ else
163
+ jitter.to_f * current_retries
164
+ end
165
+
161
166
  sleep(jitter) if jitter.positive?
162
167
 
163
168
  true
@@ -169,9 +174,6 @@ module CMDx
169
174
  #
170
175
  # @raise [Exception] The provided exception
171
176
  #
172
- # @example
173
- # raise_exception(standard_error)
174
- #
175
177
  # @rbs (Exception exception) -> void
176
178
  def raise_exception(exception)
177
179
  Chain.clear
@@ -195,13 +197,6 @@ module CMDx
195
197
 
196
198
  private
197
199
 
198
- # Lazy loaded repeator instance to handle retries.
199
- #
200
- # @rbs () -> untyped
201
- def repeator
202
- @repeator ||= Repeator.new(task)
203
- end
204
-
205
200
  # Performs pre-execution tasks including validation and attribute verification.
206
201
  #
207
202
  # @rbs () -> void
@@ -244,7 +239,7 @@ module CMDx
244
239
  invoke_callbacks(:on_bad) if task.result.bad?
245
240
  end
246
241
 
247
- # Finalizes execution by freezing the task and logging results.
242
+ # Finalizes execution by freezing the task, logging results, and rolling back work.
248
243
  #
249
244
  # @rbs () -> Result
250
245
  def finalize_execution!
@@ -253,6 +248,8 @@ module CMDx
253
248
 
254
249
  freeze_execution!
255
250
  clear_chain!
251
+
252
+ rollback_execution!
256
253
  end
257
254
 
258
255
  # Logs the execution result at the configured log level.
@@ -309,5 +306,18 @@ module CMDx
309
306
  Chain.clear
310
307
  end
311
308
 
309
+ # Rolls back the work of a task.
310
+ #
311
+ # @rbs () -> void
312
+ def rollback_execution!
313
+ return unless task.respond_to?(:rollback)
314
+
315
+ statuses = task.class.settings[:rollback_on]
316
+ statuses = Array(statuses).map(&:to_s).uniq
317
+ return unless statuses.include?(task.result.status)
318
+
319
+ task.rollback
320
+ end
321
+
312
322
  end
313
323
  end
data/lib/cmdx/version.rb CHANGED
@@ -5,6 +5,6 @@ module CMDx
5
5
  # @return [String] the version of the CMDx gem
6
6
  #
7
7
  # @rbs return: String
8
- VERSION = "1.9.1"
8
+ VERSION = "1.10.1"
9
9
 
10
10
  end
data/mkdocs.yml CHANGED
@@ -1,9 +1,9 @@
1
1
  # yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json
2
2
 
3
3
  site_name: CMDx
4
- site_url: https://drexed.github.io/cmdx/
5
- site_description: Build business logic that's powerful, predictable, and chaos-free.
4
+ site_description: Build business logic that's powerful, predictable, and maintainable.
6
5
  site_author: drexed
6
+ site_url: https://drexed.github.io/cmdx/
7
7
  repo_name: drexed/cmdx
8
8
  repo_url: https://github.com/drexed/cmdx
9
9
  edit_uri: edit/main/docs/
@@ -43,25 +43,27 @@ theme:
43
43
  icon: material/brightness-4
44
44
  name: Switch to system preference
45
45
  features:
46
+ - content.code.annotate
47
+ - content.code.copy
48
+ - content.tabs.link
49
+ - content.tooltips
46
50
  - navigation.footer
47
51
  - navigation.instant
48
52
  - navigation.instant.prefetch
49
53
  - navigation.instant.progress
50
- - navigation.tracking
51
- - navigation.sections
52
54
  - navigation.expand
53
55
  - navigation.path
56
+ - navigation.sections
54
57
  - navigation.top
58
+ - navigation.tracking
59
+ - search.highlight
55
60
  - search.share
56
61
  - search.suggest
57
- - search.highlight
58
- - content.code.copy
59
- - content.code.annotate
60
- - content.tabs.link
61
- - content.tooltips
62
62
 
63
63
  markdown_extensions:
64
64
  - admonition
65
+ - attr_list
66
+ - md_in_html
65
67
  - pymdownx.details
66
68
  - pymdownx.superfences
67
69
  - pymdownx.highlight:
@@ -73,17 +75,56 @@ markdown_extensions:
73
75
  - pymdownx.tabbed:
74
76
  alternate_style: true
75
77
  - tables
76
- - attr_list
77
- - md_in_html
78
78
  - toc:
79
79
  permalink: true
80
80
 
81
81
  plugins:
82
82
  - search
83
+ - llmstxt:
84
+ markdown_description: >-
85
+ CMDx is a Ruby framework for building maintainable, observable business logic through composable command/service objects.
86
+ It brings structure, consistency, and powerful developer tools to your business processes.
87
+ full_output: llms-full.txt
88
+ sections:
89
+ "Getting Started":
90
+ - index.md: CMDx framework overview, installation, quick examples, and core concepts
91
+ - getting_started.md: Task setup and framework fundamentals (CERO pattern)
92
+ - configuration.md: Comprehensive guide to CMDx configuration
93
+ Basics:
94
+ - basics/setup.md: Task structure, inheritance, and basic setup requirements
95
+ - basics/execution.md: Task execution methods (execute vs execute!), error handling, and control flow
96
+ - basics/context.md: Context object for data sharing, input/output management, and attribute access
97
+ - basics/chain.md: Execution chain tracking for related tasks within threads
98
+ Interruptions:
99
+ - interruptions/halt.md: Intentional task interruption using skip! and fail! methods
100
+ - interruptions/faults.md: Fault exceptions (SkipFault, FailFault) raised by execute! with rich context
101
+ - interruptions/exceptions.md: Exception handling differences between execute and execute! methods
102
+ Outcomes:
103
+ - outcomes/result.md: Result objects exposing execution state, status, context, and metadata
104
+ - outcomes/states.md: Execution lifecycle states (initialized, executing, complete, interrupted)
105
+ - outcomes/statuses.md: Business outcome statuses (success, skipped, failed) and transitions
106
+ Attributes:
107
+ - attributes/definitions.md: Attribute declarations (required/optional), validation, and type coercion
108
+ - attributes/naming.md: Customizing accessor method names with prefixes and suffixes
109
+ - attributes/coercions.md: Automatic type conversion for inputs (string to integer, date parsing, etc.)
110
+ - attributes/validations.md: Input validation rules (presence, length, format, numeric, inclusion, exclusion)
111
+ - attributes/defaults.md: Default values for optional attributes (static, dynamic, callable)
112
+ - attributes/transformations.md: Value transformation after coercion but before validation
113
+ Features:
114
+ - callbacks.md: Execution lifecycle callbacks (before_execution, on_success, on_failure, etc.)
115
+ - middlewares.md: Cross-cutting concerns wrapping task execution (authentication, caching, timeouts)
116
+ - logging.md: Structured logging with multiple formatters (JSON, KeyValue, Logstash, Line, Raw)
117
+ - internationalization.md: Multi-language support for error messages and validations (90+ locales)
118
+ - retries.md: Automatic retry functionality for transient failures with jitter and selective retries
119
+ - deprecation.md: Managing deprecated tasks with logging, warnings, or execution prevention
120
+ - workflows.md: Composing multiple tasks into sequential pipelines with conditional execution
121
+ More:
122
+ - tips_and_tricks.md: Best practices, patterns, and techniques for maintainable CMDx applications
83
123
 
84
124
  nav:
85
125
  - Home: index.md
86
126
  - Getting Started: getting_started.md
127
+ - Configuration: configuration.md
87
128
  - Basics:
88
129
  - Setup: basics/setup.md
89
130
  - Execution: basics/execution.md
@@ -104,13 +145,20 @@ nav:
104
145
  - Validations: attributes/validations.md
105
146
  - Defaults: attributes/defaults.md
106
147
  - Transformations: attributes/transformations.md
107
- - Callbacks: callbacks.md
108
- - Middlewares: middlewares.md
109
- - Logging: logging.md
110
- - Internationalization: internationalization.md
111
- - Deprecation: deprecation.md
112
- - Workflows: workflows.md
113
- - Tips and Tricks: tips_and_tricks.md
148
+ - Features:
149
+ - Callbacks: callbacks.md
150
+ - Middlewares: middlewares.md
151
+ - Logging: logging.md
152
+ - Internationalization: internationalization.md
153
+ - Retries: retries.md
154
+ - Deprecation: deprecation.md
155
+ - Workflows: workflows.md
156
+ - More:
157
+ - Tips and Tricks: tips_and_tricks.md
158
+ - References:
159
+ - API YARDocs: https://drexed.github.io/cmdx/api/index.html
160
+ - llms.txt: https://drexed.github.io/cmdx/llms.txt
161
+ - llms-full.txt: https://drexed.github.io/cmdx/llms-full.txt
114
162
 
115
163
  extra:
116
164
  generator: false
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmdx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juan Gomez
@@ -191,6 +191,20 @@ dependencies:
191
191
  - - ">="
192
192
  - !ruby/object:Gem::Version
193
193
  version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: yard
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
194
208
  description: CMDx is a framework for building maintainable business processes.
195
209
  email:
196
210
  - drexed@users.noreply.github.com
@@ -208,10 +222,10 @@ files:
208
222
  - ".rspec"
209
223
  - ".rubocop.yml"
210
224
  - ".ruby-version"
225
+ - ".yardopts"
211
226
  - CHANGELOG.md
212
227
  - CODE_OF_CONDUCT.md
213
228
  - LICENSE.txt
214
- - LLM.md
215
229
  - README.md
216
230
  - Rakefile
217
231
  - docs/.DS_Store
@@ -228,6 +242,7 @@ files:
228
242
  - docs/basics/execution.md
229
243
  - docs/basics/setup.md
230
244
  - docs/callbacks.md
245
+ - docs/configuration.md
231
246
  - docs/deprecation.md
232
247
  - docs/getting_started.md
233
248
  - docs/index.md
@@ -240,11 +255,14 @@ files:
240
255
  - docs/outcomes/result.md
241
256
  - docs/outcomes/states.md
242
257
  - docs/outcomes/statuses.md
258
+ - docs/retries.md
243
259
  - docs/stylesheets/extra.css
244
260
  - docs/tips_and_tricks.md
245
261
  - docs/workflows.md
246
262
  - examples/active_record_query_tagging.md
247
263
  - examples/paper_trail_whatdunnit.md
264
+ - examples/sidekiq_async_execution.md
265
+ - examples/stoplight_circuit_breaker.md
248
266
  - lib/cmdx.rb
249
267
  - lib/cmdx/.DS_Store
250
268
  - lib/cmdx/attribute.rb