cmdx 1.1.2 → 1.5.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.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +5 -133
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- data/lib/locales/zh.yml +0 -35
@@ -1,405 +0,0 @@
|
|
1
|
-
# Parameters - Validations
|
2
|
-
|
3
|
-
Parameter validations ensure data integrity by applying constraints to task inputs. All validators integrate with CMDx's error handling system and support internationalization for consistent error messaging across different locales.
|
4
|
-
|
5
|
-
## Table of Contents
|
6
|
-
|
7
|
-
- [TLDR](#tldr)
|
8
|
-
- [Common Options](#common-options)
|
9
|
-
- [Presence](#presence)
|
10
|
-
- [Format](#format)
|
11
|
-
- [Inclusion](#inclusion)
|
12
|
-
- [Exclusion](#exclusion)
|
13
|
-
- [Length](#length)
|
14
|
-
- [Numeric](#numeric)
|
15
|
-
- [Error Handling](#error-handling)
|
16
|
-
- [Conditional Validation](#conditional-validation)
|
17
|
-
|
18
|
-
## TLDR
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
# Basic validation
|
22
|
-
required :email, presence: true, format: { with: /@/ }
|
23
|
-
required :status, inclusion: { in: %w[pending active] }
|
24
|
-
required :password, length: { min: 8 }
|
25
|
-
|
26
|
-
# Conditional validation
|
27
|
-
optional :phone, presence: { if: :phone_required? }
|
28
|
-
required :age, numeric: { min: 18, unless: :minor_allowed? }
|
29
|
-
|
30
|
-
# Custom messages
|
31
|
-
required :username, exclusion: { in: %w[admin root], message: "reserved name" }
|
32
|
-
```
|
33
|
-
|
34
|
-
## Common Options
|
35
|
-
|
36
|
-
> [!NOTE]
|
37
|
-
> Validators on `optional` parameters only execute when arguments are provided.
|
38
|
-
|
39
|
-
All validators support these common options:
|
40
|
-
|
41
|
-
| Option | Description |
|
42
|
-
|--------|-------------|
|
43
|
-
| `:allow_nil` | Skip validation when value is `nil` |
|
44
|
-
| `:if` | Method, proc, or string determining when to validate |
|
45
|
-
| `:unless` | Method, proc, or string determining when to skip validation |
|
46
|
-
| `:message` | Custom error message for validation failures |
|
47
|
-
|
48
|
-
## Presence
|
49
|
-
|
50
|
-
Validates that parameter values are not empty using intelligent type checking:
|
51
|
-
|
52
|
-
- **Strings**: Must contain non-whitespace characters
|
53
|
-
- **Collections**: Must not be empty (arrays, hashes, sets)
|
54
|
-
- **Other objects**: Must not be `nil`
|
55
|
-
|
56
|
-
> [!TIP]
|
57
|
-
> For boolean fields accepting `true` and `false`, use `inclusion: { in: [true, false] }` instead of presence validation.
|
58
|
-
|
59
|
-
```ruby
|
60
|
-
class CreateUserTask < CMDx::Task
|
61
|
-
required :email, presence: true
|
62
|
-
required :name, presence: { message: "cannot be blank" }
|
63
|
-
required :active, inclusion: { in: [true, false] }
|
64
|
-
|
65
|
-
def call
|
66
|
-
User.create!(email: email, name: name, active: active)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# Valid inputs
|
71
|
-
CreateUserTask.call(email: "user@example.com", name: "John", active: true)
|
72
|
-
|
73
|
-
# Invalid inputs
|
74
|
-
CreateUserTask.call(email: "", name: " ", active: nil)
|
75
|
-
# → ValidationError: "email can't be blank. name cannot be blank. active must be one of: true, false"
|
76
|
-
```
|
77
|
-
|
78
|
-
## Format
|
79
|
-
|
80
|
-
Validates parameter values against regular expression patterns. Supports positive matching (`with`), negative matching (`without`), or both.
|
81
|
-
|
82
|
-
```ruby
|
83
|
-
class RegisterUserTask < CMDx::Task
|
84
|
-
required :email, format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i }
|
85
|
-
required :username, format: { without: /\A(admin|root|system)\z/i }
|
86
|
-
|
87
|
-
optional :password, format: {
|
88
|
-
with: /\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}\z/,
|
89
|
-
without: /password|123456/i,
|
90
|
-
if: :secure_password_required?
|
91
|
-
}
|
92
|
-
|
93
|
-
def call
|
94
|
-
create_user_account
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def secure_password_required?
|
100
|
-
context.security_policy.enforce_strong_passwords?
|
101
|
-
end
|
102
|
-
end
|
103
|
-
```
|
104
|
-
|
105
|
-
**Options:**
|
106
|
-
|
107
|
-
| Option | Description |
|
108
|
-
|--------|-------------|
|
109
|
-
| `:with` | Regular expression that value must match |
|
110
|
-
| `:without` | Regular expression that value must not match |
|
111
|
-
|
112
|
-
## Inclusion
|
113
|
-
|
114
|
-
> [!IMPORTANT]
|
115
|
-
> Validates that parameter values are within a specific set of allowed values (array, range, or other enumerable).
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
class UpdateOrderTask < CMDx::Task
|
119
|
-
required :status, inclusion: { in: %w[pending processing shipped delivered] }
|
120
|
-
required :priority, inclusion: { in: 1..5 }
|
121
|
-
|
122
|
-
optional :shipping_method, inclusion: {
|
123
|
-
in: %w[standard express overnight],
|
124
|
-
unless: :digital_product?
|
125
|
-
}
|
126
|
-
|
127
|
-
def call
|
128
|
-
update_order_attributes
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
def digital_product?
|
134
|
-
context.order.items.all?(&:digital?)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
```
|
138
|
-
|
139
|
-
**Options:**
|
140
|
-
|
141
|
-
| Option | Description |
|
142
|
-
|--------|-------------|
|
143
|
-
| `:in` | Enumerable of allowed values |
|
144
|
-
| `:within` | Alias for `:in` |
|
145
|
-
|
146
|
-
**Custom Error Messages:**
|
147
|
-
|
148
|
-
| Option | Description |
|
149
|
-
|--------|-------------|
|
150
|
-
| `:of_message` | Error for array validation (default: "must be one of: %{values}") |
|
151
|
-
| `:in_message` | Error for range validation (default: "must be within %{min} and %{max}") |
|
152
|
-
| `:within_message` | Alias for `:in_message` |
|
153
|
-
|
154
|
-
## Exclusion
|
155
|
-
|
156
|
-
Validates that parameter values are not within a specific set of forbidden values.
|
157
|
-
|
158
|
-
```ruby
|
159
|
-
class ProcessPaymentTask < CMDx::Task
|
160
|
-
required :payment_method, exclusion: { in: %w[cash check] }
|
161
|
-
required :amount, exclusion: { in: 0.0..0.99, in_message: "must be at least $1.00" }
|
162
|
-
|
163
|
-
optional :promo_code, exclusion: {
|
164
|
-
in: %w[EXPIRED INVALID],
|
165
|
-
of_message: "is not valid"
|
166
|
-
}
|
167
|
-
|
168
|
-
def call
|
169
|
-
charge_payment_method
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Valid usage
|
174
|
-
ProcessPaymentTask.call(
|
175
|
-
payment_method: "credit_card",
|
176
|
-
amount: 29.99,
|
177
|
-
promo_code: "SAVE20"
|
178
|
-
)
|
179
|
-
```
|
180
|
-
|
181
|
-
**Options:**
|
182
|
-
|
183
|
-
| Option | Description |
|
184
|
-
|--------|-------------|
|
185
|
-
| `:in` | Enumerable of forbidden values |
|
186
|
-
| `:within` | Alias for `:in` |
|
187
|
-
|
188
|
-
**Custom Error Messages:**
|
189
|
-
|
190
|
-
| Option | Description |
|
191
|
-
|--------|-------------|
|
192
|
-
| `:of_message` | Error for array validation (default: "must not be one of: %{values}") |
|
193
|
-
| `:in_message` | Error for range validation (default: "must not be within %{min} and %{max}") |
|
194
|
-
| `:within_message` | Alias for `:in_message` |
|
195
|
-
|
196
|
-
## Length
|
197
|
-
|
198
|
-
Validates parameter length for 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.
|
199
|
-
|
200
|
-
```ruby
|
201
|
-
class CreatePostTask < CMDx::Task
|
202
|
-
required :title, length: { within: 5..100 }
|
203
|
-
required :content, length: { min: 50 }
|
204
|
-
required :slug, length: { min: 3, max: 50 }
|
205
|
-
|
206
|
-
optional :summary, length: { max: 200, allow_nil: true }
|
207
|
-
optional :category_code, length: { is: 3 }
|
208
|
-
|
209
|
-
def call
|
210
|
-
Post.create!(title: title, content: content, slug: slug)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
```
|
214
|
-
|
215
|
-
**Constraint Options:**
|
216
|
-
|
217
|
-
| Option | Description |
|
218
|
-
|--------|-------------|
|
219
|
-
| `:within` / `:in` | Range specifying min and max length |
|
220
|
-
| `:not_within` / `:not_in` | Range specifying forbidden length range |
|
221
|
-
| `:min` | Minimum length required |
|
222
|
-
| `:max` | Maximum length allowed |
|
223
|
-
| `:is` | Exact length required |
|
224
|
-
| `:is_not` | Length that is forbidden |
|
225
|
-
|
226
|
-
**Error Messages:**
|
227
|
-
|
228
|
-
| Option | Description |
|
229
|
-
|--------|-------------|
|
230
|
-
| `:within_message` | "length must be within %{min} and %{max}" |
|
231
|
-
| `:not_within_message` | "length must not be within %{min} and %{max}" |
|
232
|
-
| `:min_message` | "length must be at least %{min}" |
|
233
|
-
| `:max_message` | "length must be at most %{max}" |
|
234
|
-
| `:is_message` | "length must be %{is}" |
|
235
|
-
| `:is_not_message` | "length must not be %{is_not}" |
|
236
|
-
|
237
|
-
## Numeric
|
238
|
-
|
239
|
-
Validates numeric values against constraints. Works with any numeric type including integers, floats, and decimals.
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
class ProcessOrderTask < CMDx::Task
|
243
|
-
required :quantity, numeric: { within: 1..100 }
|
244
|
-
required :price, numeric: { min: 0.01 }
|
245
|
-
required :tax_rate, numeric: { min: 0, max: 0.25 }
|
246
|
-
|
247
|
-
optional :discount, numeric: { max: 50, allow_nil: true }
|
248
|
-
optional :api_version, numeric: { is: 2 }
|
249
|
-
|
250
|
-
def call
|
251
|
-
calculate_order_total
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
# Error example
|
256
|
-
ProcessOrderTask.call(
|
257
|
-
quantity: 0, # Below minimum
|
258
|
-
price: -5.00, # Below minimum
|
259
|
-
tax_rate: 0.30 # Above maximum
|
260
|
-
)
|
261
|
-
# → ValidationError: "quantity must be within 1 and 100. price must be at least 0.01. tax_rate must be at most 0.25"
|
262
|
-
```
|
263
|
-
|
264
|
-
**Constraint Options:**
|
265
|
-
|
266
|
-
| Option | Description |
|
267
|
-
|--------|-------------|
|
268
|
-
| `:within` / `:in` | Range specifying min and max value |
|
269
|
-
| `:not_within` / `:not_in` | Range specifying forbidden value range |
|
270
|
-
| `:min` | Minimum value required |
|
271
|
-
| `:max` | Maximum value allowed |
|
272
|
-
| `:is` | Exact value required |
|
273
|
-
| `:is_not` | Value that is forbidden |
|
274
|
-
|
275
|
-
## Error Handling
|
276
|
-
|
277
|
-
> [!WARNING]
|
278
|
-
> Validation failures cause tasks to enter a failed state with detailed error information including parameter paths and specific violation messages.
|
279
|
-
|
280
|
-
```ruby
|
281
|
-
class CreateUserTask < CMDx::Task
|
282
|
-
required :email, format: { with: /@/, message: "must be valid" }
|
283
|
-
required :username, presence: true, length: { min: 3 }
|
284
|
-
required :age, numeric: { min: 13, max: 120 }
|
285
|
-
|
286
|
-
def call
|
287
|
-
# Process user
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
result = CreateUserTask.call(
|
292
|
-
email: "invalid-email",
|
293
|
-
username: "",
|
294
|
-
age: 5
|
295
|
-
)
|
296
|
-
|
297
|
-
result.state # → "interrupted"
|
298
|
-
result.status # → "failed"
|
299
|
-
result.failed? # → true
|
300
|
-
|
301
|
-
# Detailed error information
|
302
|
-
result.metadata
|
303
|
-
# {
|
304
|
-
# reason: "email must be valid. username can't be blank. username length must be at least 3. age must be at least 13.",
|
305
|
-
# messages: {
|
306
|
-
# email: ["must be valid"],
|
307
|
-
# username: ["can't be blank", "length must be at least 3"],
|
308
|
-
# age: ["must be at least 13"]
|
309
|
-
# }
|
310
|
-
# }
|
311
|
-
|
312
|
-
# Access specific parameter errors
|
313
|
-
result.metadata[:messages][:email] # → ["must be valid"]
|
314
|
-
result.metadata[:messages][:username] # → ["can't be blank", "length must be at least 3"]
|
315
|
-
```
|
316
|
-
|
317
|
-
### Nested Parameter Validation
|
318
|
-
|
319
|
-
```ruby
|
320
|
-
class ProcessOrderTask < CMDx::Task
|
321
|
-
required :order, type: :hash do
|
322
|
-
required :customer_email, format: { with: /@/ }
|
323
|
-
required :items, type: :array, length: { min: 1 }
|
324
|
-
|
325
|
-
optional :shipping, type: :hash do
|
326
|
-
required :method, inclusion: { in: %w[standard express] }
|
327
|
-
required :address, presence: true
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
def call
|
332
|
-
# Process validated order
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
# Nested validation errors
|
337
|
-
result = ProcessOrderTask.call(
|
338
|
-
order: {
|
339
|
-
customer_email: "invalid",
|
340
|
-
items: [],
|
341
|
-
shipping: {
|
342
|
-
method: "invalid",
|
343
|
-
address: ""
|
344
|
-
}
|
345
|
-
}
|
346
|
-
)
|
347
|
-
|
348
|
-
result.metadata[:messages]
|
349
|
-
# {
|
350
|
-
# "order.customer_email" => ["is invalid"],
|
351
|
-
# "order.items" => ["length must be at least 1"],
|
352
|
-
# "order.shipping.method" => ["must be one of: standard, express"],
|
353
|
-
# "order.shipping.address" => ["can't be blank"]
|
354
|
-
# }
|
355
|
-
```
|
356
|
-
|
357
|
-
## Conditional Validation
|
358
|
-
|
359
|
-
> [!TIP]
|
360
|
-
> Use `:if` and `:unless` options to apply validations conditionally based on runtime context or other parameter values.
|
361
|
-
|
362
|
-
```ruby
|
363
|
-
class UserRegistrationTask < CMDx::Task
|
364
|
-
required :email, presence: true, format: { with: /@/ }
|
365
|
-
required :user_type, inclusion: { in: %w[individual business] }
|
366
|
-
|
367
|
-
# Conditional validations based on user type
|
368
|
-
optional :company_name, presence: { if: :business_user? }
|
369
|
-
optional :tax_id, format: { with: /\A\d{2}-\d{7}\z/, if: :business_user? }
|
370
|
-
|
371
|
-
# Conditional validation with procs
|
372
|
-
optional :phone, presence: {
|
373
|
-
if: proc { |task| task.context.require_phone_verification? }
|
374
|
-
}
|
375
|
-
|
376
|
-
# Multiple conditions
|
377
|
-
optional :parent_email, presence: {
|
378
|
-
if: :minor_user?,
|
379
|
-
format: { with: /@/, unless: :parent_present? }
|
380
|
-
}
|
381
|
-
|
382
|
-
def call
|
383
|
-
create_user_account
|
384
|
-
end
|
385
|
-
|
386
|
-
private
|
387
|
-
|
388
|
-
def business_user?
|
389
|
-
user_type == "business"
|
390
|
-
end
|
391
|
-
|
392
|
-
def minor_user?
|
393
|
-
context.user_age < 18
|
394
|
-
end
|
395
|
-
|
396
|
-
def parent_present?
|
397
|
-
context.parent_guardian_present?
|
398
|
-
end
|
399
|
-
end
|
400
|
-
```
|
401
|
-
|
402
|
-
---
|
403
|
-
|
404
|
-
- **Prev:** [Parameters - Coercions](coercions.md)
|
405
|
-
- **Next:** [Parameters - Defaults](defaults.md)
|