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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/rules/cursor-instructions.mdc +6 -0
  4. data/.rubocop.yml +19 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +95 -28
  7. data/README.md +73 -25
  8. data/docs/ai_prompts.md +319 -0
  9. data/docs/basics/call.md +234 -14
  10. data/docs/basics/chain.md +280 -0
  11. data/docs/basics/context.md +241 -33
  12. data/docs/basics/setup.md +85 -12
  13. data/docs/callbacks.md +283 -0
  14. data/docs/configuration.md +155 -30
  15. data/docs/getting_started.md +145 -22
  16. data/docs/internationalization.md +148 -0
  17. data/docs/interruptions/exceptions.md +198 -11
  18. data/docs/interruptions/faults.md +196 -44
  19. data/docs/interruptions/halt.md +188 -35
  20. data/docs/logging.md +204 -53
  21. data/docs/middlewares.md +745 -0
  22. data/docs/outcomes/result.md +305 -10
  23. data/docs/outcomes/states.md +212 -31
  24. data/docs/outcomes/statuses.md +284 -30
  25. data/docs/parameters/coercions.md +411 -29
  26. data/docs/parameters/defaults.md +258 -25
  27. data/docs/parameters/definitions.md +247 -72
  28. data/docs/parameters/namespacing.md +259 -27
  29. data/docs/parameters/validations.md +173 -168
  30. data/docs/testing.md +560 -0
  31. data/docs/tips_and_tricks.md +103 -42
  32. data/docs/workflows.md +329 -0
  33. data/lib/cmdx/.DS_Store +0 -0
  34. data/lib/cmdx/callback.rb +69 -0
  35. data/lib/cmdx/callback_registry.rb +106 -0
  36. data/lib/cmdx/chain.rb +190 -0
  37. data/lib/cmdx/chain_inspector.rb +149 -0
  38. data/lib/cmdx/chain_serializer.rb +175 -0
  39. data/lib/cmdx/coercions/array.rb +37 -0
  40. data/lib/cmdx/coercions/big_decimal.rb +33 -0
  41. data/lib/cmdx/coercions/boolean.rb +41 -1
  42. data/lib/cmdx/coercions/complex.rb +31 -0
  43. data/lib/cmdx/coercions/date.rb +39 -0
  44. data/lib/cmdx/coercions/date_time.rb +39 -0
  45. data/lib/cmdx/coercions/float.rb +31 -0
  46. data/lib/cmdx/coercions/hash.rb +42 -0
  47. data/lib/cmdx/coercions/integer.rb +32 -0
  48. data/lib/cmdx/coercions/rational.rb +31 -0
  49. data/lib/cmdx/coercions/string.rb +31 -0
  50. data/lib/cmdx/coercions/time.rb +39 -0
  51. data/lib/cmdx/coercions/virtual.rb +31 -0
  52. data/lib/cmdx/configuration.rb +217 -9
  53. data/lib/cmdx/context.rb +173 -2
  54. data/lib/cmdx/core_ext/hash.rb +72 -0
  55. data/lib/cmdx/core_ext/module.rb +94 -0
  56. data/lib/cmdx/core_ext/object.rb +105 -0
  57. data/lib/cmdx/correlator.rb +217 -0
  58. data/lib/cmdx/error.rb +210 -8
  59. data/lib/cmdx/errors.rb +256 -1
  60. data/lib/cmdx/fault.rb +177 -2
  61. data/lib/cmdx/faults.rb +158 -2
  62. data/lib/cmdx/immutator.rb +121 -2
  63. data/lib/cmdx/lazy_struct.rb +261 -18
  64. data/lib/cmdx/log_formatters/json.rb +46 -0
  65. data/lib/cmdx/log_formatters/key_value.rb +46 -0
  66. data/lib/cmdx/log_formatters/line.rb +54 -0
  67. data/lib/cmdx/log_formatters/logstash.rb +64 -0
  68. data/lib/cmdx/log_formatters/pretty_json.rb +57 -0
  69. data/lib/cmdx/log_formatters/pretty_key_value.rb +51 -0
  70. data/lib/cmdx/log_formatters/pretty_line.rb +60 -0
  71. data/lib/cmdx/log_formatters/raw.rb +54 -0
  72. data/lib/cmdx/logger.rb +85 -0
  73. data/lib/cmdx/logger_ansi.rb +93 -7
  74. data/lib/cmdx/logger_serializer.rb +116 -0
  75. data/lib/cmdx/middleware.rb +74 -0
  76. data/lib/cmdx/middleware_registry.rb +106 -0
  77. data/lib/cmdx/middlewares/correlate.rb +266 -0
  78. data/lib/cmdx/middlewares/timeout.rb +232 -0
  79. data/lib/cmdx/parameter.rb +228 -1
  80. data/lib/cmdx/parameter_inspector.rb +61 -0
  81. data/lib/cmdx/parameter_registry.rb +125 -0
  82. data/lib/cmdx/parameter_serializer.rb +83 -0
  83. data/lib/cmdx/parameter_validator.rb +62 -0
  84. data/lib/cmdx/parameter_value.rb +109 -1
  85. data/lib/cmdx/parameters_inspector.rb +59 -0
  86. data/lib/cmdx/parameters_serializer.rb +102 -0
  87. data/lib/cmdx/railtie.rb +123 -3
  88. data/lib/cmdx/result.rb +367 -25
  89. data/lib/cmdx/result_ansi.rb +105 -9
  90. data/lib/cmdx/result_inspector.rb +76 -0
  91. data/lib/cmdx/result_logger.rb +90 -3
  92. data/lib/cmdx/result_serializer.rb +137 -0
  93. data/lib/cmdx/rspec/result_matchers.rb +917 -0
  94. data/lib/cmdx/rspec/task_matchers.rb +570 -0
  95. data/lib/cmdx/task.rb +405 -37
  96. data/lib/cmdx/task_serializer.rb +74 -2
  97. data/lib/cmdx/utils/ansi_color.rb +95 -0
  98. data/lib/cmdx/utils/log_timestamp.rb +48 -0
  99. data/lib/cmdx/utils/monotonic_runtime.rb +71 -4
  100. data/lib/cmdx/utils/name_affix.rb +78 -0
  101. data/lib/cmdx/validators/custom.rb +82 -0
  102. data/lib/cmdx/validators/exclusion.rb +94 -0
  103. data/lib/cmdx/validators/format.rb +102 -8
  104. data/lib/cmdx/validators/inclusion.rb +104 -0
  105. data/lib/cmdx/validators/length.rb +128 -0
  106. data/lib/cmdx/validators/numeric.rb +128 -0
  107. data/lib/cmdx/validators/presence.rb +93 -7
  108. data/lib/cmdx/version.rb +7 -1
  109. data/lib/cmdx/workflow.rb +394 -0
  110. data/lib/cmdx.rb +25 -64
  111. data/lib/generators/cmdx/install_generator.rb +37 -1
  112. data/lib/generators/cmdx/task_generator.rb +69 -1
  113. data/lib/generators/cmdx/templates/install.rb +43 -15
  114. data/lib/generators/cmdx/workflow_generator.rb +109 -0
  115. data/lib/locales/ar.yml +36 -0
  116. data/lib/locales/cs.yml +36 -0
  117. data/lib/locales/da.yml +36 -0
  118. data/lib/locales/de.yml +36 -0
  119. data/lib/locales/el.yml +36 -0
  120. data/lib/locales/en.yml +20 -20
  121. data/lib/locales/es.yml +20 -20
  122. data/lib/locales/fi.yml +36 -0
  123. data/lib/locales/fr.yml +36 -0
  124. data/lib/locales/he.yml +36 -0
  125. data/lib/locales/hi.yml +36 -0
  126. data/lib/locales/it.yml +36 -0
  127. data/lib/locales/ja.yml +36 -0
  128. data/lib/locales/ko.yml +36 -0
  129. data/lib/locales/nl.yml +36 -0
  130. data/lib/locales/no.yml +36 -0
  131. data/lib/locales/pl.yml +36 -0
  132. data/lib/locales/pt.yml +36 -0
  133. data/lib/locales/ru.yml +36 -0
  134. data/lib/locales/sv.yml +36 -0
  135. data/lib/locales/th.yml +36 -0
  136. data/lib/locales/tr.yml +36 -0
  137. data/lib/locales/vi.yml +36 -0
  138. data/lib/locales/zh.yml +36 -0
  139. metadata +77 -15
  140. data/docs/basics/run.md +0 -34
  141. data/docs/batch.md +0 -53
  142. data/docs/example.md +0 -82
  143. data/docs/hooks.md +0 -62
  144. data/lib/cmdx/batch.rb +0 -43
  145. data/lib/cmdx/parameters.rb +0 -35
  146. data/lib/cmdx/run.rb +0 -39
  147. data/lib/cmdx/run_inspector.rb +0 -26
  148. data/lib/cmdx/run_serializer.rb +0 -20
  149. data/lib/cmdx/task_hook.rb +0 -18
  150. data/lib/generators/cmdx/batch_generator.rb +0 -30
  151. /data/lib/generators/cmdx/templates/{batch.rb.tt → workflow.rb.tt} +0 -0
