cmdx 0.5.0 → 1.0.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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/rules/cursor-instructions.mdc +6 -0
  4. data/.rubocop.yml +19 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +95 -28
  7. data/README.md +73 -25
  8. data/docs/ai_prompts.md +319 -0
  9. data/docs/basics/call.md +234 -14
  10. data/docs/basics/chain.md +280 -0
  11. data/docs/basics/context.md +241 -33
  12. data/docs/basics/setup.md +85 -12
  13. data/docs/callbacks.md +283 -0
  14. data/docs/configuration.md +155 -30
  15. data/docs/getting_started.md +145 -22
  16. data/docs/internationalization.md +148 -0
  17. data/docs/interruptions/exceptions.md +198 -11
  18. data/docs/interruptions/faults.md +196 -44
  19. data/docs/interruptions/halt.md +188 -35
  20. data/docs/logging.md +204 -53
  21. data/docs/middlewares.md +745 -0
  22. data/docs/outcomes/result.md +305 -10
  23. data/docs/outcomes/states.md +212 -31
  24. data/docs/outcomes/statuses.md +284 -30
  25. data/docs/parameters/coercions.md +411 -29
  26. data/docs/parameters/defaults.md +258 -25
  27. data/docs/parameters/definitions.md +247 -72
  28. data/docs/parameters/namespacing.md +259 -27
  29. data/docs/parameters/validations.md +173 -168
  30. data/docs/testing.md +560 -0
  31. data/docs/tips_and_tricks.md +103 -42
  32. data/docs/workflows.md +329 -0
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +69 -0
  35. data/lib/cmdx/callback_registry.rb +106 -0
  36. data/lib/cmdx/chain.rb +190 -0
  37. data/lib/cmdx/chain_inspector.rb +149 -0
  38. data/lib/cmdx/chain_serializer.rb +175 -0
  39. data/lib/cmdx/coercions/array.rb +37 -0
  40. data/lib/cmdx/coercions/big_decimal.rb +33 -0
  41. data/lib/cmdx/coercions/boolean.rb +41 -1
  42. data/lib/cmdx/coercions/complex.rb +31 -0
  43. data/lib/cmdx/coercions/date.rb +39 -0
  44. data/lib/cmdx/coercions/date_time.rb +39 -0
  45. data/lib/cmdx/coercions/float.rb +31 -0
  46. data/lib/cmdx/coercions/hash.rb +42 -0
  47. data/lib/cmdx/coercions/integer.rb +32 -0
  48. data/lib/cmdx/coercions/rational.rb +31 -0
  49. data/lib/cmdx/coercions/string.rb +31 -0
  50. data/lib/cmdx/coercions/time.rb +39 -0
  51. data/lib/cmdx/coercions/virtual.rb +31 -0
  52. data/lib/cmdx/configuration.rb +217 -9
  53. data/lib/cmdx/context.rb +173 -2
  54. data/lib/cmdx/core_ext/hash.rb +72 -0
  55. data/lib/cmdx/core_ext/module.rb +94 -0
  56. data/lib/cmdx/core_ext/object.rb +105 -0
  57. data/lib/cmdx/correlator.rb +217 -0
  58. data/lib/cmdx/error.rb +210 -8
  59. data/lib/cmdx/errors.rb +256 -1
  60. data/lib/cmdx/fault.rb +177 -2
  61. data/lib/cmdx/faults.rb +158 -2
  62. data/lib/cmdx/immutator.rb +121 -2
  63. data/lib/cmdx/lazy_struct.rb +261 -18
  64. data/lib/cmdx/log_formatters/json.rb +46 -0
  65. data/lib/cmdx/log_formatters/key_value.rb +46 -0
  66. data/lib/cmdx/log_formatters/line.rb +54 -0
  67. data/lib/cmdx/log_formatters/logstash.rb +64 -0
  68. data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
  69. data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
  70. data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
  71. data/lib/cmdx/log_formatters/raw.rb +54 -0
  72. data/lib/cmdx/logger.rb +85 -0
  73. data/lib/cmdx/logger_ansi.rb +93 -7
  74. data/lib/cmdx/logger_serializer.rb +116 -0
  75. data/lib/cmdx/middleware.rb +74 -0
  76. data/lib/cmdx/middleware_registry.rb +106 -0
  77. data/lib/cmdx/middlewares/correlate.rb +266 -0
  78. data/lib/cmdx/middlewares/timeout.rb +232 -0
  79. data/lib/cmdx/parameter.rb +228 -1
  80. data/lib/cmdx/parameter_inspector.rb +61 -0
  81. data/lib/cmdx/parameter_registry.rb +125 -0
  82. data/lib/cmdx/parameter_serializer.rb +83 -0
  83. data/lib/cmdx/parameter_validator.rb +62 -0
  84. data/lib/cmdx/parameter_value.rb +109 -1
  85. data/lib/cmdx/parameters_inspector.rb +59 -0
  86. data/lib/cmdx/parameters_serializer.rb +102 -0
  87. data/lib/cmdx/railtie.rb +123 -3
  88. data/lib/cmdx/result.rb +367 -25
  89. data/lib/cmdx/result_ansi.rb +105 -9
  90. data/lib/cmdx/result_inspector.rb +76 -0
  91. data/lib/cmdx/result_logger.rb +90 -3
  92. data/lib/cmdx/result_serializer.rb +137 -0
  93. data/lib/cmdx/rspec/result_matchers.rb +917 -0
  94. data/lib/cmdx/rspec/task_matchers.rb +570 -0
  95. data/lib/cmdx/task.rb +405 -37
  96. data/lib/cmdx/task_serializer.rb +74 -2
  97. data/lib/cmdx/utils/ansi_color.rb +95 -0
  98. data/lib/cmdx/utils/log_timestamp.rb +48 -0
  99. data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
  100. data/lib/cmdx/utils/name_affix.rb +78 -0
  101. data/lib/cmdx/validators/custom.rb +82 -0
  102. data/lib/cmdx/validators/exclusion.rb +94 -0
  103. data/lib/cmdx/validators/format.rb +102 -8
  104. data/lib/cmdx/validators/inclusion.rb +104 -0
  105. data/lib/cmdx/validators/length.rb +128 -0
  106. data/lib/cmdx/validators/numeric.rb +128 -0
  107. data/lib/cmdx/validators/presence.rb +93 -7
  108. data/lib/cmdx/version.rb +7 -1
  109. data/lib/cmdx/workflow.rb +394 -0
  110. data/lib/cmdx.rb +25 -64
  111. data/lib/generators/cmdx/install_generator.rb +37 -1
  112. data/lib/generators/cmdx/task_generator.rb +69 -1
  113. data/lib/generators/cmdx/templates/install.rb +43 -15
  114. data/lib/generators/cmdx/workflow_generator.rb +109 -0
  115. data/lib/locales/ar.yml +36 -0
  116. data/lib/locales/cs.yml +36 -0
  117. data/lib/locales/da.yml +36 -0
  118. data/lib/locales/de.yml +36 -0
  119. data/lib/locales/el.yml +36 -0
  120. data/lib/locales/en.yml +20 -20
  121. data/lib/locales/es.yml +20 -20
  122. data/lib/locales/fi.yml +36 -0
  123. data/lib/locales/fr.yml +36 -0
  124. data/lib/locales/he.yml +36 -0
  125. data/lib/locales/hi.yml +36 -0
  126. data/lib/locales/it.yml +36 -0
  127. data/lib/locales/ja.yml +36 -0
  128. data/lib/locales/ko.yml +36 -0
  129. data/lib/locales/nl.yml +36 -0
  130. data/lib/locales/no.yml +36 -0
  131. data/lib/locales/pl.yml +36 -0
  132. data/lib/locales/pt.yml +36 -0
  133. data/lib/locales/ru.yml +36 -0
  134. data/lib/locales/sv.yml +36 -0
  135. data/lib/locales/th.yml +36 -0
  136. data/lib/locales/tr.yml +36 -0
  137. data/lib/locales/vi.yml +36 -0
  138. data/lib/locales/zh.yml +36 -0
  139. metadata +77 -15
  140. data/docs/basics/run.md +0 -34
  141. data/docs/batch.md +0 -53
  142. data/docs/example.md +0 -82
  143. data/docs/hooks.md +0 -62
  144. data/lib/cmdx/batch.rb +0 -43
  145. data/lib/cmdx/parameters.rb +0 -35
  146. data/lib/cmdx/run.rb +0 -39
  147. data/lib/cmdx/run_inspector.rb +0 -26
  148. data/lib/cmdx/run_serializer.rb +0 -20
  149. data/lib/cmdx/task_hook.rb +0 -18
  150. data/lib/generators/cmdx/batch_generator.rb +0 -30
  151. /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
