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,52 +1,434 @@
|
|
1
1
|
# Parameters - Coercions
|
2
2
|
|
3
|
-
Parameter
|
4
|
-
|
3
|
+
Parameter coercions provide automatic type conversion for task arguments, enabling
|
4
|
+
flexible input handling while ensuring type safety within task execution. Coercions
|
5
|
+
transform raw input values into expected types, supporting everything from simple
|
6
|
+
string-to-integer conversion to complex JSON parsing and custom type handling.
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
+
## Table of Contents
|
9
|
+
|
10
|
+
- [TLDR](#tldr)
|
11
|
+
- [Coercion Fundamentals](#coercion-fundamentals)
|
12
|
+
- [Available Coercion Types](#available-coercion-types)
|
13
|
+
- [Basic Type Coercion](#basic-type-coercion)
|
14
|
+
- [Multiple Type Coercion](#multiple-type-coercion)
|
15
|
+
- [Advanced Coercion Examples](#advanced-coercion-examples)
|
16
|
+
- [Array Coercion](#array-coercion)
|
17
|
+
- [Hash Coercion](#hash-coercion)
|
18
|
+
- [Boolean Coercion](#boolean-coercion)
|
19
|
+
- [Date and Time Coercion](#date-and-time-coercion)
|
20
|
+
- [Numeric Coercion](#numeric-coercion)
|
21
|
+
- [Coercion with Nested Parameters](#coercion-with-nested-parameters)
|
22
|
+
- [Coercion Error Handling](#coercion-error-handling)
|
23
|
+
- [Single Type Coercion Errors](#single-type-coercion-errors)
|
24
|
+
- [Multiple Type Coercion Errors](#multiple-type-coercion-errors)
|
25
|
+
- [Custom Coercion Options](#custom-coercion-options)
|
26
|
+
- [Date/Time Format Options](#datetime-format-options)
|
27
|
+
- [BigDecimal Precision Options](#bigdecimal-precision-options)
|
28
|
+
|
29
|
+
## TLDR
|
30
|
+
|
31
|
+
- **Type coercion** - Automatic conversion using `type:` option (`:integer`, `:boolean`, `:array`, `:hash`, etc.)
|
32
|
+
- **Multiple types** - Fallback with `type: [:float, :integer]` - tries each until one succeeds
|
33
|
+
- **No conversion** - Default `:virtual` type returns values unchanged
|
34
|
+
- **Before validation** - Coercion happens automatically before parameter validation
|
35
|
+
- **Rich types** - Supports all Ruby built-ins plus JSON parsing for arrays/hashes
|
36
|
+
|
37
|
+
## Coercion Fundamentals
|
38
|
+
|
39
|
+
> [!NOTE]
|
40
|
+
> By default, parameters use the `:virtual` type which returns values unchanged. Type coercion is specified using the `:type` option and occurs automatically during parameter value resolution, before validation.
|
41
|
+
|
42
|
+
### Available Coercion Types
|
43
|
+
|
44
|
+
CMDx supports comprehensive type coercion for Ruby's built-in types:
|
45
|
+
|
46
|
+
| Type | Description | Example Input | Example Output |
|
47
|
+
|------|-------------|---------------|----------------|
|
48
|
+
| `:array` | Converts to Array, handles JSON strings | `"[1,2,3]"` | `[1, 2, 3]` |
|
49
|
+
| `:big_decimal` | High-precision decimal arithmetic | `"123.456"` | `BigDecimal("123.456")` |
|
50
|
+
| `:boolean` | True/false conversion with text patterns | `"true"`, `"yes"`, `"1"` | `true` |
|
51
|
+
| `:complex` | Complex number conversion | `"1+2i"` | `Complex(1, 2)` |
|
52
|
+
| `:date` | Date object conversion | `"2023-12-25"` | `Date.new(2023, 12, 25)` |
|
53
|
+
| `:datetime` | DateTime object conversion | `"2023-12-25 10:30"` | `DateTime` object |
|
54
|
+
| `:float` | Floating-point number conversion | `"123.45"` | `123.45` |
|
55
|
+
| `:hash` | Hash conversion, handles JSON strings | `'{"a":1}'` | `{"a" => 1}` |
|
56
|
+
| `:integer` | Integer conversion, handles various formats | `"123"`, `"0xFF"` | `123`, `255` |
|
57
|
+
| `:rational` | Rational number conversion | `"1/2"`, `0.5` | `Rational(1, 2)` |
|
58
|
+
| `:string` | String conversion for any object | `123`, `:symbol` | `"123"`, `"symbol"` |
|
59
|
+
| `:time` | Time object conversion | `"2023-12-25 10:30"` | `Time` object |
|
60
|
+
| `:virtual` | No conversion (default) | `anything` | `anything` |
|
61
|
+
|
62
|
+
### Basic Type Coercion
|
8
63
|
|
9
64
|
```ruby
|
10
|
-
class
|
65
|
+
class ProcessUserDataTask < CMDx::Task
|
11
66
|
|
12
|
-
|
13
|
-
required :
|
67
|
+
required :user_id, type: :integer
|
68
|
+
required :order_total, type: :float
|
69
|
+
required :is_premium, type: :boolean
|
70
|
+
required :notes, type: :string
|
14
71
|
|
15
|
-
|
16
|
-
optional :
|
72
|
+
optional :product_tags, type: :array, default: []
|
73
|
+
optional :preferences, type: :hash, default: {}
|
74
|
+
optional :created_at, type: :datetime
|
75
|
+
optional :delivery_date, type: :date
|
17
76
|
|
18
77
|
def call
|
19
|
-
|
20
|
-
|
78
|
+
user_id #=> 12345 (integer from "12345")
|
79
|
+
order_total #=> 299.99 (float from "299.99")
|
80
|
+
is_premium #=> true (boolean from "true")
|
81
|
+
notes #=> "Rush delivery" (string)
|
82
|
+
product_tags #=> ["electronics", "phone"] (array from JSON)
|
83
|
+
preferences #=> {"notifications" => true} (hash from JSON)
|
84
|
+
created_at #=> DateTime object
|
85
|
+
delivery_date #=> Date object
|
21
86
|
end
|
22
87
|
|
23
88
|
end
|
24
89
|
|
25
|
-
#
|
26
|
-
|
90
|
+
# Coercion happens automatically
|
91
|
+
ProcessUserDataTask.call(
|
92
|
+
user_id: "12345",
|
93
|
+
order_total: "299.99",
|
94
|
+
is_premium: "yes",
|
95
|
+
notes: 67890,
|
96
|
+
product_tags: "[\"electronics\",\"phone\"]",
|
97
|
+
preferences: '{"notifications":true}',
|
98
|
+
created_at: "2023-12-25 14:30:00",
|
99
|
+
delivery_date: "2023-12-28"
|
100
|
+
)
|
27
101
|
```
|
28
102
|
|
29
|
-
|
30
|
-
|
31
|
-
>
|
103
|
+
## Multiple Type Coercion
|
104
|
+
|
105
|
+
> [!TIP]
|
106
|
+
> Parameters can specify multiple types for fallback coercion, attempting each type in order until one succeeds. This provides flexible input handling while maintaining type safety.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class ProcessOrderDataTask < CMDx::Task
|
110
|
+
|
111
|
+
# Try float first for precise calculations, fall back to integer
|
112
|
+
required :amount, type: [:float, :integer]
|
113
|
+
|
114
|
+
# Try hash first for structured data, fall back to string for raw data
|
115
|
+
optional :shipping_info, type: [:hash, :string]
|
116
|
+
|
117
|
+
# Complex fallback for timestamps
|
118
|
+
optional :scheduled_at, type: [:datetime, :date, :string]
|
119
|
+
|
120
|
+
def call
|
121
|
+
amount #=> 149.99 (float) or 150 (integer) depending on input
|
122
|
+
shipping_info #=> {"address" => "123 Main St"} (hash) or "Express shipping" (string)
|
123
|
+
scheduled_at #=> DateTime, Date, or String depending on input format
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
# Different inputs produce different coerced types
|
129
|
+
ProcessOrderDataTask.call(amount: "149.99") # => 149.99 (float)
|
130
|
+
ProcessOrderDataTask.call(amount: "150") # => 150 (integer)
|
131
|
+
ProcessOrderDataTask.call(shipping_info: '{"address":"123 Main St"}') # => hash
|
132
|
+
ProcessOrderDataTask.call(shipping_info: "Express shipping") # => string
|
133
|
+
```
|
134
|
+
|
135
|
+
## Advanced Coercion Examples
|
136
|
+
|
137
|
+
### Array Coercion
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class ProcessOrderItemsTask < CMDx::Task
|
141
|
+
|
142
|
+
required :item_ids, type: :array
|
143
|
+
optional :quantities, type: :array, default: []
|
144
|
+
|
145
|
+
def call
|
146
|
+
item_ids #=> Array of product IDs
|
147
|
+
quantities #=> Array of quantities or empty array
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
# Array coercion handles multiple input formats
|
153
|
+
ProcessOrderItemsTask.call(item_ids: [101, 102, 103]) # => already array
|
154
|
+
ProcessOrderItemsTask.call(item_ids: "[101,102,103]") # => from JSON string
|
155
|
+
ProcessOrderItemsTask.call(item_ids: "101") # => ["101"] (wrapped)
|
156
|
+
ProcessOrderItemsTask.call(item_ids: nil) # => [] (nil to empty)
|
157
|
+
```
|
32
158
|
|
33
|
-
|
159
|
+
### Hash Coercion
|
34
160
|
|
35
|
-
|
161
|
+
```ruby
|
162
|
+
class ProcessOrderConfigTask < CMDx::Task
|
163
|
+
|
164
|
+
required :shipping_config, type: :hash
|
165
|
+
optional :payment_options, type: :hash, default: {}
|
166
|
+
|
167
|
+
def call
|
168
|
+
shipping_config #=> Hash with shipping configuration
|
169
|
+
payment_options #=> Hash with payment options or empty hash
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
# Hash coercion supports multiple formats
|
175
|
+
ProcessOrderConfigTask.call(shipping_config: {carrier: "UPS", speed: "express"})
|
176
|
+
ProcessOrderConfigTask.call(shipping_config: '{"carrier":"UPS","speed":"express"}')
|
177
|
+
ProcessOrderConfigTask.call(shipping_config: [:carrier, "UPS", :speed, "express"])
|
178
|
+
```
|
179
|
+
|
180
|
+
### Boolean Coercion
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
class ValidateUserSettingsTask < CMDx::Task
|
184
|
+
|
185
|
+
required :email_notifications, type: :boolean
|
186
|
+
required :is_active, type: :boolean
|
187
|
+
optional :marketing_consent, type: :boolean, default: false
|
188
|
+
|
189
|
+
def call
|
190
|
+
email_notifications #=> true or false from various inputs
|
191
|
+
is_active #=> true or false
|
192
|
+
marketing_consent #=> true or false with default
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
# Boolean coercion recognizes many text patterns
|
198
|
+
ValidateUserSettingsTask.call(email_notifications: "true") # => true
|
199
|
+
ValidateUserSettingsTask.call(email_notifications: "yes") # => true
|
200
|
+
ValidateUserSettingsTask.call(email_notifications: "1") # => true
|
201
|
+
ValidateUserSettingsTask.call(email_notifications: "false") # => false
|
202
|
+
ValidateUserSettingsTask.call(email_notifications: "no") # => false
|
203
|
+
ValidateUserSettingsTask.call(email_notifications: "0") # => false
|
204
|
+
```
|
205
|
+
|
206
|
+
### Date and Time Coercion
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
class ProcessOrderScheduleTask < CMDx::Task
|
210
|
+
|
211
|
+
required :order_date, type: :date
|
212
|
+
required :created_at, type: :datetime
|
213
|
+
optional :updated_at, type: :time
|
214
|
+
|
215
|
+
# Custom format options for specific date/time formats
|
216
|
+
optional :delivery_date, type: :date, format: "%Y-%m-%d"
|
217
|
+
optional :pickup_time, type: :time, format: "%H:%M:%S"
|
218
|
+
|
219
|
+
def call
|
220
|
+
order_date #=> Date object
|
221
|
+
created_at #=> DateTime object
|
222
|
+
updated_at #=> Time object
|
223
|
+
delivery_date #=> Date parsed with custom format
|
224
|
+
pickup_time #=> Time parsed with custom format
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
ProcessOrderScheduleTask.call(
|
230
|
+
order_date: "2023-12-25",
|
231
|
+
created_at: "2023-12-25 10:30:00",
|
232
|
+
updated_at: "2023-12-25 10:30:00",
|
233
|
+
delivery_date: "2023-12-28",
|
234
|
+
pickup_time: "14:30:00"
|
235
|
+
)
|
236
|
+
```
|
237
|
+
|
238
|
+
### Numeric Coercion
|
36
239
|
|
37
240
|
```ruby
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
241
|
+
class CalculateOrderTotalsTask < CMDx::Task
|
242
|
+
|
243
|
+
required :item_count, type: :integer
|
244
|
+
required :subtotal, type: :float
|
245
|
+
required :tax_rate, type: :float
|
246
|
+
|
247
|
+
# High-precision for financial calculations
|
248
|
+
optional :discount_amount, type: :big_decimal, precision: 4
|
249
|
+
|
250
|
+
# For specialized calculations
|
251
|
+
optional :shipping_ratio, type: :rational
|
252
|
+
optional :complex_calculation, type: :complex
|
253
|
+
|
254
|
+
def call
|
255
|
+
item_count #=> Integer from various formats
|
256
|
+
subtotal #=> Float for currency
|
257
|
+
tax_rate #=> Float for percentage
|
258
|
+
discount_amount #=> BigDecimal with specified precision
|
259
|
+
shipping_ratio #=> Rational number
|
260
|
+
complex_calculation #=> Complex number
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
CalculateOrderTotalsTask.call(
|
266
|
+
item_count: "5",
|
267
|
+
subtotal: "249.99",
|
268
|
+
tax_rate: "0.0875",
|
269
|
+
discount_amount: "25.0000",
|
270
|
+
shipping_ratio: "1/10",
|
271
|
+
complex_calculation: "1+2i"
|
272
|
+
)
|
273
|
+
```
|
274
|
+
|
275
|
+
## Coercion with Nested Parameters
|
276
|
+
|
277
|
+
> [!IMPORTANT]
|
278
|
+
> Coercion works seamlessly with nested parameter structures, applying type conversion at each level of the hierarchy.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
class ProcessOrderDetailsTask < CMDx::Task
|
282
|
+
|
283
|
+
required :order, type: :hash do
|
284
|
+
required :id, type: :integer
|
285
|
+
required :total, type: :float
|
286
|
+
required :items, type: :array
|
287
|
+
|
288
|
+
optional :customer, type: :hash do
|
289
|
+
required :id, type: :integer
|
290
|
+
required :is_active, type: :boolean
|
291
|
+
optional :created_at, type: :datetime
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def call
|
296
|
+
order #=> Hash (coerced from JSON string if needed)
|
297
|
+
|
298
|
+
# Nested coercions
|
299
|
+
id #=> Integer (from order.id)
|
300
|
+
total #=> Float (from order.total)
|
301
|
+
items #=> Array (from order.items)
|
302
|
+
|
303
|
+
# Deep nested coercions
|
304
|
+
if customer
|
305
|
+
customer_id = id # Integer (from order.customer.id)
|
306
|
+
active_status = is_active # Boolean (from order.customer.is_active)
|
307
|
+
created_time = created_at # DateTime (from order.customer.created_at)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
## Coercion Error Handling
|
315
|
+
|
316
|
+
> [!WARNING]
|
317
|
+
> When coercion fails, CMDx provides detailed error information including the parameter name, attempted types, and specific failure reasons.
|
318
|
+
|
319
|
+
### Single Type Coercion Errors
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
class ValidateUserProfileTask < CMDx::Task
|
323
|
+
|
324
|
+
required :age, type: :integer
|
325
|
+
required :salary, type: :float
|
326
|
+
required :is_employed, type: :boolean
|
327
|
+
|
328
|
+
def call
|
329
|
+
# Task logic here
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
# Invalid coercion inputs
|
335
|
+
result = ValidateUserProfileTask.call(
|
336
|
+
age: "not-a-number",
|
337
|
+
salary: "invalid-amount",
|
338
|
+
is_employed: "maybe"
|
339
|
+
)
|
340
|
+
|
341
|
+
result.failed? #=> true
|
342
|
+
result.metadata
|
343
|
+
#=> {
|
344
|
+
# reason: "age could not coerce into an integer. salary could not coerce into a float. is_employed could not coerce into a boolean.",
|
345
|
+
# messages: {
|
346
|
+
# age: ["could not coerce into an integer"],
|
347
|
+
# salary: ["could not coerce into a float"],
|
348
|
+
# is_employed: ["could not coerce into a boolean"]
|
349
|
+
# }
|
350
|
+
# }
|
351
|
+
```
|
352
|
+
|
353
|
+
### Multiple Type Coercion Errors
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
class ProcessFlexibleDataTask < CMDx::Task
|
357
|
+
|
358
|
+
required :order_value, type: [:float, :integer]
|
359
|
+
required :customer_data, type: [:hash, :array, :string]
|
360
|
+
|
361
|
+
def call
|
362
|
+
# Task logic here
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
# Failed coercion with multiple types
|
368
|
+
result = ProcessFlexibleDataTask.call(
|
369
|
+
order_value: "invalid-number",
|
370
|
+
customer_data: Object.new
|
371
|
+
)
|
372
|
+
|
373
|
+
result.failed? #=> true
|
374
|
+
result.metadata
|
375
|
+
#=> {
|
376
|
+
# reason: "order_value could not coerce into one of: float, integer. customer_data could not coerce into one of: hash, array, string.",
|
377
|
+
# messages: {
|
378
|
+
# order_value: ["could not coerce into one of: float, integer"],
|
379
|
+
# customer_data: ["could not coerce into one of: hash, array, string"]
|
380
|
+
# }
|
381
|
+
# }
|
382
|
+
```
|
383
|
+
|
384
|
+
## Custom Coercion Options
|
385
|
+
|
386
|
+
### Date/Time Format Options
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
class ProcessCustomDateTask < CMDx::Task
|
390
|
+
|
391
|
+
# US date format
|
392
|
+
required :birth_date, type: :date, format: "%m/%d/%Y"
|
393
|
+
|
394
|
+
# ISO datetime with timezone
|
395
|
+
required :event_timestamp, type: :datetime, format: "%Y-%m-%d %H:%M:%S %Z"
|
396
|
+
|
397
|
+
# 24-hour time format
|
398
|
+
optional :meeting_time, type: :time, format: "%H:%M"
|
399
|
+
|
400
|
+
def call
|
401
|
+
birth_date #=> Date parsed with MM/DD/YYYY format
|
402
|
+
event_timestamp #=> DateTime with timezone
|
403
|
+
meeting_time #=> Time with hour:minute format
|
404
|
+
end
|
405
|
+
|
406
|
+
end
|
407
|
+
|
408
|
+
ProcessCustomDateTask.call(
|
409
|
+
birth_date: "12/25/1990",
|
410
|
+
event_timestamp: "2023-12-25 10:30:00 UTC",
|
411
|
+
meeting_time: "14:30"
|
412
|
+
)
|
413
|
+
```
|
414
|
+
|
415
|
+
### BigDecimal Precision Options
|
416
|
+
|
417
|
+
```ruby
|
418
|
+
class CalculatePricingTask < CMDx::Task
|
419
|
+
|
420
|
+
required :base_price, type: :big_decimal
|
421
|
+
required :tax_rate, type: :big_decimal, precision: 6
|
422
|
+
|
423
|
+
def call
|
424
|
+
base_price #=> BigDecimal with default precision
|
425
|
+
tax_rate #=> BigDecimal with 6-digit precision
|
426
|
+
end
|
427
|
+
|
428
|
+
end
|
47
429
|
```
|
48
430
|
|
49
431
|
---
|
50
432
|
|
51
|
-
- **Prev:** [Namespacing](
|
52
|
-
- **Next:** [Validations](
|
433
|
+
- **Prev:** [Parameters - Namespacing](namespacing.md)
|
434
|
+
- **Next:** [Parameters - Validations](validations.md)
|