cmdx 1.8.0 → 1.9.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.
Files changed (103) 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/yardoc.md +1 -0
  6. data/.irbrc +14 -2
  7. data/CHANGELOG.md +64 -45
  8. data/LLM.md +159 -53
  9. data/README.md +26 -83
  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 +12 -24
  14. data/docs/attributes/defaults.md +3 -16
  15. data/docs/attributes/definitions.md +16 -30
  16. data/docs/attributes/naming.md +3 -13
  17. data/docs/attributes/transformations.md +63 -0
  18. data/docs/attributes/validations.md +14 -33
  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 +109 -76
  26. data/docs/index.md +132 -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 +12 -27
  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 +88 -6
  42. data/lib/cmdx/attribute_registry.rb +20 -0
  43. data/lib/cmdx/attribute_value.rb +56 -10
  44. data/lib/cmdx/callback_registry.rb +31 -2
  45. data/lib/cmdx/chain.rb +34 -1
  46. data/lib/cmdx/coercion_registry.rb +18 -0
  47. data/lib/cmdx/coercions/array.rb +2 -0
  48. data/lib/cmdx/coercions/big_decimal.rb +3 -0
  49. data/lib/cmdx/coercions/boolean.rb +5 -0
  50. data/lib/cmdx/coercions/complex.rb +2 -0
  51. data/lib/cmdx/coercions/date.rb +4 -0
  52. data/lib/cmdx/coercions/date_time.rb +5 -0
  53. data/lib/cmdx/coercions/float.rb +2 -0
  54. data/lib/cmdx/coercions/hash.rb +4 -0
  55. data/lib/cmdx/coercions/integer.rb +2 -0
  56. data/lib/cmdx/coercions/rational.rb +2 -0
  57. data/lib/cmdx/coercions/string.rb +2 -0
  58. data/lib/cmdx/coercions/symbol.rb +2 -0
  59. data/lib/cmdx/coercions/time.rb +5 -0
  60. data/lib/cmdx/configuration.rb +119 -3
  61. data/lib/cmdx/context.rb +36 -0
  62. data/lib/cmdx/deprecator.rb +6 -3
  63. data/lib/cmdx/errors.rb +22 -0
  64. data/lib/cmdx/executor.rb +136 -7
  65. data/lib/cmdx/faults.rb +14 -0
  66. data/lib/cmdx/identifier.rb +2 -0
  67. data/lib/cmdx/locale.rb +3 -0
  68. data/lib/cmdx/log_formatters/json.rb +2 -0
  69. data/lib/cmdx/log_formatters/key_value.rb +2 -0
  70. data/lib/cmdx/log_formatters/line.rb +2 -0
  71. data/lib/cmdx/log_formatters/logstash.rb +2 -0
  72. data/lib/cmdx/log_formatters/raw.rb +2 -0
  73. data/lib/cmdx/middleware_registry.rb +20 -0
  74. data/lib/cmdx/middlewares/correlate.rb +11 -0
  75. data/lib/cmdx/middlewares/runtime.rb +4 -0
  76. data/lib/cmdx/middlewares/timeout.rb +4 -0
  77. data/lib/cmdx/pipeline.rb +24 -5
  78. data/lib/cmdx/railtie.rb +13 -0
  79. data/lib/cmdx/result.rb +133 -2
  80. data/lib/cmdx/task.rb +103 -8
  81. data/lib/cmdx/utils/call.rb +2 -0
  82. data/lib/cmdx/utils/condition.rb +3 -0
  83. data/lib/cmdx/utils/format.rb +5 -0
  84. data/lib/cmdx/validator_registry.rb +18 -0
  85. data/lib/cmdx/validators/exclusion.rb +2 -0
  86. data/lib/cmdx/validators/format.rb +2 -0
  87. data/lib/cmdx/validators/inclusion.rb +2 -0
  88. data/lib/cmdx/validators/length.rb +14 -0
  89. data/lib/cmdx/validators/numeric.rb +14 -0
  90. data/lib/cmdx/validators/presence.rb +2 -0
  91. data/lib/cmdx/version.rb +4 -1
  92. data/lib/cmdx/workflow.rb +10 -0
  93. data/lib/cmdx.rb +9 -0
  94. data/lib/generators/cmdx/locale_generator.rb +0 -1
  95. data/lib/generators/cmdx/templates/install.rb +9 -0
  96. data/mkdocs.yml +122 -0
  97. data/src/cmdx-dark-logo.png +0 -0
  98. data/src/cmdx-favicon.svg +1 -0
  99. data/src/cmdx-light-logo.png +0 -0
  100. data/src/cmdx-logo.svg +1 -0
  101. metadata +14 -3
  102. data/lib/cmdx/freezer.rb +0 -51
  103. data/src/cmdx-logo.png +0 -0
