cmdx 1.7.5 → 1.9.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.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +3 -3
  4. data/.cursor/prompts/llms.md +1 -3
  5. data/.cursor/prompts/rspec.md +1 -1
  6. data/.irbrc +14 -2
  7. data/CHANGELOG.md +62 -29
  8. data/LLM.md +203 -78
  9. data/README.md +23 -85
  10. data/docs/.DS_Store +0 -0
  11. data/docs/assets/favicon.ico +0 -0
  12. data/docs/assets/favicon.svg +1 -0
  13. data/docs/attributes/coercions.md +19 -29
  14. data/docs/attributes/defaults.md +3 -16
  15. data/docs/attributes/definitions.md +29 -39
  16. data/docs/attributes/naming.md +3 -13
  17. data/docs/attributes/transformations.md +63 -0
  18. data/docs/attributes/validations.md +23 -40
  19. data/docs/basics/chain.md +14 -23
  20. data/docs/basics/context.md +13 -22
  21. data/docs/basics/execution.md +8 -26
  22. data/docs/basics/setup.md +8 -19
  23. data/docs/callbacks.md +19 -32
  24. data/docs/deprecation.md +8 -25
  25. data/docs/getting_started.md +101 -77
  26. data/docs/index.md +120 -0
  27. data/docs/internationalization.md +6 -18
  28. data/docs/interruptions/exceptions.md +10 -16
  29. data/docs/interruptions/faults.md +8 -25
  30. data/docs/interruptions/halt.md +31 -25
  31. data/docs/logging.md +7 -17
  32. data/docs/middlewares.md +13 -29
  33. data/docs/outcomes/result.md +21 -38
  34. data/docs/outcomes/states.md +8 -22
  35. data/docs/outcomes/statuses.md +10 -21
  36. data/docs/stylesheets/extra.css +42 -0
  37. data/docs/tips_and_tricks.md +7 -46
  38. data/docs/workflows.md +23 -38
  39. data/examples/active_record_query_tagging.md +46 -0
  40. data/examples/paper_trail_whatdunnit.md +39 -0
  41. data/lib/cmdx/attribute.rb +9 -2
  42. data/lib/cmdx/attribute_value.rb +31 -10
  43. data/lib/cmdx/callback_registry.rb +12 -2
  44. data/lib/cmdx/coercions/hash.rb +6 -1
  45. data/lib/cmdx/configuration.rb +10 -2
  46. data/lib/cmdx/deprecator.rb +3 -3
  47. data/lib/cmdx/errors.rb +1 -1
  48. data/lib/cmdx/executor.rb +97 -9
  49. data/lib/cmdx/log_formatters/logstash.rb +4 -4
  50. data/lib/cmdx/pipeline.rb +4 -4
  51. data/lib/cmdx/railtie.rb +9 -0
  52. data/lib/cmdx/result.rb +10 -1
  53. data/lib/cmdx/task.rb +12 -7
  54. data/lib/cmdx/version.rb +1 -1
  55. data/lib/cmdx.rb +1 -0
  56. data/lib/generators/cmdx/templates/install.rb +9 -0
  57. data/lib/locales/af.yml +2 -2
  58. data/lib/locales/ar.yml +2 -2
  59. data/lib/locales/az.yml +2 -2
  60. data/lib/locales/be.yml +2 -2
  61. data/lib/locales/bg.yml +2 -2
  62. data/lib/locales/bn.yml +2 -2
  63. data/lib/locales/bs.yml +2 -2
  64. data/lib/locales/ca.yml +2 -2
  65. data/lib/locales/cnr.yml +2 -2
  66. data/lib/locales/cs.yml +2 -2
  67. data/lib/locales/cy.yml +2 -2
  68. data/lib/locales/da.yml +2 -2
  69. data/lib/locales/de.yml +2 -2
  70. data/lib/locales/dz.yml +2 -2
  71. data/lib/locales/el.yml +2 -2
  72. data/lib/locales/en.yml +2 -2
  73. data/lib/locales/eo.yml +2 -2
  74. data/lib/locales/es.yml +2 -2
  75. data/lib/locales/et.yml +2 -2
  76. data/lib/locales/eu.yml +2 -2
  77. data/lib/locales/fa.yml +2 -2
  78. data/lib/locales/fi.yml +2 -2
  79. data/lib/locales/fr.yml +2 -2
  80. data/lib/locales/fy.yml +2 -2
  81. data/lib/locales/gd.yml +2 -2
  82. data/lib/locales/gl.yml +2 -2
  83. data/lib/locales/he.yml +2 -2
  84. data/lib/locales/hi.yml +2 -2
  85. data/lib/locales/hr.yml +2 -2
  86. data/lib/locales/hu.yml +2 -2
  87. data/lib/locales/hy.yml +2 -2
  88. data/lib/locales/id.yml +2 -2
  89. data/lib/locales/is.yml +2 -2
  90. data/lib/locales/it.yml +2 -2
  91. data/lib/locales/ja.yml +2 -2
  92. data/lib/locales/ka.yml +2 -2
  93. data/lib/locales/kk.yml +2 -2
  94. data/lib/locales/km.yml +2 -2
  95. data/lib/locales/kn.yml +2 -2
  96. data/lib/locales/ko.yml +2 -2
  97. data/lib/locales/lb.yml +2 -2
  98. data/lib/locales/lo.yml +2 -2
  99. data/lib/locales/lt.yml +2 -2
  100. data/lib/locales/lv.yml +2 -2
  101. data/lib/locales/mg.yml +2 -2
  102. data/lib/locales/mk.yml +2 -2
  103. data/lib/locales/ml.yml +2 -2
  104. data/lib/locales/mn.yml +2 -2
  105. data/lib/locales/mr-IN.yml +2 -2
  106. data/lib/locales/ms.yml +2 -2
  107. data/lib/locales/nb.yml +2 -2
  108. data/lib/locales/ne.yml +2 -2
  109. data/lib/locales/nl.yml +2 -2
  110. data/lib/locales/nn.yml +2 -2
  111. data/lib/locales/oc.yml +2 -2
  112. data/lib/locales/or.yml +2 -2
  113. data/lib/locales/pa.yml +2 -2
  114. data/lib/locales/pl.yml +2 -2
  115. data/lib/locales/pt.yml +2 -2
  116. data/lib/locales/rm.yml +2 -2
  117. data/lib/locales/ro.yml +2 -2
  118. data/lib/locales/ru.yml +2 -2
  119. data/lib/locales/sc.yml +2 -2
  120. data/lib/locales/sk.yml +2 -2
  121. data/lib/locales/sl.yml +2 -2
  122. data/lib/locales/sq.yml +2 -2
  123. data/lib/locales/sr.yml +2 -2
  124. data/lib/locales/st.yml +2 -2
  125. data/lib/locales/sv.yml +2 -2
  126. data/lib/locales/sw.yml +2 -2
  127. data/lib/locales/ta.yml +2 -2
  128. data/lib/locales/te.yml +2 -2
  129. data/lib/locales/th.yml +2 -2
  130. data/lib/locales/tl.yml +2 -2
  131. data/lib/locales/tr.yml +2 -2
  132. data/lib/locales/tt.yml +2 -2
  133. data/lib/locales/ug.yml +2 -2
  134. data/lib/locales/uk.yml +2 -2
  135. data/lib/locales/ur.yml +2 -2
  136. data/lib/locales/uz.yml +2 -2
  137. data/lib/locales/vi.yml +2 -2
  138. data/lib/locales/wo.yml +2 -2
  139. data/lib/locales/zh-CN.yml +2 -2
  140. data/lib/locales/zh-HK.yml +2 -2
  141. data/lib/locales/zh-TW.yml +2 -2
  142. data/lib/locales/zh-YUE.yml +2 -2
  143. data/mkdocs.yml +122 -0
  144. data/src/cmdx-dark-logo.png +0 -0
  145. data/src/cmdx-favicon.svg +1 -0
  146. data/src/cmdx-light-logo.png +0 -0
  147. data/src/cmdx-logo.svg +1 -0
  148. metadata +15 -4
  149. data/lib/cmdx/freezer.rb +0 -51
  150. data/src/cmdx-logo.png +0 -0