@@ -1,312 +1,317 @@
1
1
  # Parameters - Validations
2
2
 
3
- Parameter values can be validated using one of the built-in validators. Build custom validators
4
- to check parameter values against your own business logic. `i18n` internalization is supported
5
- out of the box.
3
+ Parameter values can be validated using built-in validators or custom validation logic. All validators support internationalization (i18n) and integrate seamlessly with CMDx's error handling system.
6
4
 
7
- Built-in validators are: [Presence](#presence), [Format](#format), [Exclusion](#exclusion),
8
- [Inclusion](#inclusion), [Length](#length), [Numeric](#numeric), [Custom](#custom)
5
+ ## Table of Contents
9
6
 
10
- All validators support the following common options:
7
+ - [TLDR](#tldr)
8
+ - [Common Options](#common-options)
9
+ - [Presence](#presence)
10
+ - [Format](#format)
11
+ - [Exclusion](#exclusion)
12
+ - [Inclusion](#inclusion)
13
+ - [Length](#length)
14
+ - [Numeric](#numeric)
15
+ - [Custom](#custom)
16
+ - [Validation Results](#validation-results)
17
+
18
+ ## TLDR
19
+
20
+ - **Built-in validators** - `presence`, `format`, `inclusion`, `exclusion`, `length`, `numeric`
21
+ - **Common options** - All support `:allow_nil`, `:if`, `:unless`, `:message`
22
+ - **Usage** - Add to parameter definitions: `required :email, presence: true, format: { with: /@/ }`
23
+ - **Conditional** - Use `:if` and `:unless` for conditional validation
24
+ - **Custom validators** - Use `custom: { validator: CustomValidator }` for complex logic
25
+
26
+ ## Common Options
27
+
28
+ All validators support these common options:
11
29
 
12
30
  | Option | Description |
13
31
  | ------------ | ----------- |
14
- | `:allow_nil` | Skip validation if the parameter value is `nil`. |
15
- | `:if` | Specifies a callable method, proc or string to determine if the validation should occur. |
16
- | `:unless` | Specifies a callable method, proc, or string to determine if the validation should not occur. |
17
- | `:message` | The error message to use for a violation. Fallback for any error key below that's not provided. |
32
+ | `:allow_nil` | Skip validation if the parameter value is `nil` |
33
+ | `:if` | Callable method, proc or string to determine if validation should occur |
34
+ | `:unless` | Callable method, proc, or string to determine if validation should not occur |
35
+ | `:message` | Error message for violations. Fallback for specific error keys not provided |
18
36
 
19
37
  > [!NOTE]
20
- > Validators on `optional` parameters will only be executed if they are supplied as
21
- > call arguments.
38
+ > Validators on `optional` parameters only execute when arguments are supplied.
22
39
 
23
40
  ## Presence
24
41
 
25
- Validates that the specified parameter value is not empty. If you want to validate the
26
- presence of a boolean field (where the real values are true and false), you will
27
- want to use `inclusion: { in: [true, false] }`.
42
+ Validates that parameter values are not empty using intelligent type checking:
43
+ - **Strings**: Must contain non-whitespace characters
44
+ - **Collections**: Must not be empty (arrays, hashes, etc.)
45
+ - **Other objects**: Must not be `nil`
28
46
 
29
- ```ruby
30
- class UpdateUserDetailsTask < CMDx::Task
47
+ > [!TIP]
48
+ > For boolean fields where valid values are `true` and `false`, use `inclusion: { in: [true, false] }` instead of presence validation.
31
49
 
32
- # Boolean
33
- required :username, presence: true
34
-
35
- # With custom error message
36
- optional :email, presence: { message: "must not be empty" }
50
+ ```ruby
51
+ class CreateUserTask < CMDx::Task
52
+ required :email, presence: true
53
+ optional :phone, presence: { message: "cannot be blank" }
54
+ required :active, inclusion: { in: [true, false] }
37
55
 
38
56
  def call
39
- # Do work...
57
+ User.create!(email: email, phone: phone, active: active)
40
58
  end
41
-
42
59
  end
43
60
  ```
44
61
 
45
62
  ## Format
46
63
 
47
- Validates whether the specified parameter value is of the correct form,
48
- going by the regular expression provided.
64
+ Validates parameter values against regular expression patterns. Supports positive matching (`with`), negative matching (`without`), or both.
49
65
 
50
66
  ```ruby
51
- class UpdateUserDetailsTask < CMDx::Task
52
-
53
- # With
54
- required :username, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
55
-
56
- # Without (with proc if conditional)
57
- optional :email, format: { without: /NOSPAM/, if: proc { Current.account.spam_check? } }
67
+ class RegisterUserTask < CMDx::Task
68
+ required :email, format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i }
69
+ required :username, format: { without: /\A(admin|root|system)\z/i }
70
+ optional :password, format: {
71
+ with: /\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}\z/,
72
+ without: /password|123456/i,
73
+ if: :strong_password_required?
74
+ }
58
75
 
59
76
  def call
60
- # Do work...
77
+ create_user_account
61
78
  end
62
79
 
80
+ private
81
+
82
+ def strong_password_required?
83
+ context.account.security_policy.strong_passwords?
84
+ end
63
85
  end
64
86
  ```
65
87
 
66
- Constraint options:
88
+ **Options:**
67
89
 
68
90
  | Option | Description |
69
91
  | ---------- | ----------- |
70
- | `:with` | Regular expression that if the parameter value matches will result in a successful validation. |
71
- | `:without` | Regular expression that if the parameter value does not match will result in a successful validation. |
92
+ | `:with` | Regular expression that value must match |
93
+ | `:without` | Regular expression that value must not match |
72
94
 
73
95
  ## Exclusion
74
96
 
75
- Validates that the specified parameter is not in a particular enumerable object.
97
+ Validates that parameter values are not in a specific enumerable (array, range, etc.).
76
98
 
77
99
  ```ruby
78
- class DetermineBoxSizeTask < CMDx::Task
79
-
80
- # Array
81
- required :width, exclusion: { in: [12, 24, 36] }
82
-
83
- # Range (with allow nil)
84
- optional :length, exclusion: { in: 12..36, allow_nil: true }
100
+ class ProcessPaymentTask < CMDx::Task
101
+ required :payment_method, exclusion: { in: %w[cash check] }
102
+ required :amount, exclusion: { in: 0.0..0.99, in_message: "must be at least $1.00" }
103
+ optional :discount_percent, exclusion: { in: 90..100 }
85
104
 
86
105
  def call
87
- # Do work...
106
+ charge_payment
88
107
  end
89
-
90
108
  end
91
109
  ```
92
110
 
93
- Constraint options:
111
+ **Options:**
94
112
 
95
113
  | Option | Description |
96
114
  | ------------ | ----------- |
97
- | `:in` | An enumerable object of unavailable items such as an array or range. |
98
- | `:within` | A synonym (or alias) for `:in` |
115
+ | `:in` | Enumerable of forbidden values |
116
+ | `:within` | Alias for `:in` |
99
117
 
100
- Other options:
118
+ **Error Messages:**
101
119
 
102
120
  | Option | Description |
103
121
  | ----------------- | ----------- |
104
- | `:of_message` | The error message if the parameter value is in array. (default: "must not be one of: %{values}") |
105
- | `:in_message` | The error message if the parameter value is in range. (default: "must not be within %{min} and %{max}") |
106
- | `:within_message` | A synonym (or alias) for `:in_message` |
122
+ | `:of_message` | Error when value is in array (default: "must not be one of: %{values}") |
123
+ | `:in_message` | Error when value is in range (default: "must not be within %{min} and %{max}") |
124
+ | `:within_message` | Alias for `:in_message` |
107
125
 
108
126
  ## Inclusion
109
127
 
110
- Validates that the specified parameter value is in a particular enumerable object.
128
+ Validates that parameter values are in a specific enumerable (array, range, etc.).
111
129
 
112
130
  ```ruby
113
- class DetermineBoxSizeTask < CMDx::Task
114
-
115
- # Array
116
- required :width, inclusion: { in: [12, 24, 36] }
117
-
118
- # Range (with custom error message)
119
- optional :length, inclusion: { in: 12..36, unless: :length_check? }
131
+ class UpdateOrderTask < CMDx::Task
132
+ required :status, inclusion: { in: %w[pending processing shipped delivered] }
133
+ required :priority, inclusion: { in: 1..5 }
134
+ optional :shipping_method, inclusion: {
135
+ in: %w[standard express overnight],
136
+ unless: :digital_order?
137
+ }
120
138
 
121
139
  def call
122
- # Do work...
140
+ update_order_status
123
141
  end
124
142
 
125
143
  private
126
144
 
127
- def skip_length_check?
128
- false
145
+ def digital_order?
146
+ context.order.digital_items_only?
129
147
  end
130
-
131
148
  end
132
149
  ```
133
150
 
134
- Constraint options:
151
+ **Options:**
135
152
 
136
153
  | Option | Description |
137
154
  | ------------ | ----------- |
138
- | `:in` | An enumerable object of available items such as an array or range. |
139
- | `:within` | A synonym (or alias) for `:in` |
155
+ | `:in` | Enumerable of allowed values |
156
+ | `:within` | Alias for `:in` |
140
157
 
141
- Other options:
158
+ **Error Messages:**
142
159
 
143
160
  | Option | Description |
144
161
  | ----------------- | ----------- |
145
- | `:of_message` | The error message if the parameter value is not in array. (default: "must be one of: %{values}") |
146
- | `:in_message` | The error message if the parameter value is not in range. (default: "must be within %{min} and %{max}") |
147
- | `:within_message` | A synonym (or alias) for `:in_message` |
162
+ | `:of_message` | Error when value not in array (default: "must be one of: %{values}") |
163
+ | `:in_message` | Error when value not in range (default: "must be within %{min} and %{max}") |
164
+ | `:within_message` | Alias for `:in_message` |
148
165
 
149
166
  ## Length
150
167
 
151
- Validates that the specified parameter value matches the length restrictions supplied.
152
- Only one constraint option can be used at a time apart from `:min` and `:max`
153
- that can be combined together:
168
+ Validates parameter length/size. Works with any object responding to `#size` or `#length`. Only one constraint option can be used at a time, except `:min` and `:max` which can be combined.
154
169
 
155
170
  ```ruby
156
- class UpdateUserDetailsTask < CMDx::Task
157
-
158
- # Range (with custom error message)
159
- required :email, length: { within: 12..36, message: "must be within range" }
160
- required :username, length: { not_within: 48..96 }
161
-
162
- # Boundary
163
- optional :first_name, length: { min: 24 }
164
- optional :middle_name, length: { max: 48 }
165
- optional :last_name, length: { min: 24, max: 48 }
166
-
167
- # Exact
168
- required :title, length: { is: 24 }
169
- required :count, length: { is_not: 48 }
171
+ class CreatePostTask < CMDx::Task
172
+ required :title, length: { within: 5..100 }
173
+ required :body, length: { min: 20 }
174
+ optional :summary, length: { max: 200 }
175
+ required :slug, length: { min: 3, max: 50 }
176
+ required :category_code, length: { is: 3 }
170
177
 
171
178
  def call
172
- # Do work...
179
+ create_blog_post
173
180
  end
174
-
175
181
  end
176
182
  ```
177
183
 
178
- Constraint options:
184
+ **Options:**
179
185
 
180
186
  | Option | Description |
181
187
  | ------------- | ----------- |
182
- | `:within` | A range specifying the minimum and maximum size of the parameter value. |
183
- | `:not_within` | A range specifying the minimum and maximum size of the parameter value it's not to be. |
184
- | `:in` | A synonym (or alias) for `:within` |
185
- | `:not_in` | A synonym (or alias) for `:not_within` |
186
- | `:min` | The minimum size of the parameter value. |
187
- | `:max` | The maximum size of the parameter value. |
188
- | `:is` | The exact size of the parameter value. |
189
- | `:is_not` | The exact size of the parameter value it's not to be. |
188
+ | `:within` | Range specifying min and max size |
189
+ | `:not_within` | Range specifying forbidden size range |
190
+ | `:in` | Alias for `:within` |
191
+ | `:not_in` | Alias for `:not_within` |
192
+ | `:min` | Minimum size required |
193
+ | `:max` | Maximum size allowed |
194
+ | `:is` | Exact size required |
195
+ | `:is_not` | Size that is forbidden |
190
196
 
191
- Other options:
197
+ **Error Messages:**
192
198
 
193
199
  | Option | Description |
194
200
  | --------------------- | ----------- |
195
- | `:within_message` | The error message if the parameter value is within the value range. (default: "length must not be within %{min} and %{max}") |
196
- | `:not_within_message` | The error message if the parameter value is not within the value range. (default: "length must be within %{min} and %{max}") |
197
- | `:in_message` | A synonym (or alias) for `:within_message` |
198
- | `:not_in_message` | A synonym (or alias) for `:not_within_message` |
199
- | `:min_message` | The error message if the parameter value is below the min value. (default: "length must be at least %{min}") |
200
- | `:max_message` | The error message if the parameter value is above the min value. (default: "length must be at most %{max}") |
201
- | `:is_message` | The error message if the parameter value is the exact value. (default: "length must be %{is}") |
202
- | `:is_not_message` | The error message if the parameter value is not the exact value. (default: "length must not be %{is_not}") |
201
+ | `:within_message` | "length must be within %{min} and %{max}" |
202
+ | `:not_within_message` | "length must not be within %{min} and %{max}" |
203
+ | `:min_message` | "length must be at least %{min}" |
204
+ | `:max_message` | "length must be at most %{max}" |
205
+ | `:is_message` | "length must be %{is}" |
206
+ | `:is_not_message` | "length must not be %{is_not}" |
203
207
 
204
208
  ## Numeric
205
209
 
206
- Validates that the specified parameter value matches the numeric restrictions supplied.
207
- Only one constraint option can be used at a time apart from `:min` and `:max`
208
- that can be combined together:
210
+ Validates numeric values against constraints. Works with any numeric type. Only one constraint option can be used at a time, except `:min` and `:max` which can be combined.
209
211
 
210
212
  ```ruby
211
- class UpdateUserDetailsTask < CMDx::Task
212
-
213
- # Range (with custom error message)
214
- required :height, numeric: { within: 36..196 }
215
- required :weight, numeric: { not_within: 0..5 }
216
-
217
- # Boundary
218
- optional :dob_year, numeric: { min: 1900 }
219
- optional :dob_day, numeric: { max: 31 }
220
- optional :dob_month, numeric: { min: 1, max: 12 }
221
-
222
- # Exact
223
- required :age, numeric: { is: 18 }
224
- required :parents, numeric: { is_not: 0 }
213
+ class ProcessOrderTask < CMDx::Task
214
+ required :quantity, numeric: { within: 1..100 }
215
+ required :price, numeric: { min: 0.01 }
216
+ optional :discount_percent, numeric: { max: 50 }
217
+ required :tax_rate, numeric: { min: 0, max: 0.15 }
218
+ required :api_version, numeric: { is: 2 }
225
219
 
226
220
  def call
227
- # Do work...
221
+ calculate_order_total
228
222
  end
229
-
230
223
  end
231
224
  ```
232
225
 
233
- Constraint options:
226
+ **Options:**
234
227
 
235
228
  | Option | Description |
236
229
  | ------------- | ----------- |
237
- | `:within` | A range specifying the minimum and maximum size of the parameter value. |
238
- | `:not_within` | A range specifying the minimum and maximum size of the parameter value it's not to be. |
239
- | `:in` | A synonym (or alias) for `:within` |
240
- | `:not_in` | A synonym (or alias) for `:not_within` |
241
- | `:min` | The minimum size of the parameter value. |
242
- | `:max` | The maximum size of the parameter value. |
243
- | `:is` | The exact size of the parameter value. |
244
- | `:is_not` | The exact size of the parameter value it's not to be. |
230
+ | `:within` | Range specifying min and max value |
231
+ | `:not_within` | Range specifying forbidden value range |
232
+ | `:in` | Alias for `:within` |
233
+ | `:not_in` | Alias for `:not_within` |
234
+ | `:min` | Minimum value required |
235
+ | `:max` | Maximum value allowed |
236
+ | `:is` | Exact value required |
237
+ | `:is_not` | Value that is forbidden |
245
238
 
246
- Other options:
239
+ **Error Messages:**
247
240
 
248
241
  | Option | Description |
249
242
  | --------------------- | ----------- |
250
- | `:within_message` | The error message if the parameter value is within the value range. (default: "must not be within %{min} and %{max}") |
251
- | `:not_within_message` | The error message if the parameter value is not within the value range. (default: "must be within %{min} and %{max}") |
252
- | `:in_message` | A synonym (or alias) for `:within_message` |
253
- | `:not_in_message` | A synonym (or alias) for `:not_within_message` |
254
- | `:min_message` | The error message if the parameter value is below the min value. (default: "must be at least %{min}") |
255
- | `:max_message` | The error message if the parameter value is above the min value. (default: "must be at most %{max}") |
256
- | `:is_message` | The error message if the parameter value is the exact value. (default: "must be %{is}") |
257
- | `:is_not_message` | The error message if the parameter value is not the exact value. (default: "must not be %{is_not}") |
243
+ | `:within_message` | "must be within %{min} and %{max}" |
244
+ | `:not_within_message` | "must not be within %{min} and %{max}" |
245
+ | `:min_message` | "must be at least %{min}" |
246
+ | `:max_message` | "must be at most %{max}" |
247
+ | `:is_message` | "must be %{is}" |
248
+ | `:is_not_message` | "must not be %{is_not}" |
258
249
 
259
250
  ## Custom
260
251
 
261
- Validate the specified parameter value using custom validators.
252
+ Validates using custom logic. Accepts any callable object (class, proc, lambda) implementing a `call` method that returns truthy for valid values.
262
253
 
263
254
  ```ruby
264
- class TLDValidator
255
+ class EmailDomainValidator
265
256
  def self.call(value, options)
266
- tld = options.dig(:custom, :tld) || %w[com]
267
- value.ends_with?(tld)
257
+ allowed_domains = options.dig(:custom, :allowed_domains) || ['example.com']
258
+ domain = value.split('@').last
259
+ allowed_domains.include?(domain)
268
260
  end
269
261
  end
270
262
 
271
- class UpdateUserDetailsTask < CMDx::Task
263
+ class CreateAccountTask < CMDx::Task
264
+ required :work_email, custom: {
265
+ validator: EmailDomainValidator,
266
+ allowed_domains: ['company.com', 'partner.org'],
267
+ message: "must be from an approved domain"
268
+ }
272
269
 
273
- # Basic
274
- required :unconfirmed_email, custom: { validator: TLDValidator }
275
-
276
- # Passable options (with custom error message)
277
- optional :confirmed_email, custom: { validator: TLDValidator, tld: %w[com net org], message: "is not valid" }
270
+ required :age, custom: {
271
+ validator: ->(value, options) { value.between?(18, 120) },
272
+ message: "must be a valid age"
273
+ }
278
274
 
279
275
  def call
280
- # Do work...
276
+ create_user_account
281
277
  end
282
-
283
278
  end
284
279
  ```
285
280
 
286
- Constraint options:
281
+ **Options:**
287
282
 
288
283
  | Option | Description |
289
284
  | ------------ | ----------- |
290
- | `:validator` | Callable class that returns true or false. |
285
+ | `:validator` | Callable object returning true/false. Receives value and options as parameters |
291
286
 
292
- ## Results
287
+ ## Validation Results
293
288
 
294
- The following represents a result output example of a failed validation.
289
+ When validation fails, tasks enter a failed state with detailed error information:
295
290
 
296
291
  ```ruby
297
- result = DetermineBoxSizeTask.call
292
+ class CreateUserTask < CMDx::Task
293
+ required :email, format: { with: /@/, message: "format is invalid" }
294
+ required :username, presence: { message: "cannot be empty" }
295
+ end
296
+
297
+ result = CreateUserTask.call(email: "invalid", username: "")
298
+
298
299
  result.state #=> "interrupted"
299
300
  result.status #=> "failed"
300
301
  result.metadata #=> {
301
- #=> reason: "email format is not valid. username cannot be empty.",
302
+ #=> reason: "email format is invalid. username cannot be empty.",
302
303
  #=> messages: {
303
- #=> email: ["format is not valid"],
304
+ #=> email: ["format is invalid"],
304
305
  #=> username: ["cannot be empty"]
305
306
  #=> }
306
307
  #=> }
308
+
309
+ # Accessing individual error messages
310
+ result.metadata[:messages][:email] #=> ["format is invalid"]
311
+ result.metadata[:messages][:username] #=> ["cannot be empty"]
307
312
  ```
308
313
 
309
314
  ---
310
315
 
311
- - **Prev:** [Coercions](https://github.com/drexed/cmdx/blob/main/docs/parameters/coercions.md)
312
- - **Next:** [Defaults](https://github.com/drexed/cmdx/blob/main/docs/parameters/defaults.md)
316
+ - **Prev:** [Parameters - Coercions](coercions.md)
317
+ - **Next:** [Parameters - Defaults](defaults.md)