data/README.md CHANGED
@@ -1,68 +1,49 @@
1
- <p align="center">
2
- <img src="./src/cmdx-logo.png" width="200" alt="CMDx Logo">
3
- </p>
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">
4
+
5
+ ---
6
+
7
+ Build business logic that’s powerful, predictable, and maintainable.
8
+
9
+ [Documentation](https://drexed.github.io/cmdx) · [Changelog](./CHANGELOG.md) · [Report Bug](https://github.com/drexed/cmdx/issues) · [Request Feature](https://github.com/drexed/cmdx/issues)
4
10
 
5
- <p align="center">
6
11
  <img alt="Version" src="https://img.shields.io/gem/v/cmdx">
7
12
  <img alt="Build" src="https://github.com/drexed/cmdx/actions/workflows/ci.yml/badge.svg">
8
13
  <img alt="License" src="https://img.shields.io/github/license/drexed/cmdx">
9
- </p>
14
+ </div>
10
15
 
11
- # 🚀 CMDx — Business logic without the chaos
16
+ # CMDx
12
17
 
13
- Stop wrestling with messy service objects. CMDx gives you a clean, consistent way to design business processes:
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.
14
19
 
15
- - Start small with a single `work` method
16
- - Scale to complex tasks and multi-step workflows
17
- - Get built-in flow control, logging, validations, and more...
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.
18
22
 
19
- *Build faster. Debug easier. Stay sane.*
23
+ ## Requirements
20
24
 
21
- ## Compose, Execute, React, Observe pattern
25
+ - Ruby: MRI 3.1+ or JRuby 9.4+.
22
26
 
23
- 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.
24
-
25
- - **Compose** → Define small, contract-driven tasks with typed attributes, validations, and natural workflow composition.
26
- - **Execute** → Run tasks with clear outcomes, intentional halts, and pluggable behaviors via middlewares and callbacks.
27
- - **React** → Adapt to outcomes by chaining follow-up tasks, handling faults, or shaping future flows.
28
- - **Observe** → Capture immutable results, structured logs, and full execution chains for reliable tracing and insight.
27
+ CMDx works with any Ruby framework. Rails support is built-in, but it's framework-agnostic at its core.
29
28
 
30
29
  ## Installation
31
30
 
32
- Add this line to your application's Gemfile:
33
-
34
- ```ruby
35
- gem 'cmdx'
31
+ ```sh
32
+ gem install cmdx
33
+ # - or -
34
+ bundle add cmdx
36
35
  ```
37
36
 
38
- And then execute:
39
-
40
- $ bundle
41
-
42
- Or install it yourself as:
43
-
44
- $ gem install cmdx
45
-
46
37
  ## Quick Example
47
38
 
48
- Here's how a quick 4 step process can open up a world of possibilities:
39
+ Build powerful business logic in four simple steps:
49
40
 
50
41
  ### 1. Compose
51
42
 
52
- #### Minimum Viable Task
53
-
54
43
  ```ruby
55
- class SendAnalyzedEmail < CMDx::Task
56
- def work
57
- user = User.find(context.user_id)
58
- MetricsMailer.analyzed(user).deliver_now
59
- end
60
- end
61
- ```
62
-
63
- #### Fully Featured Task
44
+ # Full-featured task example
45
+ # See docs for minimum viable task examples
64
46
 
65
- ```ruby
66
47
  class AnalyzeMetrics < CMDx::Task
67
48
  register :middleware, CMDx::Middlewares::Correlate, id: -> { Current.request_id }
68
49
 
@@ -127,35 +108,7 @@ I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx:
127
108
  index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="AnalyzeMetrics" state="complete" status="success" metadata={runtime: 187}
128
109
  ```
129
110
 
130
- ## Table of contents
131
-
132
- - [Getting Started](docs/getting_started.md)
133
- - Basics
134
- - [Setup](docs/basics/setup.md)
135
- - [Execution](docs/basics/execution.md)
136
- - [Context](docs/basics/context.md)
137
- - [Chain](docs/basics/chain.md)
138
- - Interruptions
139
- - [Halt](docs/interruptions/halt.md)
140
- - [Faults](docs/interruptions/faults.md)
141
- - [Exceptions](docs/interruptions/exceptions.md)
142
- - Outcomes
143
- - [Result](docs/outcomes/result.md)
144
- - [States](docs/outcomes/states.md)
145
- - [Statuses](docs/outcomes/statuses.md)
146
- - Attributes
147
- - [Definitions](docs/attributes/definitions.md)
148
- - [Naming](docs/attributes/naming.md)
149
- - [Coercions](docs/attributes/coercions.md)
150
- - [Validations](docs/attributes/validations.md)
151
- - [Defaults](docs/attributes/defaults.md)
152
- - [Callbacks](docs/callbacks.md)
153
- - [Middlewares](docs/middlewares.md)
154
- - [Logging](docs/logging.md)
155
- - [Internationalization (i18n)](docs/internationalization.md)
156
- - [Deprecation](docs/deprecation.md)
157
- - [Workflows](docs/workflows.md)
158
- - [Tips and Tricks](docs/tips_and_tricks.md)
111
+ Ready to dive in? Check out the [Getting Started](https://drexed.github.io/cmdx/getting_started/) guide to learn more.
159
112
 
160
113
  ## Ecosystem
161
114
 
@@ -166,20 +119,10 @@ For backwards compatibility of certain functionality:
166
119
  - [cmdx-i18n](https://github.com/drexed/cmdx-i18n) - 85+ translations, `v1.5.0` - `v1.6.2`
167
120
  - [cmdx-parallel](https://github.com/drexed/cmdx-parallel) - Parallel workflow tasks, `v1.6.1` - `v1.6.2`
168
121
 
169
- ## Development
170
-
171
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
172
-
173
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
174
-
175
122
  ## Contributing
176
123
 
177
- Bug reports and pull requests are welcome on GitHub at https://github.com/drexed/cmdx. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
124
+ 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).
178
125
 
179
126
  ## License
180
127
 
181
128
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
182
-
183
- ## Code of Conduct
184
-
185
- Everyone interacting in the CMDx project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
data/docs/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g clip-path="url(#SvgjsClipPath1038)"><rect width="1000" height="1000" fill="#ffffff"></rect><g transform="matrix(6.235191420376605,0,0,6.235191420376605,175.46452176081812,150)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="104.098" height="112.266"><svg width="104.098" height="112.266" viewBox="0 0 60 64.708" xmlns="http://www.w3.org/2000/svg"><path d="M29.907 17.723 26.4 23.17 13.384 3.323h3.507l9.508 14.77 1.938-3.139L18.737 0H7.291L26.4 29.262 45.045 0h-3.97L30 17.54zM9.23 61.293H6.091l18.646-29.447L4.43.093H.46l20.308 31.846L0 64.708l10.985-.092 39.138-61.2h3.323L31.57 37.754l13.662 21.415h3.97L35.445 37.754 59.537.093H48.37zm29.63-23.262 15.047 23.262H43.384l-13.662-20.77-15.508 24.093h3.97l11.63-18 11.723 18H60L41.17 35.354l-.278-.461z" fill="#000"></path></svg></svg></g></g><defs><clipPath id="SvgjsClipPath1038"><rect width="1000" height="1000" x="0" y="0" rx="150" ry="150"></rect></clipPath></defs></svg>
@@ -1,18 +1,8 @@
1
1
  # Attributes - Coercions
2
2
 
3
- Attribute coercions automatically convert task arguments to expected types, ensuring type safety while providing flexible input handling. Coercions transform raw input values into the specified types, supporting simple conversions like string-to-integer and complex operations like JSON parsing.
3
+ Automatically convert inputs to expected types. Coercions handle everything from simple string-to-integer conversions to JSON parsing.
4
4
 
5
- Check out the [Getting Started](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#coercions) docs for global configuration.
6
-
7
- ## Table of Contents
8
-
9
- - [Usage](#usage)
10
- - [Built-in Coercions](#built-in-coercions)
11
- - [Declarations](#declarations)
12
- - [Proc or Lambda](#proc-or-lambda)
13
- - [Class or Module](#class-or-module)
14
- - [Removals](#removals)
15
- - [Error Handling](#error-handling)
5
+ See [Global Configuration](../getting_started.md#coercions) for custom coercion setup.
16
6
 
17
7
  ## Usage
18
8
 
@@ -43,8 +33,9 @@ ParseMetrics.execute(
43
33
  )
44
34
  ```
45
35
 
46
- > [!TIP]
47
- > Specify multiple coercion types for attributes that could be a variety of value formats. CMDx attempts each type in order until one succeeds.
36
+ !!! tip
37
+
38
+ Specify multiple coercion types for attributes that could be a variety of value formats. CMDx attempts each type in order until one succeeds.
48
39
 
49
40
  ## Built-in Coercions
50
41
 
@@ -66,8 +57,9 @@ ParseMetrics.execute(
66
57
 
67
58
  ## Declarations
68
59
 
69
- > [!IMPORTANT]
70
- > Coercions must raise a CMDx::CoercionError and its message is used as part of the fault reason and metadata.
60
+ !!! warning "Important"
61
+
62
+ Custom coercions must raise `CMDx::CoercionError` with a descriptive message.
71
63
 
72
64
  ### Proc or Lambda
73
65
 
@@ -117,10 +109,11 @@ end
117
109
 
118
110
  ## Removals
119
111
 
120
- Remove custom coercions when no longer needed:
112
+ Remove unwanted coercions:
121
113
 
122
- > [!WARNING]
123
- > Only one removal operation is allowed per `deregister` call. Multiple removals require separate calls.
114
+ !!! warning
115
+
116
+ Each `deregister` call removes one coercion. Use multiple calls for batch removals.
124
117
 
125
118
  ```ruby
126
119
  class TransformCoordinates < CMDx::Task
@@ -160,8 +153,3 @@ result.metadata #=> {
160
153
  # }
161
154
  # }
162
155
  ```
163
-
164
- ---
165
-
166
- - **Prev:** [Attributes - Naming](naming.md)
167
- - **Next:** [Attributes - Validations](validations.md)
@@ -1,18 +1,10 @@
1
1
  # Attributes - Defaults
2
2
 
3
- Attribute defaults provide fallback values when arguments are not provided or resolve to `nil`. Defaults ensure tasks have sensible values for optional attributes while maintaining flexibility for callers to override when needed.
4
-
5
- ## Table of Contents
6
-
7
- - [Declarations](#declarations)
8
- - [Static Values](#static-values)
9
- - [Symbol References](#symbol-references)
10
- - [Proc or Lambda](#proc-or-lambda)
11
- - [Coercions and Validations](#coercions-and-validations)
3
+ Provide fallback values for optional attributes. Defaults kick in when values aren't provided or are `nil`.
12
4
 
13
5
  ## Declarations
14
6
 
15
- Defaults apply when attributes are not provided or resolve to `nil`. They work seamlessly with coercion, validation, and nested attributes.
7
+ Defaults work seamlessly with coercions, validations, and nested attributes:
16
8
 
17
9
  ### Static Values
18
10
 
@@ -72,7 +64,7 @@ end
72
64
 
73
65
  ## Coercions and Validations
74
66
 
75
- Defaults are subject to the same coercion and validation rules as provided values, ensuring consistency and catching configuration errors early.
67
+ Defaults follow the same coercion and validation rules as provided values:
76
68
 
77
69
  ```ruby
78
70
  class ScheduleBackup < CMDx::Task
@@ -83,8 +75,3 @@ class ScheduleBackup < CMDx::Task
83
75
  optional :frequency, default: "daily", inclusion: { in: %w[hourly daily weekly monthly] }
84
76
  end
85
77
  ```
86
-
87
- ---
88
-
89
- - **Prev:** [Attributes - Validations](validations.md)
90
- - **Next:** [Callbacks](../callbacks.md)
@@ -1,24 +1,12 @@
1
1
  # Attributes - Definitions
2
2
 
3
- Attributes define the interface between task callers and implementation, enabling automatic validation, type coercion, and method generation. They provide a contract to verify that task execution arguments match expected requirements and structure.
4
-
5
- ## Table of Contents
6
-
7
- - [Declarations](#declarations)
8
- - [Optional](#optional)
9
- - [Required](#required)
10
- - [Sources](#sources)
11
- - [Context](#context)
12
- - [Symbol References](#symbol-references)
13
- - [Proc or Lambda](#proc-or-lambda)
14
- - [Class or Module](#class-or-module)
15
- - [Nesting](#nesting)
16
- - [Error Handling](#error-handling)
3
+ Attributes define your task's interface with automatic validation, type coercion, and accessor generation. They're the contract between callers and your business logic.
17
4
 
18
5
  ## Declarations
19
6
 
20
- > [!TIP]
21
- > Prefer using the `required` and `optional` alias for `attributes` for brevity and to clearly signal intent.
7
+ !!! tip
8
+
9
+ Prefer using the `required` and `optional` alias for `attributes` for brevity and to clearly signal intent.
22
10
 
23
11
  ### Optional
24
12
 
@@ -87,7 +75,7 @@ PublishArticle.execute(
87
75
 
88
76
  ## Sources
89
77
 
90
- Attributes delegate to accessible objects within the task. The default source is `:context`, but any accessible method or object can serve as an attribute source.
78
+ Attributes read from any accessible object—not just context. Use sources to pull data from models, services, or any callable:
91
79
 
92
80
  ### Context
93
81
 
@@ -167,10 +155,11 @@ end
167
155
 
168
156
  ## Nesting
169
157
 
170
- Nested attributes enable complex attribute structures where child attributes automatically inherit their parent as the source. This allows validation and access of structured data.
158
+ Build complex structures with nested attributes. Children inherit their parent as source and support all attribute options:
159
+
160
+ !!! note
171
161
 
172
- > [!NOTE]
173
- > All options available to top-level attributes are available to nested attributes, eg: naming, coercions, and validations
162
+ Nested attributes support all features: naming, coercions, validations, defaults, and more.
174
163
 
175
164
  ```ruby
176
165
  class ConfigureServer < CMDx::Task
@@ -223,15 +212,17 @@ ConfigureServer.execute(
223
212
  )
224
213
  ```
225
214
 
226
- > [!IMPORTANT]
227
- > Child attributes are only required when their parent attribute is provided, enabling flexible optional structures.
215
+ !!! warning "Important"
216
+
217
+ Child requirements only apply when the parent is provided—perfect for optional structures.
228
218
 
229
219
  ## Error Handling
230
220
 
231
- Attribute validation failures result in structured error information with details about each failed attribute.
221
+ Validation failures provide detailed, structured error messages:
222
+
223
+ !!! note
232
224
 
233
- > [!NOTE]
234
- > Nested attributes are only ever evaluated when the parent attribute is available and valid.
225
+ Nested attributes are only validated when their parent is present and valid.
235
226
 
236
227
  ```ruby
237
228
  class ConfigureServer < CMDx::Task
@@ -280,8 +271,3 @@ result.metadata #=> {
280
271
  # }
281
272
  # }
282
273
  ```
283
-
284
- ---
285
-
286
- - **Prev:** [Outcomes - States](../outcomes/states.md)
287
- - **Next:** [Attributes - Naming](naming.md)
@@ -1,15 +1,10 @@
1
1
  # Attributes - Naming
2
2
 
3
- Attribute naming provides method name customization to prevent conflicts and enable flexible attribute access patterns. When attributes share names with existing methods or when multiple attributes from different sources have the same name, affixing ensures clean method resolution within tasks.
3
+ Customize accessor method names to avoid conflicts and improve clarity. Affixing changes only the generated methods—not the original attribute names.
4
4
 
5
- > [!NOTE]
6
- > Affixing modifies only the generated accessor method names within tasks.
5
+ !!! note
7
6
 
8
- ## Table of Contents
9
-
10
- - [Prefix](#prefix)
11
- - [Suffix](#suffix)
12
- - [As](#as)
7
+ Use naming when attributes conflict with existing methods or need better clarity in your code.
13
8
 
14
9
  ## Prefix
15
10
 
@@ -71,8 +66,3 @@ end
71
66
  # Attributes passed as original attribute names
72
67
  ScheduleMaintenance.execute(scheduled_at: DateTime.new(2024, 12, 15, 2, 0, 0))
73
68
  ```
74
-
75
- ---
76
-
77
- - **Prev:** [Attributes - Definitions](definitions.md)
78
- - **Next:** [Attributes - Coercions](coercions.md)
@@ -0,0 +1,63 @@
1
+ # Attributes - Transformations
2
+
3
+ Modify attribute values after coercion but before validation. Perfect for normalization, formatting, and data cleanup.
4
+
5
+ ## Declarations
6
+
7
+ ### Symbol References
8
+
9
+ Reference instance methods by symbol for dynamic value transformations:
10
+
11
+ ```ruby
12
+ class ProcessAnalytics < CMDx::Task
13
+ attribute :options, transform: :compact_blank
14
+ end
15
+ ```
16
+
17
+ ### Proc or Lambda
18
+
19
+ Use anonymous functions for dynamic value transformations:
20
+
21
+ ```ruby
22
+ class CacheContent < CMDx::Task
23
+ # Proc
24
+ attribute :expire_hours, transform: proc { |v| v * 2 }
25
+
26
+ # Lambda
27
+ attribute :compression, transform: ->(v) { v.to_s.upcase.strip[0..2] }
28
+ end
29
+ ```
30
+
31
+ ### Class or Module
32
+
33
+ Use any object that responds to `call` for reusable transformation logic:
34
+
35
+ ```ruby
36
+ class EmailNormalizer
37
+ def call(value)
38
+ value.to_s.downcase.strip
39
+ end
40
+ end
41
+
42
+ class ProcessContacts < CMDx::Task
43
+ # Class or Module
44
+ attribute :email, transform: EmailNormalizer
45
+
46
+ # Instance
47
+ attribute :email, transform: EmailNormalizer.new
48
+ end
49
+ ```
50
+
51
+ ## Validations
52
+
53
+ Validations run on transformed values, ensuring data consistency:
54
+
55
+ ```ruby
56
+ class ScheduleBackup < CMDx::Task
57
+ # Coercions
58
+ attribute :retention_days, type: :integer, transform: proc { |v| v.clamp(1, 5) }
59
+
60
+ # Validations
61
+ optional :frequency, transform: :downcase, inclusion: { in: %w[hourly daily weekly monthly] }
62
+ end
63
+ ```
@@ -1,25 +1,8 @@
1
1
  # Attributes - Validations
2
2
 
3
- Attribute validations ensure task arguments meet specified requirements before execution begins. Validations run after coercions and provide declarative rules for data integrity, supporting both built-in validators and custom validation logic.
4
-
5
- Check out the [Getting Started](https://github.com/drexed/cmdx/blob/main/docs/getting_started.md#validations) docs for global configuration.
6
-
7
- ## Table of Contents
8
-
9
- - [Usage](#usage)
10
- - [Built-in Validators](#built-in-validators)
11
- - [Common Options](#common-options)
12
- - [Exclusion](#exclusion)
13
- - [Format](#format)
14
- - [Inclusion](#inclusion)
15
- - [Length](#length)
16
- - [Numeric](#numeric)
17
- - [Presence](#presence)
18
- - [Declarations](#declarations)
19
- - [Proc or Lambda](#proc-or-lambda)
20
- - [Class or Module](#class-or-module)
21
- - [Removals](#removals)
22
- - [Error Handling](#error-handling)
3
+ Ensure inputs meet requirements before execution. Validations run after coercions, giving you declarative data integrity checks.
4
+
5
+ See [Global Configuration](../getting_started.md#validators) for custom validator setup.
23
6
 
24
7
  ## Usage
25
8
 
@@ -55,8 +38,9 @@ ProcessSubscription.execute(
55
38
  )
56
39
  ```
57
40
 
58
- > [!TIP]
59
- > Validations run after coercions, so you can validate the final coerced values rather than raw input.
41
+ !!! tip
42
+
43
+ Validations run after coercions, so you can validate the final coerced values rather than raw input.
60
44
 
61
45
  ## Built-in Validators
62
46
 
@@ -211,8 +195,9 @@ end
211
195
 
212
196
  ## Declarations
213
197
 
214
- > [!IMPORTANT]
215
- > Custom validators must raise a `CMDx::ValidationError` and its message is used as part of the fault reason and metadata.
198
+ !!! warning "Important"
199
+
200
+ Custom validators must raise `CMDx::ValidationError` with a descriptive message.
216
201
 
217
202
  ### Proc or Lambda
218
203
 
@@ -258,10 +243,11 @@ end
258
243
 
259
244
  ## Removals
260
245
 
261
- Remove custom validators when no longer needed:
246
+ Remove unwanted validators:
262
247
 
263
- > [!WARNING]
264
- > Only one removal operation is allowed per `deregister` call. Multiple removals require separate calls.
248
+ !!! warning
249
+
250
+ Each `deregister` call removes one validator. Use multiple calls for batch removals.
265
251
 
266
252
  ```ruby
267
253
  class SetupApplication < CMDx::Task
@@ -271,7 +257,7 @@ end
271
257
 
272
258
  ## Error Handling
273
259
 
274
- Validation failures provide detailed error information including attribute paths, validation rules, and specific failure reasons:
260
+ Validation failures provide detailed, structured error messages:
275
261
 
276
262
  ```ruby
277
263
  class CreateProject < CMDx::Task
@@ -307,8 +293,3 @@ result.metadata #=> {
307
293
  # }
308
294
  # }
309
295
  ```
310
-
311
- ---
312
-
313
- - **Prev:** [Attributes - Coercions](coercions.md)
314
- - **Next:** [Attributes - Defaults](defaults.md)
data/docs/basics/chain.md CHANGED
@@ -1,20 +1,14 @@
1
1
  # Basics - Chain
2
2
 
3
- Chains automatically group related task executions within a thread, providing unified tracking, correlation, and execution context management. Each thread maintains its own chain through thread-local storage, eliminating the need for manual coordination.
4
-
5
- ## Table of Contents
6
-
7
- - [Management](#management)
8
- - [Links](#links)
9
- - [Inheritance](#inheritance)
10
- - [Structure](#structure)
3
+ Chains automatically track related task executions within a thread. Think of them as execution traces that help you understand what happened and in what order.
11
4
 
12
5
  ## Management
13
6
 
14
- Each thread maintains its own chain context through thread-local storage, providing automatic isolation without manual coordination.
7
+ Each thread maintains its own isolated chain using thread-local storage.
15
8
 
16
- > [!WARNING]
17
- > Chain operations are thread-local. Never share chain references across threads as this can lead to race conditions and data corruption.
9
+ !!! warning
10
+
11
+ Chains are thread-local. Don't share chain references across threads—it causes race conditions.
18
12
 
19
13
  ```ruby
20
14
  # Thread A
@@ -36,10 +30,11 @@ CMDx::Chain.clear #=> Clears current thread's chain
36
30
 
37
31
  ## Links
38
32
 
39
- Every task execution automatically creates or joins the current thread's chain:
33
+ Tasks automatically create or join the current thread's chain:
34
+
35
+ !!! warning "Important"
40
36
 
41
- > [!IMPORTANT]
42
- > Chain creation is automatic and transparent. You don't need to manually manage chain lifecycle.
37
+ Chain management is automatic—no manual lifecycle handling needed.
43
38
 
44
39
  ```ruby
45
40
  class ImportDataset < CMDx::Task
@@ -62,7 +57,7 @@ end
62
57
 
63
58
  ## Inheritance
64
59
 
65
- When tasks call subtasks within the same thread, all executions automatically inherit the current chain, creating a unified execution trail.
60
+ Subtasks automatically inherit the current thread's chain, building a unified execution trail:
66
61
 
67
62
  ```ruby
68
63
  class ImportDataset < CMDx::Task
@@ -87,10 +82,11 @@ chain.results.map { |r| r.task.class }
87
82
 
88
83
  ## Structure
89
84
 
90
- Chains provide comprehensive execution information with state delegation:
85
+ Chains expose comprehensive execution information:
91
86
 
92
- > [!IMPORTANT]
93
- > Chain state always reflects the first (outer-most) task result, not individual subtask outcomes. Subtasks maintain their own success/failure states.
87
+ !!! warning "Important"
88
+
89
+ Chain state reflects the first (outermost) task result. Subtasks maintain their own states.
94
90
 
95
91
  ```ruby
96
92
  result = ImportDataset.execute(dataset_id: 456)
@@ -110,8 +106,3 @@ chain.results.each_with_index do |result, index|
110
106
  puts "#{index}: #{result.task.class} - #{result.status}"
111
107
  end
112
108
  ```
113
-
114
- ---
115
-
116
- - **Prev:** [Basics - Context](context.md)
117
- - **Next:** [Interruptions - Halt](../interruptions/halt.md)