@@ -1,51 +1,33 @@
1
1
  # Getting Started
2
2
 
3
- CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. Design robust workflows with automatic attribute validation, structured error handling, comprehensive logging, and intelligent execution flow control.
4
-
5
- **Common Challenges:**
6
-
7
- - Inconsistent patterns across implementations
8
- - Minimal or no logging, making debugging painful
9
- - Fragile designs that erode developer confidence
10
-
11
- **CMDx Solutions:**
12
-
13
- - Establishes a consistent, standardized design
14
- - Provides flow control and error handling
15
- - Supports composable, reusable workflows
16
- - Includes detailed logging for observability
17
- - Defines input attributes with fallback defaults
18
- - Adds validations and type coercions
19
- - Plus many other developer-friendly tools
20
-
21
- ## Table of Contents
22
-
23
- - [Compose, Execute, React, Observe pattern](#compose-execute-react-observe-pattern)
24
- - [Installation](#installation)
25
- - [Configuration Hierarchy](#configuration-hierarchy)
26
- - [Global Configuration](#global-configuration)
27
- - [Breakpoints](#breakpoints)
28
- - [Logging](#logging)
29
- - [Middlewares](#middlewares)
30
- - [Callbacks](#callbacks)
31
- - [Coercions](#coercions)
32
- - [Validators](#validators)
33
- - [Task Configuration](#task-configuration)
34
- - [Settings](#settings)
35
- - [Registrations](#registrations)
36
- - [Configuration Management](#configuration-management)
37
- - [Access](#access)
38
- - [Resetting](#resetting)
39
- - [Task Generator](#task-generator)
40
-
41
- ## Compose, Execute, React, Observe pattern
42
-
43
- CMDx encourages breaking business logic into composable tasks. Each task can be combined into larger workflows, executed with standardized flow control, and fully observed through logging, validations, and context.
44
-
45
- - *Compose* → Define small, contract-driven tasks with typed attributes, validations, and natural workflow composition.
46
- - *Execute* → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
47
- - *React* → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
48
- - *Observe* → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
3
+ CMDx is a Ruby framework for building maintainable, observable business logic through composable command objects. It brings structure, consistency, and powerful developer tools to your business processes.
4
+
5
+ **Common challenges it solves:**
6
+
7
+ - Inconsistent service object patterns across your codebase
8
+ - Limited logging makes debugging a nightmare
9
+ - Fragile error handling erodes confidence
10
+
11
+ **What you get:**
12
+
13
+ - Consistent, standardized architecture
14
+ - Built-in flow control and error handling
15
+ - Composable, reusable workflows
16
+ - Comprehensive logging for observability
17
+ - Attribute validation with type coercions
18
+ - Sensible defaults and developer-friendly APIs
19
+
20
+ ## The CERO Pattern
21
+
22
+ CMDx embraces the Compose, Execute, React, Observe (CERO) pattern—a simple yet powerful approach to building reliable business logic.
23
+
24
+ 🧩 **Compose** — Define small, focused tasks with typed attributes and validations
25
+
26
+ **Execute** — Run tasks with clear outcomes and pluggable behaviors
27
+
28
+ 🔄 **React** — Adapt to outcomes by chaining follow-up tasks or handling faults
29
+
30
+ 🔍 **Observe** — Capture structured logs and execution chains for debugging
49
31
 
50
32
  ## Installation
51
33
 
@@ -65,39 +47,78 @@ This creates `config/initializers/cmdx.rb` file.
65
47
 
66
48
  ## Configuration Hierarchy
67
49
 
68
- CMDx follows a two-tier configuration hierarchy:
50
+ CMDx uses a straightforward two-tier configuration system:
69
51
 
70
- 1. **Global Configuration**: Framework-wide defaults
71
- 2. **Task Settings**: Class-level overrides via `settings`
52
+ 1. **Global Configuration** Framework-wide defaults
53
+ 2. **Task Settings** Class-level overrides using `settings`
72
54
 
73
- > [!IMPORTANT]
74
- > Task-level settings take precedence over global configuration. Settings are inherited from superclasses and can be overridden in subclasses.
55
+ !!! warning "Important"
56
+
57
+ Task settings take precedence over global config. Settings are inherited from parent classes and can be overridden in subclasses.
75
58
 
76
59
  ## Global Configuration
77
60
 
78
- Global configuration settings apply to all tasks inherited from `CMDx::Task`.
79
- Globally these settings are initialized with sensible defaults.
61
+ Configure framework-wide defaults that apply to all tasks. These settings come with sensible defaults out of the box.
80
62
 
81
63
  ### Breakpoints
82
64
 
83
- Raise `CMDx::Fault` when a task called with `execute!` returns a matching status.
65
+ Control when `execute!` raises a `CMDx::Fault` based on task status.
84
66
 
85
67
  ```ruby
86
68
  CMDx.configure do |config|
87
- # String or Array[String]
88
- config.task_breakpoints = "failed"
69
+ config.task_breakpoints = "failed" # String or Array[String]
89
70
  end
90
71
  ```
91
72
 
92
- Workflow breakpoints stops execution and of workflow pipeline on the first task that returns a matching status and throws its `CMDx::Fault`.
73
+ For workflows, configure which statuses halt the execution pipeline:
93
74
 
94
75
  ```ruby
95
76
  CMDx.configure do |config|
96
- # String or Array[String]
97
77
  config.workflow_breakpoints = ["skipped", "failed"]
98
78
  end
99
79
  ```
100
80
 
81
+ ### Backtraces
82
+
83
+ Enable detailed backtraces for non-fault exceptions to improve debugging. Optionally clean up stack traces to remove framework noise.
84
+
85
+ !!! note
86
+
87
+ In Rails environments, `backtrace_cleaner` defaults to `Rails.backtrace_cleaner.clean`.
88
+
89
+ ```ruby
90
+ CMDx.configure do |config|
91
+ # Truthy
92
+ config.backtrace = true
93
+
94
+ # Via callable (must respond to `call(backtrace)`)
95
+ config.backtrace_cleaner = AdvanceCleaner.new
96
+
97
+ # Via proc or lambda
98
+ config.backtrace_cleaner = ->(backtrace) { backtrace[0..5] }
99
+ end
100
+ ```
101
+
102
+ ### Exception Handlers
103
+
104
+ Register handlers that run when non-fault exceptions occur.
105
+
106
+ !!! tip
107
+
108
+ Use exception handlers to send errors to your APM of choice.
109
+
110
+ ```ruby
111
+ CMDx.configure do |config|
112
+ # Via callable (must respond to `call(task, exception)`)
113
+ config.exception_handler = NewRelicReporter
114
+
115
+ # Via proc or lambda
116
+ config.exception_handler = proc do |task, exception|
117
+ APMService.report(exception, extra_data: { task: task.name, id: task.id })
118
+ end
119
+ end
120
+ ```
121
+
101
122
  ### Logging
102
123
 
103
124
  ```ruby
@@ -108,7 +129,7 @@ end
108
129
 
109
130
  ### Middlewares
110
131
 
111
- See the [Middelwares](#https://github.com/drexed/cmdx/blob/main/docs/middlewares.md#declarations) docs for task level configurations.
132
+ See the [Middlewares](middlewares.md#declarations) docs for task level configurations.
112
133
 
113
134
  ```ruby
114
135
  CMDx.configure do |config|
@@ -132,12 +153,13 @@ CMDx.configure do |config|
132
153
  end
133
154
  ```
134
155
 
135
- > [!NOTE]
136
- > Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
156
+ !!! note
157
+
158
+ Middlewares are executed in registration order. Each middleware wraps the next, creating an execution chain around task logic.
137
159
 
138
160
  ### Callbacks
139
161
 
140
- See the [Callbacks](#https://github.com/drexed/cmdx/blob/main/docs/callbacks.md#declarations) docs for task level configurations.
162
+ See the [Callbacks](callbacks.md#declarations) docs for task level configurations.
141
163
 
142
164
  ```ruby
143
165
  CMDx.configure do |config|
@@ -163,7 +185,7 @@ end
163
185
 
164
186
  ### Coercions
165
187
 
166
- See the [Attributes - Coercions](#https://github.com/drexed/cmdx/blob/main/docs/attributes/coercions.md#declarations) docs for task level configurations.
188
+ See the [Attributes - Coercions](attributes/coercions.md#declarations) docs for task level configurations.
167
189
 
168
190
  ```ruby
169
191
  CMDx.configure do |config|
@@ -189,7 +211,7 @@ end
189
211
 
190
212
  ### Validators
191
213
 
192
- See the [Attributes - Validations](#https://github.com/drexed/cmdx/blob/main/docs/attributes/validations.md#declarations) docs for task level configurations.
214
+ See the [Attributes - Validations](attributes/validations.md#declarations) docs for task level configurations.
193
215
 
194
216
  ```ruby
195
217
  CMDx.configure do |config|
@@ -224,6 +246,8 @@ class GenerateInvoice < CMDx::Task
224
246
  # Global configuration overrides
225
247
  task_breakpoints: ["failed"], # Breakpoint override
226
248
  workflow_breakpoints: [], # Breakpoint override
249
+ backtrace: true, # Toggle backtrace
250
+ backtrace_cleaner: ->(bt) { bt[0..5] }, # Backtrace cleaner
227
251
  logger: CustomLogger.new($stdout), # Custom logger
228
252
 
229
253
  # Task configuration settings
@@ -231,7 +255,10 @@ class GenerateInvoice < CMDx::Task
231
255
  log_level: :info, # Log level override
232
256
  log_formatter: CMDx::LogFormatters::Json.new # Log formatter override
233
257
  tags: ["billing", "financial"], # Logging tags
234
- deprecated: true # Task deprecations
258
+ deprecated: true, # Task deprecations
259
+ retries: 3, # Non-fault exception retries
260
+ retry_on: [External::ApiError], # List of exceptions to retry on
261
+ retry_jitter: 1 # Space between retry iteration, eg: current retry num + 1
235
262
  )
236
263
 
237
264
  def work
@@ -240,13 +267,13 @@ class GenerateInvoice < CMDx::Task
240
267
  end
241
268
  ```
242
269
 
243
- > [!TIP]
244
- > Use task-level settings for tasks that require special handling, such as financial reporting, external API integrations, or critical system operations.
270
+ !!! warning "Important"
271
+
272
+ Retries reuse the same context. By default, all `StandardError` exceptions are retried unless you specify `retry_on`.
245
273
 
246
274
  ### Registrations
247
275
 
248
- Register middlewares, callbacks, coercions, and validators on a specific task.
249
- Deregister options that should not be available.
276
+ Register or deregister middlewares, callbacks, coercions, and validators for specific tasks:
250
277
 
251
278
  ```ruby
252
279
  class SendCampaignEmail < CMDx::Task
@@ -298,8 +325,9 @@ end
298
325
 
299
326
  ### Resetting
300
327
 
301
- > [!WARNING]
302
- > Resetting configuration affects the entire application. Use primarily in test environments or during application initialization.
328
+ !!! warning
329
+
330
+ Resetting affects your entire application. Use this primarily in test environments.
303
331
 
304
332
  ```ruby
305
333
  # Reset to framework defaults
@@ -336,10 +364,6 @@ class ModerateBlogPost < CMDx::Task
336
364
  end
337
365
  ```
338
366
 
339
- > [!TIP]
340
- > Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
341
-
342
- ---
367
+ !!! tip
343
368
 
344
- - **Prev:** [Tips and Tricks](tips_and_tricks.md)
345
- - **Next:** [Basics - Setup](basics/setup.md)
369
+ Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
data/docs/index.md ADDED
@@ -0,0 +1,120 @@
1
+ # CMDx
2
+
3
+ Build business logic that's powerful, predictable, and maintainable.
4
+
5
+ [![Version](https://img.shields.io/gem/v/cmdx)](https://rubygems.org/gems/cmdx)
6
+ [![Build](https://github.com/drexed/cmdx/actions/workflows/ci.yml/badge.svg)](https://github.com/drexed/cmdx/actions/workflows/ci.yml)
7
+ [![License](https://img.shields.io/github/license/drexed/cmdx)](https://github.com/drexed/cmdx/blob/main/LICENSE.txt)
8
+
9
+ Say goodbye to messy service objects. CMDx helps you design business logic with clarity and consistency—build faster, debug easier, and ship with confidence.
10
+
11
+ ## Installation
12
+
13
+ ```sh
14
+ gem install cmdx
15
+ # - or -
16
+ bundle add cmdx
17
+ ```
18
+
19
+ ## Quick Example
20
+
21
+ Build powerful business logic in four simple steps:
22
+
23
+ ### 1. Compose
24
+
25
+ === "Full Featured Task"
26
+
27
+ ```ruby
28
+ class AnalyzeMetrics < CMDx::Task
29
+ register :middleware, CMDx::Middlewares::Correlate, id: -> { Current.request_id }
30
+
31
+ on_success :track_analysis_completion!
32
+
33
+ required :dataset_id, type: :integer, numeric: { min: 1 }
34
+ optional :analysis_type, default: "standard"
35
+
36
+ def work
37
+ if dataset.nil?
38
+ fail!("Dataset not found", code: 404)
39
+ elsif dataset.unprocessed?
40
+ skip!("Dataset not ready for analysis")
41
+ else
42
+ context.result = PValueAnalyzer.execute(dataset:, analysis_type:)
43
+ context.analyzed_at = Time.now
44
+
45
+ SendAnalyzedEmail.execute(user_id: Current.account.manager_id)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def dataset
52
+ @dataset ||= Dataset.find_by(id: dataset_id)
53
+ end
54
+
55
+ def track_analysis_completion!
56
+ dataset.update!(analysis_result_id: context.result.id)
57
+ end
58
+ end
59
+ ```
60
+
61
+ === "Minimum Viable Task"
62
+
63
+ ```ruby
64
+ class SendAnalyzedEmail < CMDx::Task
65
+ def work
66
+ user = User.find(context.user_id)
67
+ MetricsMailer.analyzed(user).deliver_now
68
+ end
69
+ end
70
+ ```
71
+
72
+ ### 2. Execute
73
+
74
+ ```ruby
75
+ result = AnalyzeMetrics.execute(
76
+ dataset_id: 123,
77
+ "analysis_type" => "advanced"
78
+ )
79
+ ```
80
+
81
+ ### 3. React
82
+
83
+ ```ruby
84
+ if result.success?
85
+ puts "Metrics analyzed at #{result.context.analyzed_at}"
86
+ elsif result.skipped?
87
+ puts "Skipping analyzation due to: #{result.reason}"
88
+ elsif result.failed?
89
+ puts "Analyzation failed due to: #{result.reason} with code #{result.metadata[:code]}"
90
+ end
91
+ ```
92
+
93
+ ### 4. Observe
94
+
95
+ ```log
96
+ I, [2022-07-17T18:42:37.000000 #3784] INFO -- CMDx:
97
+ index=1 chain_id="018c2b95-23j4-2kj3-32kj-3n4jk3n4jknf" type="Task" class="SendAnalyzedEmail" state="complete" status="success" metadata={runtime: 347}
98
+
99
+ I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx:
100
+ index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="AnalyzeMetrics" state="complete" status="success" metadata={runtime: 187}
101
+ ```
102
+
103
+ Ready to dive in? Check out the [Getting Started](getting_started.md) guide to learn more.
104
+
105
+ ## Ecosystem
106
+
107
+ - [cmdx-rspec](https://github.com/drexed/cmdx-rspec) - RSpec test matchers
108
+
109
+ For backwards compatibility of certain functionality:
110
+
111
+ - [cmdx-i18n](https://github.com/drexed/cmdx-i18n) - 85+ translations, `v1.5.0` - `v1.6.2`
112
+ - [cmdx-parallel](https://github.com/drexed/cmdx-parallel) - Parallel workflow tasks, `v1.6.1` - `v1.6.2`
113
+
114
+ ## Contributing
115
+
116
+ Bug reports and pull requests are welcome at https://github.com/drexed/cmdx. We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
117
+
118
+ ## License
119
+
120
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,17 +1,10 @@
1
1
  # Internationalization (i18n)
2
2
 
3
- CMDx provides comprehensive internationalization support for all error messages, attribute validation failures, coercion errors, and fault messages. All user-facing text is automatically localized based on the current `I18n.locale`, ensuring your applications can serve global audiences with native-language error reporting.
3
+ CMDx supports 90+ languages out of the box for all error messages, validations, coercions, and faults. Error messages automatically adapt to the current `I18n.locale`, making it easy to build applications for global audiences.
4
4
 
5
- ## Table of Contents
5
+ ## Usage
6
6
 
7
- - [Localization](#localization)
8
- - [Configuration](#configuration)
9
- - [Local Copies](#local-copies)
10
- - [Available Locales](#available-locales)
11
-
12
- ## Localization
13
-
14
- CMDx automatically localizes all error messages based on the `I18n.locale` setting.
7
+ All error messages are automatically localized based on your current locale:
15
8
 
16
9
  ```ruby
17
10
  class ProcessQuote < CMDx::Task
@@ -30,11 +23,11 @@ end
30
23
 
31
24
  ## Configuration
32
25
 
33
- Localization is handled by the `I18n` gem. In Rails applications, locales are loaded automatically and managed via the `I18n.available_locales` setting.
26
+ CMDx uses the `I18n` gem for localization. In Rails, locales load automatically.
34
27
 
35
- ### Local Copies
28
+ ### Copy Locale Files
36
29
 
37
- Execute the following command to copy any locale into the Rails applications `config/locales` directory:
30
+ Copy locale files to your Rails application's `config/locales` directory:
38
31
 
39
32
  ```bash
40
33
  rails generate cmdx:locale [LOCALE]
@@ -131,8 +124,3 @@ rails generate cmdx:locale fr
131
124
  - zh-HK - Chinese (Hong Kong)
132
125
  - zh-TW - Chinese (Traditional)
133
126
  - zh-YUE - Chinese (Yue)
134
-
135
- ---
136
-
137
- - **Prev:** [Logging](logging.md)
138
- - **Next:** [Deprecation](deprecation.md)
@@ -1,21 +1,16 @@
1
1
  # Interruptions - Exceptions
2
2
 
3
- CMDx provides robust exception handling that differs between the `execute` and `execute!` methods. Understanding how unhandled exceptions are processed is crucial for building reliable task execution flows and implementing proper error handling strategies.
4
-
5
- ## Table of Contents
6
-
7
- - [Exception Handling](#exception-handling)
8
- - [Non-bang execution](#non-bang-execution)
9
- - [Bang execution](#bang-execution)
3
+ Exception handling differs between `execute` and `execute!`. Choose the method that matches your error handling strategy.
10
4
 
11
5
  ## Exception Handling
12
6
 
13
- > [!IMPORTANT]
14
- > When designing tasks try not to `raise` your own exceptions directly, instead use `skip!` or `fail!` to signal intent clearly.
7
+ !!! warning "Important"
8
+
9
+ Prefer `skip!` and `fail!` over raising exceptions—they signal intent more clearly.
15
10
 
16
11
  ### Non-bang execution
17
12
 
18
- The `execute` method captures **all** unhandled exceptions and converts them to failed results, ensuring predictable behavior and consistent result processing.
13
+ Captures all exceptions and returns them as failed results:
19
14
 
20
15
  ```ruby
21
16
  class CompressDocument < CMDx::Task
@@ -33,9 +28,13 @@ result.reason #=> "[ActiveRecord::NotFoundError] record not found"
33
28
  result.cause #=> <ActiveRecord::NotFoundError>
34
29
  ```
35
30
 
31
+ !!! note
32
+
33
+ Use `exception_handler` with `execute` to send exceptions to APM tools before they become failed results.
34
+
36
35
  ### Bang execution
37
36
 
38
- The `execute!` method allows unhandled exceptions to propagate, enabling standard Ruby exception handling while respecting CMDx fault configuration.
37
+ Lets exceptions propagate naturally for standard Ruby error handling:
39
38
 
40
39
  ```ruby
41
40
  class CompressDocument < CMDx::Task
@@ -51,8 +50,3 @@ rescue ActiveRecord::NotFoundError => e
51
50
  puts "Handle exception: #{e.message}"
52
51
  end
53
52
  ```
54
-
55
- ---
56
-
57
- - **Prev:** [Interruptions - Faults](faults.md)
58
- - **Next:** [Outcomes - Result](../outcomes/result.md)
@@ -1,19 +1,6 @@
1
1
  # Interruptions - Faults
2
2
 
3
- Faults are exception mechanisms that halt task execution via `skip!` and `fail!` methods. When tasks execute with the `execute!` method, fault exceptions matching the task's interruption status are raised, enabling sophisticated exception handling and control flow patterns.
4
-
5
- ## Table of Contents
6
-
7
- - [Fault Types](#fault-types)
8
- - [Fault Handling](#fault-handling)
9
- - [Data Access](#data-access)
10
- - [Advanced Matching](#advanced-matching)
11
- - [Task-Specific Matching](#task-specific-matching)
12
- - [Custom Logic Matching](#custom-logic-matching)
13
- - [Fault Propagation](#fault-propagation)
14
- - [Basic Propagation](#basic-propagation)
15
- - [Additional Metadata](#additional-metadata)
16
- - [Chain Analysis](#chain-analysis)
3
+ Faults are exceptions raised by `execute!` when tasks halt. They carry rich context about execution state, enabling sophisticated error handling patterns.
17
4
 
18
5
  ## Fault Types
19
6
 
@@ -23,8 +10,9 @@ Faults are exception mechanisms that halt task execution via `skip!` and `fail!`
23
10
  | `CMDx::SkipFault` | `skip!` method | Optional processing, early returns |
24
11
  | `CMDx::FailFault` | `fail!` method | Validation errors, processing failures |
25
12
 
26
- > [!IMPORTANT]
27
- > All fault exceptions inherit from `CMDx::Fault` and provide access to the complete task execution context including result, task, context, and chain information.
13
+ !!! warning "Important"
14
+
15
+ All faults inherit from `CMDx::Fault` and expose result, task, context, and chain data.
28
16
 
29
17
  ## Fault Handling
30
18
 
@@ -45,7 +33,7 @@ end
45
33
 
46
34
  ## Data Access
47
35
 
48
- Faults provide comprehensive access to execution context, eg:
36
+ Access rich execution data from fault exceptions:
49
37
 
50
38
  ```ruby
51
39
  begin
@@ -74,7 +62,7 @@ end
74
62
 
75
63
  ### Task-Specific Matching
76
64
 
77
- Use `for?` to handle faults only from specific task classes, enabling targeted exception handling in complex workflows.
65
+ Handle faults only from specific tasks using `for?`:
78
66
 
79
67
  ```ruby
80
68
  begin
@@ -104,7 +92,7 @@ end
104
92
 
105
93
  ## Fault Propagation
106
94
 
107
- Use `throw!` to propagate failures while preserving fault context and maintaining the error chain for debugging.
95
+ Propagate failures with `throw!` to preserve context and maintain the error chain:
108
96
 
109
97
  ### Basic Propagation
110
98
 
@@ -151,7 +139,7 @@ end
151
139
 
152
140
  ## Chain Analysis
153
141
 
154
- Results provide methods to analyze fault propagation and identify original failure sources in complex execution chains.
142
+ Trace fault origins and propagation through the execution chain:
155
143
 
156
144
  ```ruby
157
145
  result = DocumentWorkflow.execute(invalid_data)
@@ -179,8 +167,3 @@ if result.failed?
179
167
  end
180
168
  end
181
169
  ```
182
-
183
- ---
184
-
185
- - **Prev:** [Interruptions - Halt](halt.md)
186
- - **Next:** [Interruptions - Exceptions](exceptions.md)
@@ -1,24 +1,14 @@
1
1
  # Interruptions - Halt
2
2
 
3
- Halting stops task execution with explicit intent signaling. Tasks provide two primary halt methods that control execution flow and result in different outcomes.
4
-
5
- ## Table of Contents
6
-
7
- - [Skipping](#skipping)
8
- - [Failing](#failing)
9
- - [Metadata Enrichment](#metadata-enrichment)
10
- - [State Transitions](#state-transitions)
11
- - [Execution Behavior](#execution-behavior)
12
- - [Non-bang execution](#non-bang-execution)
13
- - [Bang execution](#bang-execution)
14
- - [Best Practices](#best-practices)
3
+ Stop task execution intentionally using `skip!` or `fail!`. Both methods signal clear intent about why execution stopped.
15
4
 
16
5
  ## Skipping
17
6
 
18
- `skip!` communicates that the task is to be intentionally bypassed. This represents a controlled, intentional interruption where the task determines that execution is not necessary or appropriate.
7
+ Use `skip!` when the task doesn't need to run. It's a no-op, not an error.
19
8
 
20
- > [!IMPORTANT]
21
- > Skipping is a no-op, not a failure or error and are considered successful outcomes.
9
+ !!! warning "Important"
10
+
11
+ Skipped tasks are considered "good" outcomes—they succeeded by doing nothing.
22
12
 
23
13
  ```ruby
24
14
  class ProcessInventory < CMDx::Task
@@ -45,7 +35,7 @@ result = ProcessInventory.execute(inventory_id: 456)
45
35
  result.status #=> "skipped"
46
36
 
47
37
  # Without a reason
48
- result.reason #=> "No reason given"
38
+ result.reason #=> "Unspecified"
49
39
 
50
40
  # With a reason
51
41
  result.reason #=> "Warehouse closed"
@@ -53,7 +43,7 @@ result.reason #=> "Warehouse closed"
53
43
 
54
44
  ## Failing
55
45
 
56
- `fail!` communicates that the task encountered an impediment that prevents successful completion. This represents controlled failure where the task explicitly determines that execution cannot continue.
46
+ Use `fail!` when the task can't complete successfully. It signals controlled, intentional failure:
57
47
 
58
48
  ```ruby
59
49
  class ProcessRefund < CMDx::Task
@@ -80,7 +70,7 @@ result = ProcessRefund.execute(refund_id: 789)
80
70
  result.status #=> "failed"
81
71
 
82
72
  # Without a reason
83
- result.reason #=> "No reason given"
73
+ result.reason #=> "Unspecified"
84
74
 
85
75
  # With a reason
86
76
  result.reason #=> "Refund period has expired"
@@ -88,7 +78,7 @@ result.reason #=> "Refund period has expired"
88
78
 
89
79
  ## Metadata Enrichment
90
80
 
91
- Both halt methods accept metadata to provide additional context about the interruption. Metadata is stored as a hash and becomes available through the result object.
81
+ Enrich halt calls with metadata for better debugging and error handling:
92
82
 
93
83
  ```ruby
94
84
  class ProcessRenewal < CMDx::Task
@@ -188,7 +178,7 @@ end
188
178
 
189
179
  ## Best Practices
190
180
 
191
- Always try to provide a `reason` when using halt methods. This provides clear context for debugging and creates meaningful exception messages.
181
+ Always provide a reason for better debugging and clearer exception messages:
192
182
 
193
183
  ```ruby
194
184
  # Good: Clear, specific reason
@@ -200,11 +190,27 @@ skip!("Paused")
200
190
  fail!("Unsupported")
201
191
 
202
192
  # Bad: Default, cannot determine reason
203
- skip! #=> "No reason given"
204
- fail! #=> "No reason given"
193
+ skip! #=> "Unspecified"
194
+ fail! #=> "Unspecified"
205
195
  ```
206
196
 
207
- ---
197
+ ## Manual Errors
198
+
199
+ For rare cases, manually add errors before halting:
208
200
 
209
- - **Prev:** [Basics - Chain](../basics/chain.md)
210
- - **Next:** [Interruptions - Faults](faults.md)
201
+ !!! warning "Important"
202
+
203
+ Manual errors don't stop execution—you still need to call `fail!` or `skip!`.
204
+
205
+ ```ruby
206
+ class ProcessRenewal < CMDx::Task
207
+ def work
208
+ if document.nonrenewable?
209
+ errors.add(:document, "not renewable")
210
+ fail!("document could not be renewed")
211
+ else
212
+ document.renew!
213
+ end
214
+ end
215
+ end
216
+ ```