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.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.cursor/rules/cursor-instructions.mdc +6 -0
- data/.rubocop.yml +19 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +95 -28
- data/README.md +73 -25
- data/docs/ai_prompts.md +319 -0
- data/docs/basics/call.md +234 -14
- data/docs/basics/chain.md +280 -0
- data/docs/basics/context.md +241 -33
- data/docs/basics/setup.md +85 -12
- data/docs/callbacks.md +283 -0
- data/docs/configuration.md +155 -30
- data/docs/getting_started.md +145 -22
- data/docs/internationalization.md +148 -0
- data/docs/interruptions/exceptions.md +198 -11
- data/docs/interruptions/faults.md +196 -44
- data/docs/interruptions/halt.md +188 -35
- data/docs/logging.md +204 -53
- data/docs/middlewares.md +745 -0
- data/docs/outcomes/result.md +305 -10
- data/docs/outcomes/states.md +212 -31
- data/docs/outcomes/statuses.md +284 -30
- data/docs/parameters/coercions.md +411 -29
- data/docs/parameters/defaults.md +258 -25
- data/docs/parameters/definitions.md +247 -72
- data/docs/parameters/namespacing.md +259 -27
- data/docs/parameters/validations.md +173 -168
- data/docs/testing.md +560 -0
- data/docs/tips_and_tricks.md +103 -42
- data/docs/workflows.md +329 -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 +367 -25
- 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 +405 -37
- 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 +43 -15
- data/lib/generators/cmdx/workflow_generator.rb +109 -0
- data/lib/locales/ar.yml +36 -0
- data/lib/locales/cs.yml +36 -0
- data/lib/locales/da.yml +36 -0
- data/lib/locales/de.yml +36 -0
- data/lib/locales/el.yml +36 -0
- data/lib/locales/en.yml +20 -20
- data/lib/locales/es.yml +20 -20
- data/lib/locales/fi.yml +36 -0
- data/lib/locales/fr.yml +36 -0
- data/lib/locales/he.yml +36 -0
- data/lib/locales/hi.yml +36 -0
- data/lib/locales/it.yml +36 -0
- data/lib/locales/ja.yml +36 -0
- data/lib/locales/ko.yml +36 -0
- data/lib/locales/nl.yml +36 -0
- data/lib/locales/no.yml +36 -0
- data/lib/locales/pl.yml +36 -0
- data/lib/locales/pt.yml +36 -0
- data/lib/locales/ru.yml +36 -0
- data/lib/locales/sv.yml +36 -0
- data/lib/locales/th.yml +36 -0
- data/lib/locales/tr.yml +36 -0
- data/lib/locales/vi.yml +36 -0
- data/lib/locales/zh.yml +36 -0
- metadata +77 -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 -62
- data/lib/cmdx/batch.rb +0 -43
- data/lib/cmdx/parameters.rb +0 -35
- data/lib/cmdx/run.rb +0 -39
- data/lib/cmdx/run_inspector.rb +0 -26
- data/lib/cmdx/run_serializer.rb +0 -20
- 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,317 @@
|
|
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
|
+
- [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` |
|
16
|
-
| `:unless` |
|
17
|
-
| `:message` |
|
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
|
21
|
-
> call arguments.
|
38
|
+
> Validators on `optional` parameters only execute when arguments are supplied.
|
22
39
|
|
23
40
|
## Presence
|
24
41
|
|
25
|
-
Validates that
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
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
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
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
|
-
|
88
|
+
**Options:**
|
67
89
|
|
68
90
|
| Option | Description |
|
69
91
|
| ---------- | ----------- |
|
70
|
-
| `:with` | Regular expression that
|
71
|
-
| `:without` | Regular expression that
|
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
|
97
|
+
Validates that parameter values are not in a specific enumerable (array, range, etc.).
|
76
98
|
|
77
99
|
```ruby
|
78
|
-
class
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
106
|
+
charge_payment
|
88
107
|
end
|
89
|
-
|
90
108
|
end
|
91
109
|
```
|
92
110
|
|
93
|
-
|
111
|
+
**Options:**
|
94
112
|
|
95
113
|
| Option | Description |
|
96
114
|
| ------------ | ----------- |
|
97
|
-
| `:in` |
|
98
|
-
| `:within` |
|
115
|
+
| `:in` | Enumerable of forbidden values |
|
116
|
+
| `:within` | Alias for `:in` |
|
99
117
|
|
100
|
-
|
118
|
+
**Error Messages:**
|
101
119
|
|
102
120
|
| Option | Description |
|
103
121
|
| ----------------- | ----------- |
|
104
|
-
| `:of_message` |
|
105
|
-
| `:in_message` |
|
106
|
-
| `:within_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
|
128
|
+
Validates that parameter values are in a specific enumerable (array, range, etc.).
|
111
129
|
|
112
130
|
```ruby
|
113
|
-
class
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
140
|
+
update_order_status
|
123
141
|
end
|
124
142
|
|
125
143
|
private
|
126
144
|
|
127
|
-
def
|
128
|
-
|
145
|
+
def digital_order?
|
146
|
+
context.order.digital_items_only?
|
129
147
|
end
|
130
|
-
|
131
148
|
end
|
132
149
|
```
|
133
150
|
|
134
|
-
|
151
|
+
**Options:**
|
135
152
|
|
136
153
|
| Option | Description |
|
137
154
|
| ------------ | ----------- |
|
138
|
-
| `:in` |
|
139
|
-
| `:within` |
|
155
|
+
| `:in` | Enumerable of allowed values |
|
156
|
+
| `:within` | Alias for `:in` |
|
140
157
|
|
141
|
-
|
158
|
+
**Error Messages:**
|
142
159
|
|
143
160
|
| Option | Description |
|
144
161
|
| ----------------- | ----------- |
|
145
|
-
| `:of_message` |
|
146
|
-
| `:in_message` |
|
147
|
-
| `:within_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
|
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
|
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 }
|
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
|
-
|
179
|
+
create_blog_post
|
173
180
|
end
|
174
|
-
|
175
181
|
end
|
176
182
|
```
|
177
183
|
|
178
|
-
|
184
|
+
**Options:**
|
179
185
|
|
180
186
|
| Option | Description |
|
181
187
|
| ------------- | ----------- |
|
182
|
-
| `:within` |
|
183
|
-
| `:not_within` |
|
184
|
-
| `:in` |
|
185
|
-
| `:not_in` |
|
186
|
-
| `:min` |
|
187
|
-
| `:max` |
|
188
|
-
| `:is` |
|
189
|
-
| `:is_not` |
|
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
|
-
|
197
|
+
**Error Messages:**
|
192
198
|
|
193
199
|
| Option | Description |
|
194
200
|
| --------------------- | ----------- |
|
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}") |
|
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
|
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
|
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 }
|
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
|
-
|
221
|
+
calculate_order_total
|
228
222
|
end
|
229
|
-
|
230
223
|
end
|
231
224
|
```
|
232
225
|
|
233
|
-
|
226
|
+
**Options:**
|
234
227
|
|
235
228
|
| Option | Description |
|
236
229
|
| ------------- | ----------- |
|
237
|
-
| `:within` |
|
238
|
-
| `:not_within` |
|
239
|
-
| `:in` |
|
240
|
-
| `:not_in` |
|
241
|
-
| `:min` |
|
242
|
-
| `:max` |
|
243
|
-
| `:is` |
|
244
|
-
| `:is_not` |
|
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
|
-
|
239
|
+
**Error Messages:**
|
247
240
|
|
248
241
|
| Option | Description |
|
249
242
|
| --------------------- | ----------- |
|
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}") |
|
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
|
-
|
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
|
255
|
+
class EmailDomainValidator
|
265
256
|
def self.call(value, options)
|
266
|
-
|
267
|
-
value.
|
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
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
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
|
-
|
276
|
+
create_user_account
|
281
277
|
end
|
282
|
-
|
283
278
|
end
|
284
279
|
```
|
285
280
|
|
286
|
-
|
281
|
+
**Options:**
|
287
282
|
|
288
283
|
| Option | Description |
|
289
284
|
| ------------ | ----------- |
|
290
|
-
| `:validator` | Callable
|
285
|
+
| `:validator` | Callable object returning true/false. Receives value and options as parameters |
|
291
286
|
|
292
|
-
## Results
|
287
|
+
## Validation Results
|
293
288
|
|
294
|
-
|
289
|
+
When validation fails, tasks enter a failed state with detailed error information:
|
295
290
|
|
296
291
|
```ruby
|
297
|
-
|
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
|
302
|
+
#=> reason: "email format is invalid. username cannot be empty.",
|
302
303
|
#=> messages: {
|
303
|
-
#=> email: ["format is
|
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](
|
312
|
-
- **Next:** [Defaults](
|
316
|
+
- **Prev:** [Parameters - Coercions](coercions.md)
|
317
|
+
- **Next:** [Parameters - Defaults](defaults.md)
|