@@ -1,57 +1,289 @@
1
1
  # Parameters - Namespacing
2
2
 
3
- Parameters can have the delegated method prefixed and/or suffixed to
4
- prevent clashing where the source object have methods with the same name.
3
+ Parameter namespacing provides method name customization to prevent conflicts
4
+ and enable flexible parameter access patterns. When parameters share names with
5
+ existing methods or when multiple parameters from different sources have the
6
+ same name, namespacing ensures clean method resolution within tasks.
5
7
 
6
- `:prefix` and `:suffix` can be used independently or both at the same time.
8
+ ## Table of Contents
7
9
 
8
- ## With fixed value
10
+ - [TLDR](#tldr)
11
+ - [Namespacing Fundamentals](#namespacing-fundamentals)
12
+ - [Fixed Value Namespacing](#fixed-value-namespacing)
13
+ - [Dynamic Source-Based Namespacing](#dynamic-source-based-namespacing)
14
+ - [Conflict Resolution](#conflict-resolution)
15
+ - [Advanced Namespacing Patterns](#advanced-namespacing-patterns)
16
+ - [Error Handling with Namespacing](#error-handling-with-namespacing)
17
+
18
+ ## TLDR
19
+
20
+ - **Method naming** - Use `prefix:` and `suffix:` to customize parameter method names
21
+ - **Fixed prefixes** - `prefix: "user_"` creates `user_name` method for `name` parameter
22
+ - **Dynamic prefixes** - `prefix: true` uses source name (e.g., `context_name`)
23
+ - **Conflict resolution** - Avoid conflicts with Ruby methods or multiple same-named parameters
24
+ - **Call arguments** - Always use original parameter names, namespacing only affects method names
25
+
26
+ ## Namespacing Fundamentals
27
+
28
+ > [!IMPORTANT]
29
+ > The `:prefix` and `:suffix` options modify only the generated accessor method names while preserving the original parameter names for call arguments.
30
+
31
+ This separation allows for flexible method naming without affecting the task interface.
32
+
33
+ ### Fixed Value Namespacing
34
+
35
+ Use string or symbol values to add consistent prefixes or suffixes to parameter
36
+ method names:
9
37
 
10
38
  ```ruby
11
- class DetermineBoxSizeTask < CMDx::Task
39
+ class CreateOrderTask < CMDx::Task
40
+
41
+ # Fixed prefix for shipping dimensions
42
+ required :width, prefix: "shipping_"
43
+ required :height, prefix: "shipping_"
44
+
45
+ # Fixed suffix for user contact info
46
+ required :email, suffix: "_contact"
47
+ required :phone, suffix: "_contact"
12
48
 
13
- required :width, prefix: :before_
14
- required :height, suffix: "_after"
49
+ # Combined prefix and suffix
50
+ required :weight, prefix: "item_", suffix: "_kg"
15
51
 
16
52
  def call
17
- before_width #=> 1
18
- height_after #=> 2
53
+ # Generated method names with namespacing
54
+ shipping_width #=> accesses width parameter
55
+ shipping_height #=> accesses height parameter
56
+ email_contact #=> accesses email parameter
57
+ phone_contact #=> accesses phone parameter
58
+ item_weight_kg #=> accesses weight parameter
19
59
  end
20
60
 
21
61
  end
22
62
 
23
- # Call arguments match the parameter names
24
- DetermineBoxSizeTask.call(width: 1, height: 2)
63
+ # Call arguments use original parameter names
64
+ CreateOrderTask.call(
65
+ width: 10,
66
+ height: 20,
67
+ email: "customer@example.com",
68
+ phone: "555-1234",
69
+ weight: 2.5
70
+ )
25
71
  ```
26
72
 
27
- ## With source name
73
+ ### Dynamic Source-Based Namespacing
74
+
75
+ Use `true` value to automatically generate prefixes or suffixes based on the
76
+ parameter source name:
28
77
 
29
78
  ```ruby
30
- class DetermineBoxSizeTask < CMDx::Task
79
+ class ProcessUserRegistrationTask < CMDx::Task
80
+
81
+ # Automatic prefix from default source (:context)
82
+ required :user_id, prefix: true # Generates: context_user_id
31
83
 
32
- # Default (:context) as source
33
- optional :height, prefix: true
84
+ # Automatic suffix from custom source
85
+ required :name, source: :profile, suffix: true # Generates: name_profile
34
86
 
35
- # Custom source
36
- optional :width, source: :account, suffix: true
87
+ # Combined automatic namespacing
88
+ required :email, source: :account, prefix: true, suffix: true # Generates: account_email_account
37
89
 
38
90
  def call
39
- context_height #=> 1
40
- width_account #=> 2
91
+ context_user_id #=> accesses context.user_id
92
+ name_profile #=> accesses profile.name
93
+ account_email_account #=> accesses account.email
41
94
  end
42
95
 
43
- end
96
+ private
44
97
 
45
- # Call arguments match the parameter names
46
- account = Account.new(width: 2)
47
- DetermineBoxSizeTask.call(height: 1, account: account)
98
+ def profile
99
+ @profile ||= User.find(context.user_id).profile
100
+ end
101
+
102
+ def account
103
+ @account ||= User.find(context.user_id).account
104
+ end
105
+
106
+ end
48
107
  ```
49
108
 
50
109
  > [!NOTE]
51
- > `:prefix` or `:suffix` with a custom source and a fixed value
52
- > will always return the fixed value without the source.
110
+ > Call arguments always use original parameter names regardless of namespacing configuration.
111
+
112
+ ## Conflict Resolution
113
+
114
+ Namespacing is essential when dealing with method name conflicts or when
115
+ accessing multiple objects with similar attribute names:
116
+
117
+ ### Method Name Conflicts
118
+
119
+ ```ruby
120
+ class UpdateUserProfileTask < CMDx::Task
121
+
122
+ # Avoid conflict with Ruby's built-in 'name' method
123
+ required :name, prefix: "user_"
124
+
125
+ # Avoid conflict with custom private methods
126
+ required :status, suffix: "_param"
127
+
128
+ def call
129
+ user_name #=> parameter value, not Ruby's Object#name
130
+ status_param #=> parameter value, not custom status method
131
+ end
132
+
133
+ private
134
+
135
+ def status
136
+ "processing" # Custom method that would conflict without suffix
137
+ end
138
+
139
+ end
140
+ ```
141
+
142
+ ### Multiple Source Disambiguation
143
+
144
+ ```ruby
145
+ class GenerateInvoiceTask < CMDx::Task
146
+
147
+ # Customer information
148
+ required :name, source: :customer, prefix: "customer_"
149
+ required :email, source: :customer, prefix: "customer_"
150
+
151
+ # Company information
152
+ required :name, source: :company, prefix: "company_"
153
+ required :email, source: :company, prefix: "company_"
154
+
155
+ # Order information
156
+ required :total, source: :order, suffix: "_amount"
157
+ required :status, source: :order, suffix: "_state"
158
+
159
+ def call
160
+ # Clear disambiguation of same-named attributes
161
+ customer_name #=> customer.name
162
+ company_name #=> company.name
163
+ customer_email #=> customer.email
164
+ company_email #=> company.email
165
+ total_amount #=> order.total
166
+ status_state #=> order.status
167
+ end
168
+
169
+ private
170
+
171
+ def customer
172
+ @customer ||= Customer.find(context.customer_id)
173
+ end
174
+
175
+ def company
176
+ @company ||= Company.find(context.company_id)
177
+ end
178
+
179
+ def order
180
+ @order ||= Order.find(context.order_id)
181
+ end
182
+
183
+ end
184
+ ```
185
+
186
+ ## Advanced Namespacing Patterns
187
+
188
+ ### Hierarchical Namespacing
189
+
190
+ Combine namespacing with nested parameters for complex data structures:
191
+
192
+ ```ruby
193
+ class CreateShipmentTask < CMDx::Task
194
+
195
+ # Origin address with prefix
196
+ required :origin_address, source: :shipment, prefix: "from_" do
197
+ required :street, :city, :state, :zip
198
+ end
199
+
200
+ # Destination address with suffix
201
+ required :destination_address, source: :shipment, suffix: "_to" do
202
+ required :street, :city, :state, :zip
203
+ end
204
+
205
+ def call
206
+ from_origin_address #=> shipment.origin_address
207
+ destination_address_to #=> shipment.destination_address
208
+
209
+ # Nested parameters access depends on current context
210
+ street #=> current address context street
211
+ city #=> current address context city
212
+ end
213
+
214
+ private
215
+
216
+ def shipment
217
+ @shipment ||= Shipment.find(context.shipment_id)
218
+ end
219
+
220
+ end
221
+ ```
222
+
223
+ ### Conditional Namespacing
224
+
225
+ Apply namespacing based on runtime conditions:
226
+
227
+ ```ruby
228
+ class ProcessPaymentTask < CMDx::Task
229
+
230
+ # Different namespacing based on payment type
231
+ required :reference_id,
232
+ prefix: -> { context.payment_type == "credit_card" ? "card_" : "bank_" }
233
+
234
+ def call
235
+ # Method names determined at runtime
236
+ if context.payment_type == "credit_card"
237
+ card_reference_id #=> accesses reference_id parameter
238
+ else
239
+ bank_reference_id #=> accesses reference_id parameter
240
+ end
241
+ end
242
+
243
+ end
244
+ ```
245
+
246
+ ## Error Handling with Namespacing
247
+
248
+ ```ruby
249
+ class ValidateUserDataTask < CMDx::Task
250
+
251
+ required :email,
252
+ prefix: "user_",
253
+ type: :string,
254
+ format: { with: /@/ }
255
+
256
+ required :age,
257
+ suffix: "_years",
258
+ type: :integer,
259
+ numeric: { min: 18 }
260
+
261
+ def call
262
+ # Access via namespaced methods
263
+ user_email #=> validated email
264
+ age_years #=> validated age
265
+ end
266
+
267
+ end
268
+
269
+ # Invalid parameters
270
+ result = ValidateUserDataTask.call(
271
+ email: "invalid-email",
272
+ age: "not-a-number"
273
+ )
274
+
275
+ result.failed? #=> true
276
+ result.metadata
277
+ #=> {
278
+ # reason: "email format is not valid. age could not coerce into an integer.",
279
+ # messages: {
280
+ # user_email: ["format is not valid"],
281
+ # age_years: ["could not coerce into an integer"]
282
+ # }
283
+ # }
284
+ ```
53
285
 
54
286
  ---
55
287
 
56
- - **Prev:** [Definitions](https://github.com/drexed/cmdx/blob/main/docs/parameters/definitions.md)
57
- - **Next:** [Coercions](https://github.com/drexed/cmdx/blob/main/docs/parameters/coercions.md)
288
+ - **Prev:** [Parameters - Definitions](definitions.md)
289
+ - **Next:** [Parameters - Coercions](coercions.md)