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