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,396 +0,0 @@
|
|
1
|
-
# Parameters - Coercions
|
2
|
-
|
3
|
-
Parameter coercions provide automatic type conversion for task arguments, enabling flexible input handling while ensuring type safety. Coercions transform raw input values into expected types, supporting everything from simple string-to-integer conversion to complex JSON parsing and custom type handling.
|
4
|
-
|
5
|
-
## Table of Contents
|
6
|
-
|
7
|
-
- [TLDR](#tldr)
|
8
|
-
- [Coercion Fundamentals](#coercion-fundamentals)
|
9
|
-
- [Multiple Type Coercion](#multiple-type-coercion)
|
10
|
-
- [Advanced Examples](#advanced-examples)
|
11
|
-
- [Coercion with Nested Parameters](#coercion-with-nested-parameters)
|
12
|
-
- [Error Handling](#error-handling)
|
13
|
-
- [Custom Coercion Options](#custom-coercion-options)
|
14
|
-
- [Custom Coercions](#custom-coercions)
|
15
|
-
|
16
|
-
## TLDR
|
17
|
-
|
18
|
-
```ruby
|
19
|
-
# Basic type coercion
|
20
|
-
required :user_id, type: :integer # "123" → 123
|
21
|
-
required :active, type: :boolean # "true" → true
|
22
|
-
required :tags, type: :array # "[1,2,3]" → [1, 2, 3]
|
23
|
-
|
24
|
-
# Multiple type fallback
|
25
|
-
required :amount, type: [:float, :integer] # Tries float, then integer
|
26
|
-
|
27
|
-
# Custom formats
|
28
|
-
required :created_at, type: :date, format: "%Y-%m-%d"
|
29
|
-
|
30
|
-
# No conversion (default)
|
31
|
-
required :raw_data, type: :virtual # Returns unchanged
|
32
|
-
```
|
33
|
-
|
34
|
-
## Coercion Fundamentals
|
35
|
-
|
36
|
-
> [!NOTE]
|
37
|
-
> Parameters use `:virtual` type by default (no conversion). Coercion occurs automatically during parameter resolution, before validation.
|
38
|
-
|
39
|
-
### Available Types
|
40
|
-
|
41
|
-
| Type | Description | Example |
|
42
|
-
|------|-------------|---------|
|
43
|
-
| `:array` | Array conversion, handles JSON | `"[1,2,3]"` → `[1, 2, 3]` |
|
44
|
-
| `:big_decimal` | High-precision decimal | `"123.45"` → `BigDecimal("123.45")` |
|
45
|
-
| `:boolean` | True/false with text patterns | `"yes"` → `true` |
|
46
|
-
| `:complex` | Complex numbers | `"1+2i"` → `Complex(1, 2)` |
|
47
|
-
| `:date` | Date objects | `"2023-12-25"` → `Date` |
|
48
|
-
| `:datetime` | DateTime objects | `"2023-12-25 10:30"` → `DateTime` |
|
49
|
-
| `:float` | Floating-point | `"123.45"` → `123.45` |
|
50
|
-
| `:hash` | Hash conversion, handles JSON | `'{"a":1}'` → `{"a" => 1}` |
|
51
|
-
| `:integer` | Integer, handles hex/octal | `"0xFF"` → `255` |
|
52
|
-
| `:rational` | Rational numbers | `"1/2"` → `Rational(1, 2)` |
|
53
|
-
| `:string` | String conversion | `123` → `"123"` |
|
54
|
-
| `:time` | Time objects | `"10:30:00"` → `Time` |
|
55
|
-
| `:virtual` | No conversion (default) | Input unchanged |
|
56
|
-
|
57
|
-
### Basic Usage
|
58
|
-
|
59
|
-
```ruby
|
60
|
-
class ProcessPaymentTask < CMDx::Task
|
61
|
-
required :amount, type: :float
|
62
|
-
required :user_id, type: :integer
|
63
|
-
required :send_email, type: :boolean
|
64
|
-
|
65
|
-
optional :metadata, type: :hash, default: {}
|
66
|
-
optional :tags, type: :array, default: []
|
67
|
-
|
68
|
-
def call
|
69
|
-
# All parameters automatically coerced
|
70
|
-
charge_amount = amount * 100 # Float math
|
71
|
-
user = User.find(user_id) # Integer lookup
|
72
|
-
|
73
|
-
send_notification if send_email # Boolean logic
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Usage with string inputs
|
78
|
-
ProcessPaymentTask.call(
|
79
|
-
amount: "99.99", # → 99.99 (Float)
|
80
|
-
user_id: "12345", # → 12345 (Integer)
|
81
|
-
send_email: "true", # → true (Boolean)
|
82
|
-
metadata: '{"source":"web"}', # → {"source" => "web"} (Hash)
|
83
|
-
tags: "[\"priority\"]" # → ["priority"] (Array)
|
84
|
-
)
|
85
|
-
```
|
86
|
-
|
87
|
-
## Multiple Type Coercion
|
88
|
-
|
89
|
-
> [!TIP]
|
90
|
-
> Specify multiple types for fallback coercion. CMDx attempts each type in order until one succeeds.
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
class ProcessOrderTask < CMDx::Task
|
94
|
-
# Numeric: try precise float, fall back to integer
|
95
|
-
required :total, type: [:float, :integer]
|
96
|
-
|
97
|
-
# Data: try structured hash, fall back to raw string
|
98
|
-
optional :notes, type: [:hash, :string]
|
99
|
-
|
100
|
-
# Temporal: flexible date/time handling
|
101
|
-
optional :due_date, type: [:datetime, :date, :string]
|
102
|
-
|
103
|
-
def call
|
104
|
-
case total
|
105
|
-
when Float then process_precise_amount(total)
|
106
|
-
when Integer then process_rounded_amount(total)
|
107
|
-
end
|
108
|
-
|
109
|
-
case notes
|
110
|
-
when Hash then structured_notes = notes
|
111
|
-
when String then fallback_notes = notes
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Different inputs produce different types
|
117
|
-
ProcessOrderTask.call(total: "99.99") # → 99.99 (Float)
|
118
|
-
ProcessOrderTask.call(total: "100") # → 100 (Integer)
|
119
|
-
```
|
120
|
-
|
121
|
-
## Advanced Examples
|
122
|
-
|
123
|
-
### Array and Hash Coercion
|
124
|
-
|
125
|
-
```ruby
|
126
|
-
class ProcessInventoryTask < CMDx::Task
|
127
|
-
required :product_ids, type: :array
|
128
|
-
required :config, type: :hash
|
129
|
-
|
130
|
-
def call
|
131
|
-
products = Product.where(id: product_ids)
|
132
|
-
apply_configuration(config)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# Multiple input formats supported
|
137
|
-
ProcessInventoryTask.call(
|
138
|
-
product_ids: [1, 2, 3], # Already array
|
139
|
-
product_ids: "[1,2,3]", # JSON string
|
140
|
-
product_ids: "1", # Single value → ["1"]
|
141
|
-
|
142
|
-
config: {key: "value"}, # Already hash
|
143
|
-
config: '{"key":"value"}', # JSON string
|
144
|
-
config: [:key, "value"] # Array pairs → Hash
|
145
|
-
)
|
146
|
-
```
|
147
|
-
|
148
|
-
### Boolean Patterns
|
149
|
-
|
150
|
-
```ruby
|
151
|
-
class UpdateUserSettingsTask < CMDx::Task
|
152
|
-
required :notifications, type: :boolean
|
153
|
-
required :active, type: :boolean
|
154
|
-
|
155
|
-
def call
|
156
|
-
user.update!(
|
157
|
-
email_notifications: notifications,
|
158
|
-
account_active: active
|
159
|
-
)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Boolean coercion recognizes many patterns
|
164
|
-
UpdateUserSettingsTask.call(
|
165
|
-
notifications: "true", # → true
|
166
|
-
notifications: "yes", # → true
|
167
|
-
notifications: "1", # → true
|
168
|
-
notifications: "on", # → true
|
169
|
-
|
170
|
-
active: "false", # → false
|
171
|
-
active: "no", # → false
|
172
|
-
active: "0", # → false
|
173
|
-
active: "off" # → false
|
174
|
-
)
|
175
|
-
```
|
176
|
-
|
177
|
-
### Date and Time Handling
|
178
|
-
|
179
|
-
```ruby
|
180
|
-
class ScheduleEventTask < CMDx::Task
|
181
|
-
required :event_date, type: :date
|
182
|
-
required :start_time, type: :time
|
183
|
-
|
184
|
-
# Custom formats for specific inputs
|
185
|
-
optional :deadline, type: :date, format: "%m/%d/%Y"
|
186
|
-
optional :meeting_time, type: :time, format: "%I:%M %p"
|
187
|
-
|
188
|
-
def call
|
189
|
-
Event.create!(
|
190
|
-
scheduled_date: event_date,
|
191
|
-
start_time: start_time,
|
192
|
-
deadline: deadline,
|
193
|
-
meeting_time: meeting_time
|
194
|
-
)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
ScheduleEventTask.call(
|
199
|
-
event_date: "2023-12-25", # Standard ISO format
|
200
|
-
start_time: "14:30:00", # 24-hour format
|
201
|
-
deadline: "12/31/2023", # Custom MM/DD/YYYY format
|
202
|
-
meeting_time: "2:30 PM" # 12-hour with AM/PM
|
203
|
-
)
|
204
|
-
```
|
205
|
-
|
206
|
-
## Coercion with Nested Parameters
|
207
|
-
|
208
|
-
> [!IMPORTANT]
|
209
|
-
> Coercion applies at every level of nested parameter structures, enabling complex data transformation while maintaining type safety.
|
210
|
-
|
211
|
-
```ruby
|
212
|
-
class ProcessOrderTask < CMDx::Task
|
213
|
-
required :order, type: :hash do
|
214
|
-
required :id, type: :integer
|
215
|
-
required :total, type: :float
|
216
|
-
required :items, type: :array
|
217
|
-
|
218
|
-
optional :customer, type: :hash do
|
219
|
-
required :id, type: :integer
|
220
|
-
required :active, type: :boolean
|
221
|
-
optional :signup_date, type: :date
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def call
|
226
|
-
order_id = order[:id] # Integer (coerced)
|
227
|
-
total_amount = order[:total] # Float (coerced)
|
228
|
-
|
229
|
-
if order[:customer]
|
230
|
-
customer_id = order[:customer][:id] # Integer (coerced)
|
231
|
-
is_active = order[:customer][:active] # Boolean (coerced)
|
232
|
-
signup = order[:customer][:signup_date] # Date (coerced)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
# JSON input with automatic nested coercion
|
238
|
-
ProcessOrderTask.call(
|
239
|
-
order: '{
|
240
|
-
"id": "12345",
|
241
|
-
"total": "299.99",
|
242
|
-
"items": ["item1", "item2"],
|
243
|
-
"customer": {
|
244
|
-
"id": "67890",
|
245
|
-
"active": "true",
|
246
|
-
"signup_date": "2023-01-15"
|
247
|
-
}
|
248
|
-
}'
|
249
|
-
)
|
250
|
-
```
|
251
|
-
|
252
|
-
## Error Handling
|
253
|
-
|
254
|
-
> [!WARNING]
|
255
|
-
> Coercion failures provide detailed error information including parameter paths, attempted types, and specific failure reasons.
|
256
|
-
|
257
|
-
```ruby
|
258
|
-
class ProcessDataTask < CMDx::Task
|
259
|
-
required :count, type: :integer
|
260
|
-
required :amount, type: [:float, :big_decimal]
|
261
|
-
required :active, type: :boolean
|
262
|
-
|
263
|
-
def call
|
264
|
-
# Task logic
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
# Invalid inputs
|
269
|
-
result = ProcessDataTask.call(
|
270
|
-
count: "not-a-number",
|
271
|
-
amount: "invalid-float",
|
272
|
-
active: "maybe"
|
273
|
-
)
|
274
|
-
|
275
|
-
result.failed? # → true
|
276
|
-
result.metadata
|
277
|
-
# {
|
278
|
-
# reason: "count could not coerce into an integer. amount could not coerce into one of: float, big_decimal. active could not coerce into a boolean.",
|
279
|
-
# messages: {
|
280
|
-
# count: ["could not coerce into an integer"],
|
281
|
-
# amount: ["could not coerce into one of: float, big_decimal"],
|
282
|
-
# active: ["could not coerce into a boolean"]
|
283
|
-
# }
|
284
|
-
# }
|
285
|
-
```
|
286
|
-
|
287
|
-
### Common Error Scenarios
|
288
|
-
|
289
|
-
```ruby
|
290
|
-
# Invalid array JSON
|
291
|
-
ProcessDataTask.call(items: "[invalid json")
|
292
|
-
# → "items could not coerce into an array"
|
293
|
-
|
294
|
-
# Invalid date format
|
295
|
-
ProcessDataTask.call(start_date: "not-a-date")
|
296
|
-
# → "start_date could not coerce into a date"
|
297
|
-
|
298
|
-
# Multiple type failure
|
299
|
-
ProcessDataTask.call(value: "abc", type: [:integer, :float])
|
300
|
-
# → "value could not coerce into one of: integer, float"
|
301
|
-
```
|
302
|
-
|
303
|
-
## Custom Coercion Options
|
304
|
-
|
305
|
-
### Date/Time Formats
|
306
|
-
|
307
|
-
```ruby
|
308
|
-
class ImportDataTask < CMDx::Task
|
309
|
-
# US date format
|
310
|
-
required :birth_date, type: :date, format: "%m/%d/%Y"
|
311
|
-
|
312
|
-
# European datetime
|
313
|
-
required :timestamp, type: :datetime, format: "%d.%m.%Y %H:%M"
|
314
|
-
|
315
|
-
# 12-hour time
|
316
|
-
optional :appointment, type: :time, format: "%I:%M %p"
|
317
|
-
|
318
|
-
def call
|
319
|
-
# Dates parsed according to specified formats
|
320
|
-
end
|
321
|
-
end
|
322
|
-
```
|
323
|
-
|
324
|
-
### BigDecimal Precision
|
325
|
-
|
326
|
-
```ruby
|
327
|
-
class CalculatePriceTask < CMDx::Task
|
328
|
-
required :base_price, type: :big_decimal
|
329
|
-
required :tax_rate, type: :big_decimal, precision: 8
|
330
|
-
|
331
|
-
def call
|
332
|
-
tax_amount = base_price * tax_rate # High-precision calculation
|
333
|
-
end
|
334
|
-
end
|
335
|
-
```
|
336
|
-
|
337
|
-
## Custom Coercions
|
338
|
-
|
339
|
-
> [!NOTE]
|
340
|
-
> Register custom coercions for domain-specific types not covered by built-in coercions.
|
341
|
-
|
342
|
-
```ruby
|
343
|
-
# Custom coercion for currency handling
|
344
|
-
module CurrencyCoercion
|
345
|
-
module_function
|
346
|
-
|
347
|
-
def call(value, options = {})
|
348
|
-
return value if value.is_a?(BigDecimal)
|
349
|
-
|
350
|
-
# Remove currency symbols and formatting
|
351
|
-
clean_value = value.to_s.gsub(/[$,£€¥]/, '')
|
352
|
-
BigDecimal(clean_value)
|
353
|
-
rescue ArgumentError
|
354
|
-
raise CMDx::Coercion::Error, "Invalid currency format: #{value}"
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
# URL slug coercion
|
359
|
-
SlugCoercion = proc do |value|
|
360
|
-
value.to_s.downcase
|
361
|
-
.gsub(/[^a-z0-9\s-]/, '')
|
362
|
-
.gsub(/\s+/, '-')
|
363
|
-
.gsub(/-+/, '-')
|
364
|
-
.strip('-')
|
365
|
-
end
|
366
|
-
|
367
|
-
# Register coercions globally
|
368
|
-
CMDx.configure do |config|
|
369
|
-
config.coercions.register(:currency, CurrencyCoercion)
|
370
|
-
config.coercions.register(:slug, SlugCoercion)
|
371
|
-
end
|
372
|
-
|
373
|
-
# Use in tasks
|
374
|
-
class ProcessProductTask < CMDx::Task
|
375
|
-
required :price, type: :currency
|
376
|
-
required :url_slug, type: :slug
|
377
|
-
|
378
|
-
def call
|
379
|
-
price # → BigDecimal from "$99.99"
|
380
|
-
url_slug # → "my-product-name" from "My Product Name!"
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
ProcessProductTask.call(
|
385
|
-
price: "$149.99",
|
386
|
-
url_slug: "My Amazing Product!"
|
387
|
-
)
|
388
|
-
```
|
389
|
-
|
390
|
-
> [!TIP]
|
391
|
-
> Custom coercions should be idempotent and handle edge cases gracefully. Include proper error handling for invalid inputs.
|
392
|
-
|
393
|
-
---
|
394
|
-
|
395
|
-
- **Prev:** [Parameters - Namespacing](namespacing.md)
|
396
|
-
- **Next:** [Parameters - Validations](validations.md)
|