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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/CHANGELOG.md +5 -133
  11. data/LLM.md +3317 -0
  12. data/README.md +68 -44
  13. data/docs/attributes/coercions.md +162 -0
  14. data/docs/attributes/defaults.md +90 -0
  15. data/docs/attributes/definitions.md +281 -0
  16. data/docs/attributes/naming.md +78 -0
  17. data/docs/attributes/validations.md +309 -0
  18. data/docs/basics/chain.md +56 -249
  19. data/docs/basics/context.md +56 -289
  20. data/docs/basics/execution.md +114 -0
  21. data/docs/basics/setup.md +37 -334
  22. data/docs/callbacks.md +89 -467
  23. data/docs/deprecation.md +91 -174
  24. data/docs/getting_started.md +212 -202
  25. data/docs/internationalization.md +11 -647
  26. data/docs/interruptions/exceptions.md +23 -198
  27. data/docs/interruptions/faults.md +71 -151
  28. data/docs/interruptions/halt.md +109 -186
  29. data/docs/logging.md +44 -256
  30. data/docs/middlewares.md +113 -426
  31. data/docs/outcomes/result.md +81 -228
  32. data/docs/outcomes/states.md +33 -221
  33. data/docs/outcomes/statuses.md +21 -311
  34. data/docs/tips_and_tricks.md +120 -70
  35. data/docs/workflows.md +99 -283
  36. data/lib/cmdx/.DS_Store +0 -0
  37. data/lib/cmdx/attribute.rb +229 -0
  38. data/lib/cmdx/attribute_registry.rb +94 -0
  39. data/lib/cmdx/attribute_value.rb +193 -0
  40. data/lib/cmdx/callback_registry.rb +69 -77
  41. data/lib/cmdx/chain.rb +56 -73
  42. data/lib/cmdx/coercion_registry.rb +52 -68
  43. data/lib/cmdx/coercions/array.rb +19 -18
  44. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  45. data/lib/cmdx/coercions/boolean.rb +26 -25
  46. data/lib/cmdx/coercions/complex.rb +21 -22
  47. data/lib/cmdx/coercions/date.rb +25 -23
  48. data/lib/cmdx/coercions/date_time.rb +24 -25
  49. data/lib/cmdx/coercions/float.rb +25 -22
  50. data/lib/cmdx/coercions/hash.rb +31 -32
  51. data/lib/cmdx/coercions/integer.rb +30 -24
  52. data/lib/cmdx/coercions/rational.rb +29 -24
  53. data/lib/cmdx/coercions/string.rb +19 -22
  54. data/lib/cmdx/coercions/symbol.rb +37 -0
  55. data/lib/cmdx/coercions/time.rb +26 -25
  56. data/lib/cmdx/configuration.rb +49 -108
  57. data/lib/cmdx/context.rb +222 -44
  58. data/lib/cmdx/deprecator.rb +61 -0
  59. data/lib/cmdx/errors.rb +42 -252
  60. data/lib/cmdx/exceptions.rb +39 -0
  61. data/lib/cmdx/faults.rb +78 -39
  62. data/lib/cmdx/freezer.rb +51 -0
  63. data/lib/cmdx/identifier.rb +30 -0
  64. data/lib/cmdx/locale.rb +52 -0
  65. data/lib/cmdx/log_formatters/json.rb +21 -22
  66. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  67. data/lib/cmdx/log_formatters/line.rb +15 -22
  68. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  69. data/lib/cmdx/log_formatters/raw.rb +16 -22
  70. data/lib/cmdx/middleware_registry.rb +70 -74
  71. data/lib/cmdx/middlewares/correlate.rb +90 -54
  72. data/lib/cmdx/middlewares/runtime.rb +58 -0
  73. data/lib/cmdx/middlewares/timeout.rb +48 -68
  74. data/lib/cmdx/railtie.rb +12 -45
  75. data/lib/cmdx/result.rb +229 -314
  76. data/lib/cmdx/task.rb +194 -366
  77. data/lib/cmdx/utils/call.rb +49 -0
  78. data/lib/cmdx/utils/condition.rb +71 -0
  79. data/lib/cmdx/utils/format.rb +61 -0
  80. data/lib/cmdx/validator_registry.rb +63 -72
  81. data/lib/cmdx/validators/exclusion.rb +38 -67
  82. data/lib/cmdx/validators/format.rb +48 -49
  83. data/lib/cmdx/validators/inclusion.rb +43 -74
  84. data/lib/cmdx/validators/length.rb +91 -154
  85. data/lib/cmdx/validators/numeric.rb +87 -162
  86. data/lib/cmdx/validators/presence.rb +37 -50
  87. data/lib/cmdx/version.rb +1 -1
  88. data/lib/cmdx/worker.rb +178 -0
  89. data/lib/cmdx/workflow.rb +85 -81
  90. data/lib/cmdx.rb +19 -13
  91. data/lib/generators/cmdx/install_generator.rb +14 -13
  92. data/lib/generators/cmdx/task_generator.rb +25 -50
  93. data/lib/generators/cmdx/templates/install.rb +11 -46
  94. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  95. data/lib/locales/en.yml +18 -4
  96. data/src/cmdx-logo.png +0 -0
  97. metadata +32 -116
  98. data/docs/ai_prompts.md +0 -393
  99. data/docs/basics/call.md +0 -317
  100. data/docs/configuration.md +0 -344
  101. data/docs/parameters/coercions.md +0 -396
  102. data/docs/parameters/defaults.md +0 -335
  103. data/docs/parameters/definitions.md +0 -446
  104. data/docs/parameters/namespacing.md +0 -378
  105. data/docs/parameters/validations.md +0 -405
  106. data/docs/testing.md +0 -553
  107. data/lib/cmdx/callback.rb +0 -53
  108. data/lib/cmdx/chain_inspector.rb +0 -56
  109. data/lib/cmdx/chain_serializer.rb +0 -63
  110. data/lib/cmdx/coercion.rb +0 -57
  111. data/lib/cmdx/coercions/virtual.rb +0 -29
  112. data/lib/cmdx/core_ext/hash.rb +0 -83
  113. data/lib/cmdx/core_ext/module.rb +0 -98
  114. data/lib/cmdx/core_ext/object.rb +0 -125
  115. data/lib/cmdx/correlator.rb +0 -122
  116. data/lib/cmdx/error.rb +0 -67
  117. data/lib/cmdx/fault.rb +0 -140
  118. data/lib/cmdx/immutator.rb +0 -52
  119. data/lib/cmdx/lazy_struct.rb +0 -246
  120. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  121. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  122. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  123. data/lib/cmdx/logger.rb +0 -49
  124. data/lib/cmdx/logger_ansi.rb +0 -68
  125. data/lib/cmdx/logger_serializer.rb +0 -116
  126. data/lib/cmdx/middleware.rb +0 -70
  127. data/lib/cmdx/parameter.rb +0 -312
  128. data/lib/cmdx/parameter_evaluator.rb +0 -231
  129. data/lib/cmdx/parameter_inspector.rb +0 -66
  130. data/lib/cmdx/parameter_registry.rb +0 -106
  131. data/lib/cmdx/parameter_serializer.rb +0 -59
  132. data/lib/cmdx/result_ansi.rb +0 -71
  133. data/lib/cmdx/result_inspector.rb +0 -71
  134. data/lib/cmdx/result_logger.rb +0 -59
  135. data/lib/cmdx/result_serializer.rb +0 -104
  136. data/lib/cmdx/rspec/matchers.rb +0 -28
  137. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  138. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  139. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  141. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  142. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  143. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  144. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  145. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  146. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  147. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  148. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  149. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  150. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  151. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  152. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  153. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  154. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  155. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  156. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  157. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  158. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  159. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  160. data/lib/cmdx/task_deprecator.rb +0 -58
  161. data/lib/cmdx/task_processor.rb +0 -246
  162. data/lib/cmdx/task_serializer.rb +0 -57
  163. data/lib/cmdx/utils/ansi_color.rb +0 -73
  164. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  165. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  166. data/lib/cmdx/utils/name_affix.rb +0 -52
  167. data/lib/cmdx/validator.rb +0 -57
  168. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  169. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  170. data/lib/locales/ar.yml +0 -35
  171. data/lib/locales/cs.yml +0 -35
  172. data/lib/locales/da.yml +0 -35
  173. data/lib/locales/de.yml +0 -35
  174. data/lib/locales/el.yml +0 -35
  175. data/lib/locales/es.yml +0 -35
  176. data/lib/locales/fi.yml +0 -35
  177. data/lib/locales/fr.yml +0 -35
  178. data/lib/locales/he.yml +0 -35
  179. data/lib/locales/hi.yml +0 -35
  180. data/lib/locales/it.yml +0 -35
  181. data/lib/locales/ja.yml +0 -35
  182. data/lib/locales/ko.yml +0 -35
  183. data/lib/locales/nl.yml +0 -35
  184. data/lib/locales/no.yml +0 -35
  185. data/lib/locales/pl.yml +0 -35
  186. data/lib/locales/pt.yml +0 -35
  187. data/lib/locales/ru.yml +0 -35
  188. data/lib/locales/sv.yml +0 -35
  189. data/lib/locales/th.yml +0 -35
  190. data/lib/locales/tr.yml +0 -35
  191. data/lib/locales/vi.yml +0 -35
  192. 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)