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
data/README.md CHANGED
@@ -1,68 +1,46 @@
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
+ ## Requirements
18
21
 
19
- *Build faster. Debug easier. Stay sane.*
22
+ - Ruby: MRI 3.1+ or JRuby 9.4+.
20
23
 
21
- ## Compose, Execute, React, Observe pattern
22
-
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.
24
+ CMDx works with any Ruby framework. Rails support is built-in, but it's framework-agnostic at its core.
29
25
 
30
26
  ## Installation
31
27
 
32
- Add this line to your application's Gemfile:
33
-
34
- ```ruby
35
- gem 'cmdx'
28
+ ```sh
29
+ gem install cmdx
30
+ # - or -
31
+ bundle add cmdx
36
32
  ```
37
33
 
38
- And then execute:
39
-
40
- $ bundle
41
-
42
- Or install it yourself as:
43
-
44
- $ gem install cmdx
45
-
46
34
  ## Quick Example
47
35
 
48
- Here's how a quick 4 step process can open up a world of possibilities:
36
+ Build powerful business logic in four simple steps:
49
37
 
50
38
  ### 1. Compose
51
39
 
52
- #### Minimum Viable Task
53
-
54
40
  ```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
41
+ # Full-featured task example
42
+ # See docs for minimum viable task examples
64
43
 
65
- ```ruby
66
44
  class AnalyzeMetrics < CMDx::Task
67
45
  register :middleware, CMDx::Middlewares::Correlate, id: -> { Current.request_id }
68
46
 
@@ -127,36 +105,6 @@ I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx:
127
105
  index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="AnalyzeMetrics" state="complete" status="success" metadata={runtime: 187}
128
106
  ```
129
107
 
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)
159
-
160
108
  ## Ecosystem
161
109
 
162
110
  - [cmdx-rspec](https://github.com/drexed/cmdx-rspec) - RSpec test matchers
@@ -166,20 +114,10 @@ For backwards compatibility of certain functionality:
166
114
  - [cmdx-i18n](https://github.com/drexed/cmdx-i18n) - 85+ translations, `v1.5.0` - `v1.6.2`
167
115
  - [cmdx-parallel](https://github.com/drexed/cmdx-parallel) - Parallel workflow tasks, `v1.6.1` - `v1.6.2`
168
116
 
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
117
  ## Contributing
176
118
 
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.
119
+ 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
120
 
179
121
  ## License
180
122
 
181
123
  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
@@ -149,17 +142,14 @@ result = AnalyzePerformance.execute(
149
142
 
150
143
  result.state #=> "interrupted"
151
144
  result.status #=> "failed"
152
- result.reason #=> "Invalid inputs"
145
+ result.reason #=> "Invalid"
153
146
  result.metadata #=> {
154
- # full_message: "iterations could not coerce into an integer. score could not coerce into one of: float, big_decimal.",
155
- # messages: {
156
- # iterations: ["could not coerce into an integer"],
157
- # score: ["could not coerce into one of: float, big_decimal"]
147
+ # errors: {
148
+ # full_message: "iterations could not coerce into an integer. score could not coerce into one of: float, big_decimal.",
149
+ # messages: {
150
+ # iterations: ["could not coerce into an integer"],
151
+ # score: ["could not coerce into one of: float, big_decimal"]
152
+ # }
158
153
  # }
159
154
  # }
160
155
  ```
161
-
162
- ---
163
-
164
- - **Prev:** [Attributes - Naming](naming.md)
165
- - **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
@@ -250,12 +241,14 @@ result = ConfigureServer.execute(server_id: "srv-001")
250
241
 
251
242
  result.state #=> "interrupted"
252
243
  result.status #=> "failed"
253
- result.reason #=> "Invalid inputs"
244
+ result.reason #=> "Invalid"
254
245
  result.metadata #=> {
255
- # full_message: "environment is required. network_config is required.",
256
- # messages: {
257
- # environment: ["is required"],
258
- # network_config: ["is required"]
246
+ # errors: {
247
+ # full_message: "environment is required. network_config is required.",
248
+ # messages: {
249
+ # environment: ["is required"],
250
+ # network_config: ["is required"]
251
+ # }
259
252
  # }
260
253
  # }
261
254
 
@@ -268,16 +261,13 @@ result = ConfigureServer.execute(
268
261
 
269
262
  result.state #=> "interrupted"
270
263
  result.status #=> "failed"
271
- result.reason #=> "Invalid inputs"
264
+ result.reason #=> "Invalid"
272
265
  result.metadata #=> {
273
- # full_message: "port is required.",
274
- # messages: {
275
- # port: ["is required"]
266
+ # errors: {
267
+ # full_message: "port is required.",
268
+ # messages: {
269
+ # port: ["is required"]
270
+ # }
276
271
  # }
277
272
  # }
278
273
  ```
279
-
280
- ---
281
-
282
- - **Prev:** [Outcomes - States](../outcomes/states.md)
283
- - **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
@@ -294,19 +280,16 @@ result = CreateProject.execute(
294
280
 
295
281
  result.state #=> "interrupted"
296
282
  result.status #=> "failed"
297
- result.reason #=> "Invalid inputs"
283
+ result.reason #=> "Invalid"
298
284
  result.metadata #=> {
299
- # full_message: "project_name is too short (minimum is 3 characters). budget must be greater than 1000. priority is not included in the list. contact_email is invalid.",
300
- # messages: {
301
- # project_name: ["is too short (minimum is 3 characters)"],
302
- # budget: ["must be greater than 1000"],
303
- # priority: ["is not included in the list"],
304
- # contact_email: ["is invalid"]
285
+ # errors: {
286
+ # full_message: "project_name is too short (minimum is 3 characters). budget must be greater than 1000. priority is not included in the list. contact_email is invalid.",
287
+ # messages: {
288
+ # project_name: ["is too short (minimum is 3 characters)"],
289
+ # budget: ["must be greater than 1000"],
290
+ # priority: ["is not included in the list"],
291
+ # contact_email: ["is invalid"]
292
+ # }
305
293
  # }
306
294
  # }
307
295
  ```
308
-
309
- ---
310
-
311
- - **Prev:** [Attributes - Coercions](coercions.md)
312
- - **Next:** [Attributes - Defaults](defaults.md)