cmdx 1.20.0 → 2.0.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 (195) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +131 -1
  3. data/README.md +37 -24
  4. data/lib/cmdx/.DS_Store +0 -0
  5. data/lib/cmdx/callbacks.rb +179 -0
  6. data/lib/cmdx/chain.rb +78 -175
  7. data/lib/cmdx/coercions/array.rb +19 -33
  8. data/lib/cmdx/coercions/big_decimal.rb +12 -29
  9. data/lib/cmdx/coercions/boolean.rb +25 -45
  10. data/lib/cmdx/coercions/coerce.rb +32 -0
  11. data/lib/cmdx/coercions/complex.rb +12 -27
  12. data/lib/cmdx/coercions/date.rb +29 -33
  13. data/lib/cmdx/coercions/date_time.rb +29 -33
  14. data/lib/cmdx/coercions/float.rb +8 -29
  15. data/lib/cmdx/coercions/hash.rb +17 -43
  16. data/lib/cmdx/coercions/integer.rb +8 -32
  17. data/lib/cmdx/coercions/rational.rb +12 -33
  18. data/lib/cmdx/coercions/string.rb +6 -24
  19. data/lib/cmdx/coercions/symbol.rb +12 -26
  20. data/lib/cmdx/coercions/time.rb +31 -35
  21. data/lib/cmdx/coercions.rb +174 -0
  22. data/lib/cmdx/configuration.rb +45 -225
  23. data/lib/cmdx/context.rb +263 -242
  24. data/lib/cmdx/deprecation.rb +67 -0
  25. data/lib/cmdx/deprecators/error.rb +22 -0
  26. data/lib/cmdx/deprecators/log.rb +22 -0
  27. data/lib/cmdx/deprecators/warn.rb +21 -0
  28. data/lib/cmdx/deprecators.rb +101 -0
  29. data/lib/cmdx/errors.rb +145 -79
  30. data/lib/cmdx/executors/fiber.rb +42 -0
  31. data/lib/cmdx/executors/thread.rb +36 -0
  32. data/lib/cmdx/executors.rb +95 -0
  33. data/lib/cmdx/fault.rb +85 -78
  34. data/lib/cmdx/i18n_proxy.rb +104 -0
  35. data/lib/cmdx/input.rb +294 -0
  36. data/lib/cmdx/inputs.rb +218 -0
  37. data/lib/cmdx/log_formatters/json.rb +9 -20
  38. data/lib/cmdx/log_formatters/key_value.rb +10 -21
  39. data/lib/cmdx/log_formatters/line.rb +7 -19
  40. data/lib/cmdx/log_formatters/logstash.rb +8 -21
  41. data/lib/cmdx/log_formatters/raw.rb +8 -20
  42. data/lib/cmdx/logger_proxy.rb +30 -0
  43. data/lib/cmdx/mergers/deep_merge.rb +23 -0
  44. data/lib/cmdx/mergers/last_write_wins.rb +23 -0
  45. data/lib/cmdx/mergers/no_merge.rb +20 -0
  46. data/lib/cmdx/mergers.rb +95 -0
  47. data/lib/cmdx/middlewares.rb +128 -0
  48. data/lib/cmdx/output.rb +115 -0
  49. data/lib/cmdx/outputs.rb +66 -0
  50. data/lib/cmdx/pipeline.rb +144 -131
  51. data/lib/cmdx/railtie.rb +10 -36
  52. data/lib/cmdx/result.rb +252 -473
  53. data/lib/cmdx/retriers/bounded_random.rb +24 -0
  54. data/lib/cmdx/retriers/decorrelated_jitter.rb +28 -0
  55. data/lib/cmdx/retriers/exponential.rb +23 -0
  56. data/lib/cmdx/retriers/fibonacci.rb +39 -0
  57. data/lib/cmdx/retriers/full_random.rb +23 -0
  58. data/lib/cmdx/retriers/half_random.rb +24 -0
  59. data/lib/cmdx/retriers/linear.rb +23 -0
  60. data/lib/cmdx/retriers.rb +106 -0
  61. data/lib/cmdx/retry.rb +117 -138
  62. data/lib/cmdx/runtime.rb +251 -0
  63. data/lib/cmdx/settings.rb +68 -196
  64. data/lib/cmdx/signal.rb +165 -0
  65. data/lib/cmdx/task.rb +443 -336
  66. data/lib/cmdx/telemetry.rb +108 -0
  67. data/lib/cmdx/util.rb +73 -0
  68. data/lib/cmdx/validators/absence.rb +10 -39
  69. data/lib/cmdx/validators/exclusion.rb +33 -52
  70. data/lib/cmdx/validators/format.rb +19 -49
  71. data/lib/cmdx/validators/inclusion.rb +33 -54
  72. data/lib/cmdx/validators/length.rb +125 -127
  73. data/lib/cmdx/validators/numeric.rb +123 -123
  74. data/lib/cmdx/validators/presence.rb +10 -39
  75. data/lib/cmdx/validators/validate.rb +31 -0
  76. data/lib/cmdx/validators.rb +161 -0
  77. data/lib/cmdx/version.rb +2 -4
  78. data/lib/cmdx/workflow.rb +74 -82
  79. data/lib/cmdx.rb +111 -42
  80. data/lib/generators/cmdx/install_generator.rb +7 -17
  81. data/lib/generators/cmdx/task_generator.rb +12 -29
  82. data/lib/generators/cmdx/templates/install.rb +128 -52
  83. data/lib/generators/cmdx/templates/task.rb.tt +1 -1
  84. data/lib/generators/cmdx/templates/workflow.rb.tt +1 -2
  85. data/lib/generators/cmdx/workflow_generator.rb +12 -29
  86. data/lib/locales/en.yml +9 -6
  87. data/mkdocs.yml +25 -23
  88. metadata +39 -138
  89. data/lib/cmdx/attribute.rb +0 -440
  90. data/lib/cmdx/attribute_registry.rb +0 -185
  91. data/lib/cmdx/attribute_value.rb +0 -252
  92. data/lib/cmdx/callback_registry.rb +0 -169
  93. data/lib/cmdx/coercion_registry.rb +0 -138
  94. data/lib/cmdx/deprecator.rb +0 -77
  95. data/lib/cmdx/exception.rb +0 -46
  96. data/lib/cmdx/executor.rb +0 -374
  97. data/lib/cmdx/identifier.rb +0 -30
  98. data/lib/cmdx/locale.rb +0 -78
  99. data/lib/cmdx/middleware_registry.rb +0 -148
  100. data/lib/cmdx/middlewares/correlate.rb +0 -140
  101. data/lib/cmdx/middlewares/runtime.rb +0 -62
  102. data/lib/cmdx/middlewares/timeout.rb +0 -78
  103. data/lib/cmdx/parallelizer.rb +0 -100
  104. data/lib/cmdx/utils/call.rb +0 -53
  105. data/lib/cmdx/utils/condition.rb +0 -71
  106. data/lib/cmdx/utils/format.rb +0 -82
  107. data/lib/cmdx/utils/normalize.rb +0 -52
  108. data/lib/cmdx/utils/wrap.rb +0 -38
  109. data/lib/cmdx/validator_registry.rb +0 -143
  110. data/lib/generators/cmdx/locale_generator.rb +0 -39
  111. data/lib/locales/af.yml +0 -53
  112. data/lib/locales/ar.yml +0 -53
  113. data/lib/locales/az.yml +0 -53
  114. data/lib/locales/be.yml +0 -53
  115. data/lib/locales/bg.yml +0 -53
  116. data/lib/locales/bn.yml +0 -53
  117. data/lib/locales/bs.yml +0 -53
  118. data/lib/locales/ca.yml +0 -53
  119. data/lib/locales/cnr.yml +0 -53
  120. data/lib/locales/cs.yml +0 -53
  121. data/lib/locales/cy.yml +0 -53
  122. data/lib/locales/da.yml +0 -53
  123. data/lib/locales/de.yml +0 -53
  124. data/lib/locales/dz.yml +0 -53
  125. data/lib/locales/el.yml +0 -53
  126. data/lib/locales/eo.yml +0 -53
  127. data/lib/locales/es.yml +0 -53
  128. data/lib/locales/et.yml +0 -53
  129. data/lib/locales/eu.yml +0 -53
  130. data/lib/locales/fa.yml +0 -53
  131. data/lib/locales/fi.yml +0 -53
  132. data/lib/locales/fr.yml +0 -53
  133. data/lib/locales/fy.yml +0 -53
  134. data/lib/locales/gd.yml +0 -53
  135. data/lib/locales/gl.yml +0 -53
  136. data/lib/locales/he.yml +0 -53
  137. data/lib/locales/hi.yml +0 -53
  138. data/lib/locales/hr.yml +0 -53
  139. data/lib/locales/hu.yml +0 -53
  140. data/lib/locales/hy.yml +0 -53
  141. data/lib/locales/id.yml +0 -53
  142. data/lib/locales/is.yml +0 -53
  143. data/lib/locales/it.yml +0 -53
  144. data/lib/locales/ja.yml +0 -53
  145. data/lib/locales/ka.yml +0 -53
  146. data/lib/locales/kk.yml +0 -53
  147. data/lib/locales/km.yml +0 -53
  148. data/lib/locales/kn.yml +0 -53
  149. data/lib/locales/ko.yml +0 -53
  150. data/lib/locales/lb.yml +0 -53
  151. data/lib/locales/lo.yml +0 -53
  152. data/lib/locales/lt.yml +0 -53
  153. data/lib/locales/lv.yml +0 -53
  154. data/lib/locales/mg.yml +0 -53
  155. data/lib/locales/mk.yml +0 -53
  156. data/lib/locales/ml.yml +0 -53
  157. data/lib/locales/mn.yml +0 -53
  158. data/lib/locales/mr-IN.yml +0 -53
  159. data/lib/locales/ms.yml +0 -53
  160. data/lib/locales/nb.yml +0 -53
  161. data/lib/locales/ne.yml +0 -53
  162. data/lib/locales/nl.yml +0 -53
  163. data/lib/locales/nn.yml +0 -53
  164. data/lib/locales/oc.yml +0 -53
  165. data/lib/locales/or.yml +0 -53
  166. data/lib/locales/pa.yml +0 -53
  167. data/lib/locales/pl.yml +0 -53
  168. data/lib/locales/pt.yml +0 -53
  169. data/lib/locales/rm.yml +0 -53
  170. data/lib/locales/ro.yml +0 -53
  171. data/lib/locales/ru.yml +0 -53
  172. data/lib/locales/sc.yml +0 -53
  173. data/lib/locales/sk.yml +0 -53
  174. data/lib/locales/sl.yml +0 -53
  175. data/lib/locales/sq.yml +0 -53
  176. data/lib/locales/sr.yml +0 -53
  177. data/lib/locales/st.yml +0 -53
  178. data/lib/locales/sv.yml +0 -53
  179. data/lib/locales/sw.yml +0 -53
  180. data/lib/locales/ta.yml +0 -53
  181. data/lib/locales/te.yml +0 -53
  182. data/lib/locales/th.yml +0 -53
  183. data/lib/locales/tl.yml +0 -53
  184. data/lib/locales/tr.yml +0 -53
  185. data/lib/locales/tt.yml +0 -53
  186. data/lib/locales/ug.yml +0 -53
  187. data/lib/locales/uk.yml +0 -53
  188. data/lib/locales/ur.yml +0 -53
  189. data/lib/locales/uz.yml +0 -53
  190. data/lib/locales/vi.yml +0 -53
  191. data/lib/locales/wo.yml +0 -53
  192. data/lib/locales/zh-CN.yml +0 -53
  193. data/lib/locales/zh-HK.yml +0 -53
  194. data/lib/locales/zh-TW.yml +0 -53
  195. data/lib/locales/zh-YUE.yml +0 -53
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cmdx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.20.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juan Gomez
@@ -37,20 +37,6 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
- - !ruby/object:Gem::Dependency
41
- name: zeitwerk
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: '0'
47
- type: :runtime
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
40
  - !ruby/object:Gem::Dependency
55
41
  name: bundler
56
42
  requirement: !ruby/object:Gem::Requirement
@@ -65,20 +51,6 @@ dependencies:
65
51
  - - ">="
66
52
  - !ruby/object:Gem::Version
67
53
  version: '0'
68
- - !ruby/object:Gem::Dependency
69
- name: cmdx-rspec
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- version: '0'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
54
  - !ruby/object:Gem::Dependency
83
55
  name: i18n
84
56
  requirement: !ruby/object:Gem::Requirement
@@ -234,15 +206,13 @@ files:
234
206
  - Rakefile
235
207
  - lib/cmdx.rb
236
208
  - lib/cmdx/.DS_Store
237
- - lib/cmdx/attribute.rb
238
- - lib/cmdx/attribute_registry.rb
239
- - lib/cmdx/attribute_value.rb
240
- - lib/cmdx/callback_registry.rb
209
+ - lib/cmdx/callbacks.rb
241
210
  - lib/cmdx/chain.rb
242
- - lib/cmdx/coercion_registry.rb
211
+ - lib/cmdx/coercions.rb
243
212
  - lib/cmdx/coercions/array.rb
244
213
  - lib/cmdx/coercions/big_decimal.rb
245
214
  - lib/cmdx/coercions/boolean.rb
215
+ - lib/cmdx/coercions/coerce.rb
246
216
  - lib/cmdx/coercions/complex.rb
247
217
  - lib/cmdx/coercions/date.rb
248
218
  - lib/cmdx/coercions/date_time.rb
@@ -255,35 +225,51 @@ files:
255
225
  - lib/cmdx/coercions/time.rb
256
226
  - lib/cmdx/configuration.rb
257
227
  - lib/cmdx/context.rb
258
- - lib/cmdx/deprecator.rb
228
+ - lib/cmdx/deprecation.rb
229
+ - lib/cmdx/deprecators.rb
230
+ - lib/cmdx/deprecators/error.rb
231
+ - lib/cmdx/deprecators/log.rb
232
+ - lib/cmdx/deprecators/warn.rb
259
233
  - lib/cmdx/errors.rb
260
- - lib/cmdx/exception.rb
261
- - lib/cmdx/executor.rb
234
+ - lib/cmdx/executors.rb
235
+ - lib/cmdx/executors/fiber.rb
236
+ - lib/cmdx/executors/thread.rb
262
237
  - lib/cmdx/fault.rb
263
- - lib/cmdx/identifier.rb
264
- - lib/cmdx/locale.rb
238
+ - lib/cmdx/i18n_proxy.rb
239
+ - lib/cmdx/input.rb
240
+ - lib/cmdx/inputs.rb
265
241
  - lib/cmdx/log_formatters/json.rb
266
242
  - lib/cmdx/log_formatters/key_value.rb
267
243
  - lib/cmdx/log_formatters/line.rb
268
244
  - lib/cmdx/log_formatters/logstash.rb
269
245
  - lib/cmdx/log_formatters/raw.rb
270
- - lib/cmdx/middleware_registry.rb
271
- - lib/cmdx/middlewares/correlate.rb
272
- - lib/cmdx/middlewares/runtime.rb
273
- - lib/cmdx/middlewares/timeout.rb
274
- - lib/cmdx/parallelizer.rb
246
+ - lib/cmdx/logger_proxy.rb
247
+ - lib/cmdx/mergers.rb
248
+ - lib/cmdx/mergers/deep_merge.rb
249
+ - lib/cmdx/mergers/last_write_wins.rb
250
+ - lib/cmdx/mergers/no_merge.rb
251
+ - lib/cmdx/middlewares.rb
252
+ - lib/cmdx/output.rb
253
+ - lib/cmdx/outputs.rb
275
254
  - lib/cmdx/pipeline.rb
276
255
  - lib/cmdx/railtie.rb
277
256
  - lib/cmdx/result.rb
257
+ - lib/cmdx/retriers.rb
258
+ - lib/cmdx/retriers/bounded_random.rb
259
+ - lib/cmdx/retriers/decorrelated_jitter.rb
260
+ - lib/cmdx/retriers/exponential.rb
261
+ - lib/cmdx/retriers/fibonacci.rb
262
+ - lib/cmdx/retriers/full_random.rb
263
+ - lib/cmdx/retriers/half_random.rb
264
+ - lib/cmdx/retriers/linear.rb
278
265
  - lib/cmdx/retry.rb
266
+ - lib/cmdx/runtime.rb
279
267
  - lib/cmdx/settings.rb
268
+ - lib/cmdx/signal.rb
280
269
  - lib/cmdx/task.rb
281
- - lib/cmdx/utils/call.rb
282
- - lib/cmdx/utils/condition.rb
283
- - lib/cmdx/utils/format.rb
284
- - lib/cmdx/utils/normalize.rb
285
- - lib/cmdx/utils/wrap.rb
286
- - lib/cmdx/validator_registry.rb
270
+ - lib/cmdx/telemetry.rb
271
+ - lib/cmdx/util.rb
272
+ - lib/cmdx/validators.rb
287
273
  - lib/cmdx/validators/absence.rb
288
274
  - lib/cmdx/validators/exclusion.rb
289
275
  - lib/cmdx/validators/format.rb
@@ -291,101 +277,16 @@ files:
291
277
  - lib/cmdx/validators/length.rb
292
278
  - lib/cmdx/validators/numeric.rb
293
279
  - lib/cmdx/validators/presence.rb
280
+ - lib/cmdx/validators/validate.rb
294
281
  - lib/cmdx/version.rb
295
282
  - lib/cmdx/workflow.rb
296
283
  - lib/generators/cmdx/install_generator.rb
297
- - lib/generators/cmdx/locale_generator.rb
298
284
  - lib/generators/cmdx/task_generator.rb
299
285
  - lib/generators/cmdx/templates/install.rb
300
286
  - lib/generators/cmdx/templates/task.rb.tt
301
287
  - lib/generators/cmdx/templates/workflow.rb.tt
302
288
  - lib/generators/cmdx/workflow_generator.rb
303
- - lib/locales/af.yml
304
- - lib/locales/ar.yml
305
- - lib/locales/az.yml
306
- - lib/locales/be.yml
307
- - lib/locales/bg.yml
308
- - lib/locales/bn.yml
309
- - lib/locales/bs.yml
310
- - lib/locales/ca.yml
311
- - lib/locales/cnr.yml
312
- - lib/locales/cs.yml
313
- - lib/locales/cy.yml
314
- - lib/locales/da.yml
315
- - lib/locales/de.yml
316
- - lib/locales/dz.yml
317
- - lib/locales/el.yml
318
289
  - lib/locales/en.yml
319
- - lib/locales/eo.yml
320
- - lib/locales/es.yml
321
- - lib/locales/et.yml
322
- - lib/locales/eu.yml
323
- - lib/locales/fa.yml
324
- - lib/locales/fi.yml
325
- - lib/locales/fr.yml
326
- - lib/locales/fy.yml
327
- - lib/locales/gd.yml
328
- - lib/locales/gl.yml
329
- - lib/locales/he.yml
330
- - lib/locales/hi.yml
331
- - lib/locales/hr.yml
332
- - lib/locales/hu.yml
333
- - lib/locales/hy.yml
334
- - lib/locales/id.yml
335
- - lib/locales/is.yml
336
- - lib/locales/it.yml
337
- - lib/locales/ja.yml
338
- - lib/locales/ka.yml
339
- - lib/locales/kk.yml
340
- - lib/locales/km.yml
341
- - lib/locales/kn.yml
342
- - lib/locales/ko.yml
343
- - lib/locales/lb.yml
344
- - lib/locales/lo.yml
345
- - lib/locales/lt.yml
346
- - lib/locales/lv.yml
347
- - lib/locales/mg.yml
348
- - lib/locales/mk.yml
349
- - lib/locales/ml.yml
350
- - lib/locales/mn.yml
351
- - lib/locales/mr-IN.yml
352
- - lib/locales/ms.yml
353
- - lib/locales/nb.yml
354
- - lib/locales/ne.yml
355
- - lib/locales/nl.yml
356
- - lib/locales/nn.yml
357
- - lib/locales/oc.yml
358
- - lib/locales/or.yml
359
- - lib/locales/pa.yml
360
- - lib/locales/pl.yml
361
- - lib/locales/pt.yml
362
- - lib/locales/rm.yml
363
- - lib/locales/ro.yml
364
- - lib/locales/ru.yml
365
- - lib/locales/sc.yml
366
- - lib/locales/sk.yml
367
- - lib/locales/sl.yml
368
- - lib/locales/sq.yml
369
- - lib/locales/sr.yml
370
- - lib/locales/st.yml
371
- - lib/locales/sv.yml
372
- - lib/locales/sw.yml
373
- - lib/locales/ta.yml
374
- - lib/locales/te.yml
375
- - lib/locales/th.yml
376
- - lib/locales/tl.yml
377
- - lib/locales/tr.yml
378
- - lib/locales/tt.yml
379
- - lib/locales/ug.yml
380
- - lib/locales/uk.yml
381
- - lib/locales/ur.yml
382
- - lib/locales/uz.yml
383
- - lib/locales/vi.yml
384
- - lib/locales/wo.yml
385
- - lib/locales/zh-CN.yml
386
- - lib/locales/zh-HK.yml
387
- - lib/locales/zh-TW.yml
388
- - lib/locales/zh-YUE.yml
389
290
  - mkdocs.yml
390
291
  homepage: https://github.com/drexed/cmdx
391
292
  licenses:
@@ -404,14 +305,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
404
305
  requirements:
405
306
  - - ">="
406
307
  - !ruby/object:Gem::Version
407
- version: 3.1.0
308
+ version: 3.3.0
408
309
  required_rubygems_version: !ruby/object:Gem::Requirement
409
310
  requirements:
410
311
  - - ">="
411
312
  - !ruby/object:Gem::Version
412
313
  version: '0'
413
314
  requirements: []
414
- rubygems_version: 4.0.6
315
+ rubygems_version: 4.0.11
415
316
  specification_version: 4
416
317
  summary: CMDx is a framework for building maintainable business processes.
417
318
  test_files: []
@@ -1,440 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- # Represents a configurable attribute within a CMDx task.
5
- # Attributes define the data structure and validation rules for task parameters.
6
- # They can be nested to create complex hierarchical data structures.
7
- class Attribute
8
-
9
- # @rbs AFFIX: Proc
10
- AFFIX = proc do |value, &block|
11
- value == true ? block.call : value
12
- end.freeze
13
- private_constant :AFFIX
14
-
15
- # Returns the task instance associated with this attribute.
16
- #
17
- # @return [CMDx::Task] The task instance
18
- #
19
- # @example
20
- # attribute.task.context[:user_id] # => 42
21
- #
22
- # @rbs @task: Task
23
- attr_accessor :task
24
-
25
- # Returns the name of this attribute.
26
- #
27
- # @return [Symbol] The attribute name
28
- #
29
- # @example
30
- # attribute.name # => :user_id
31
- #
32
- # @rbs @name: Symbol
33
- attr_reader :name
34
-
35
- # Returns the configuration options for this attribute.
36
- #
37
- # @return [Hash{Symbol => Object}] Configuration options hash
38
- #
39
- # @example
40
- # attribute.options # => { required: true, default: 0 }
41
- #
42
- # @rbs @options: Hash[Symbol, untyped]
43
- attr_reader :options
44
-
45
- # Returns the child attributes for nested structures.
46
- #
47
- # @return [Array<Attribute>] Array of child attributes
48
- #
49
- # @example
50
- # attribute.children # => [#<Attribute @name=:street>, #<Attribute @name=:city>]
51
- #
52
- # @rbs @children: Array[Attribute]
53
- attr_reader :children
54
-
55
- # Returns the parent attribute if this is a nested attribute.
56
- #
57
- # @return [Attribute, nil] The parent attribute, or nil if root-level
58
- #
59
- # @example
60
- # attribute.parent # => #<Attribute @name=:address>
61
- #
62
- # @rbs @parent: (Attribute | nil)
63
- attr_reader :parent
64
-
65
- # Returns the expected type(s) for this attribute's value.
66
- #
67
- # @return [Array<Class>] Array of expected type classes
68
- #
69
- # @example
70
- # attribute.types # => [Integer, String]
71
- #
72
- # @rbs @types: Array[Class]
73
- attr_reader :types
74
-
75
- # Returns the description of the attribute.
76
- #
77
- # @return [String] The description of the attribute
78
- #
79
- # @example
80
- # attribute.description # => "The user's name"
81
- #
82
- # @rbs @description: String
83
- attr_reader :description
84
-
85
- # Creates a new attribute with the specified name and configuration.
86
- #
87
- # @param name [Symbol, String] The name of the attribute
88
- # @param options [Hash] Configuration options for the attribute
89
- # @option options [Attribute] :parent The parent attribute for nested structures
90
- # @option options [Boolean] :required Whether the attribute is required (default: false)
91
- # @option options [Array<Class>, Class] :types The expected type(s) for the attribute value
92
- # @option options [String] :description The description of the attribute
93
- # @option options [Symbol, String, Proc] :source The source of the attribute value
94
- # @option options [Symbol, String] :as The method name to use for this attribute
95
- # @option options [Symbol, String, Boolean] :prefix The prefix to add to the method name
96
- # @option options [Symbol, String, Boolean] :suffix The suffix to add to the method name
97
- # @option options [Object] :default The default value for the attribute
98
- #
99
- # @yield [self] Block to configure nested attributes
100
- #
101
- # @example
102
- # Attribute.new(:user_id, required: true, types: [Integer, String]) do
103
- # required :name, types: String
104
- # optional :email, types: String
105
- # end
106
- #
107
- # @rbs ((Symbol | String) name, ?Hash[Symbol, untyped] options) ?{ () -> void } -> void
108
- def initialize(name, options = {}, &)
109
- @parent = options.delete(:parent)
110
- @required = options.delete(:required) || false
111
- @types = Utils::Wrap.array(options.delete(:types) || options.delete(:type))
112
- @description = options.delete(:description) || options.delete(:desc)
113
-
114
- @name = name.to_sym
115
- @options = options
116
- @children = []
117
-
118
- instance_eval(&) if block_given?
119
- end
120
-
121
- # Deep-copies children and clears task-dependent memoization so that
122
- # duped attributes can be safely bound to a task without mutating the
123
- # class-level originals. This makes concurrent execution thread-safe.
124
- #
125
- # @param source [Attribute] The attribute being duplicated
126
- #
127
- # @rbs (Attribute source) -> void
128
- def initialize_dup(source)
129
- super
130
- @children = source.children.map(&:dup)
131
- remove_instance_variable(:@source) if defined?(@source)
132
- remove_instance_variable(:@method_name) if defined?(@method_name)
133
- @task = nil
134
- end
135
-
136
- class << self
137
-
138
- # Builds multiple attributes with the same configuration.
139
- #
140
- # @param names [Array<Symbol, String>] The names of the attributes to create
141
- # @param options [Hash] Configuration options for the attributes
142
- # @option options [Object] :* Any attribute configuration option
143
- #
144
- # @yield [self] Block to configure nested attributes
145
- #
146
- # @return [Array<Attribute>] Array of created attributes
147
- #
148
- # @raise [ArgumentError] When no names are provided or :as is used with multiple attributes
149
- #
150
- # @example
151
- # Attribute.build(:first_name, :last_name, required: true, types: String)
152
- #
153
- # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
154
- def build(*names, **options, &)
155
- if names.none?
156
- raise ArgumentError, "no attributes given"
157
- elsif (names.size > 1) && options.key?(:as)
158
- raise ArgumentError, "the :as option only supports one attribute per definition"
159
- end
160
-
161
- names.filter_map { |name| new(name, **options, &) }
162
- end
163
-
164
- # Creates optional attributes (not required).
165
- #
166
- # @param names [Array<Symbol, String>] The names of the attributes to create
167
- # @param options [Hash] Configuration options for the attributes
168
- # @option options [Object] :* Any attribute configuration option
169
- #
170
- # @yield [self] Block to configure nested attributes
171
- #
172
- # @return [Array<Attribute>] Array of created optional attributes
173
- #
174
- # @example
175
- # Attribute.optional(:description, :tags, types: String)
176
- #
177
- # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
178
- def optional(*names, **options, &)
179
- build(*names, **options.merge(required: false), &)
180
- end
181
-
182
- # Creates required attributes.
183
- #
184
- # @param names [Array<Symbol, String>] The names of the attributes to create
185
- # @param options [Hash] Configuration options for the attributes
186
- # @option options [Object] :* Any attribute configuration option
187
- #
188
- # @yield [self] Block to configure nested attributes
189
- #
190
- # @return [Array<Attribute>] Array of created required attributes
191
- #
192
- # @example
193
- # Attribute.required(:id, :name, types: [Integer, String])
194
- #
195
- # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
196
- def required(*names, **options, &)
197
- build(*names, **options.merge(required: true), &)
198
- end
199
-
200
- end
201
-
202
- # Checks if the attribute is optional.
203
- #
204
- # @return [Boolean] true if the attribute is optional, false otherwise
205
- #
206
- # @example
207
- # attribute.optional? # => true
208
- #
209
- # @rbs () -> bool
210
- def optional?
211
- !required? || !!options[:optional]
212
- end
213
-
214
- # Checks if the attribute is required.
215
- #
216
- # @return [Boolean] true if the attribute is required, false otherwise
217
- #
218
- # @example
219
- # attribute.required? # => true
220
- #
221
- # @rbs () -> bool
222
- def required?
223
- !!@required
224
- end
225
-
226
- # Determines the source of the attribute value. Returns :context
227
- # as a safe fallback when task is not yet set (e.g., schema introspection).
228
- #
229
- # @return [Symbol] The source identifier for the attribute value
230
- #
231
- # @example
232
- # attribute.source # => :context
233
- #
234
- # @rbs () -> untyped
235
- def source
236
- return @source if defined?(@source)
237
-
238
- parent&.method_name || begin
239
- value = options[:source]
240
-
241
- if value.is_a?(Proc)
242
- task ? @source = task.instance_eval(&value) : :context
243
- elsif value.respond_to?(:call)
244
- task ? @source = value.call(task) : :context
245
- else
246
- @source = value || :context
247
- end
248
- end
249
- end
250
-
251
- # Returns the method name for this attribute when it can be resolved
252
- # statically (without a task instance). Returns nil for Proc/callable
253
- # sources whose method name depends on runtime evaluation.
254
- #
255
- # @return [Symbol, nil] The static method name, or nil if dynamic
256
- #
257
- # @example
258
- # attribute.allocation_name # => :user_name
259
- #
260
- # @rbs () -> Symbol?
261
- def allocation_name
262
- return @allocation_name if defined?(@allocation_name)
263
-
264
- @allocation_name = options[:as] || begin
265
- src = options[:source]
266
- source_name =
267
- if parent
268
- parent.allocation_name
269
- elsif !src.is_a?(Proc) && !src.respond_to?(:call)
270
- src || :context
271
- end
272
-
273
- if source_name.is_a?(Symbol)
274
- prefix = AFFIX.call(options[:prefix]) { "#{source_name}_" }
275
- suffix = AFFIX.call(options[:suffix]) { "_#{source_name}" }
276
- :"#{prefix}#{name}#{suffix}"
277
- end
278
- end
279
- end
280
-
281
- # Generates the method name for accessing this attribute.
282
- #
283
- # @return [Symbol] The method name for the attribute
284
- #
285
- # @example
286
- # attribute.method_name # => :user_name
287
- #
288
- # @rbs () -> Symbol
289
- def method_name
290
- return @method_name if defined?(@method_name)
291
-
292
- result = options[:as] || begin
293
- prefix = AFFIX.call(options[:prefix]) { "#{source}_" }
294
- suffix = AFFIX.call(options[:suffix]) { "_#{source}" }
295
- :"#{prefix}#{name}#{suffix}"
296
- end
297
-
298
- # Only memoize if @source is defined to avoid memoizing method
299
- # name when no task is present.
300
- return result unless defined?(@source)
301
-
302
- @method_name = result
303
- end
304
-
305
- # Defines and verifies the entire attribute tree including nested children.
306
- #
307
- # @rbs () -> void
308
- def define_and_verify_tree
309
- define_and_verify
310
-
311
- children.each do |child|
312
- child.task = task
313
- child.define_and_verify_tree
314
- end
315
- end
316
-
317
- # Recursively clears the task reference from this attribute and all children.
318
- # Prevents the class-level attribute from retaining the last-executed task instance.
319
- #
320
- # @rbs () -> void
321
- def clear_task_tree!
322
- @task = nil
323
- children.each(&:clear_task_tree!)
324
- end
325
-
326
- # @return [Hash] A hash representation of the attribute
327
- #
328
- # @example
329
- # attribute.to_h # => {
330
- # name: :user_id,
331
- # method_name: :current_user_id,
332
- # description: "The user's name",
333
- # required: true,
334
- # types: [:integer],
335
- # options: {},
336
- # children: []
337
- # }
338
- #
339
- # @rbs () -> Hash[Symbol, untyped]
340
- def to_h
341
- {
342
- name: name,
343
- method_name: method_name,
344
- description: description,
345
- required: required?,
346
- types: types,
347
- options: options.except(:if, :unless),
348
- children: children.map(&:to_h)
349
- }
350
- end
351
-
352
- private
353
-
354
- # Creates nested attributes as children of this attribute.
355
- #
356
- # @param names [Array<Symbol, String>] The names of the child attributes
357
- # @param options [Hash] Configuration options for the child attributes
358
- # @option options [Object] :* Any attribute configuration option
359
- #
360
- # @yield [self] Block to configure the child attributes
361
- #
362
- # @return [Array<Attribute>] Array of created child attributes
363
- #
364
- # @example
365
- # attributes :street, :city, :zip, types: String
366
- #
367
- # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
368
- def attributes(*names, **options, &)
369
- attrs = self.class.build(*names, **options.merge(parent: self), &)
370
- children.concat(attrs)
371
- end
372
- alias attribute attributes
373
-
374
- # Creates optional nested attributes.
375
- #
376
- # @param names [Array<Symbol, String>] The names of the optional child attributes
377
- # @param options [Hash] Configuration options for the child attributes
378
- # @option options [Object] :* Any attribute configuration option
379
- #
380
- # @yield [self] Block to configure the child attributes
381
- #
382
- # @return [Array<Attribute>] Array of created optional child attributes
383
- #
384
- # @example
385
- # optional :middle_name, :nickname, types: String
386
- #
387
- # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
388
- def optional(*names, **options, &)
389
- attributes(*names, **options.merge(required: false), &)
390
- end
391
-
392
- # Creates required nested attributes.
393
- #
394
- # @param names [Array<Symbol, String>] The names of the required child attributes
395
- # @param options [Hash] Configuration options for the child attributes
396
- # @option options [Object] :* Any attribute configuration option
397
- #
398
- # @yield [self] Block to configure the child attributes
399
- #
400
- # @return [Array<Attribute>] Array of created required child attributes
401
- #
402
- # @example
403
- # required :first_name, :last_name, types: String
404
- #
405
- # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
406
- def required(*names, **options, &)
407
- attributes(*names, **options.merge(required: true), &)
408
- end
409
-
410
- # Defines the attribute reader on the task class (once) and
411
- # generates/validates the per-instance value (every execution).
412
- #
413
- # @raise [RuntimeError] When the method name is already defined on the task
414
- #
415
- # @rbs () -> void
416
- def define_and_verify
417
- name_of_method = method_name
418
-
419
- unless task.class.method_defined?(name_of_method)
420
- if task.respond_to?(name_of_method, true)
421
- raise <<~MESSAGE
422
- The method #{name_of_method.inspect} is already defined on the #{task.class.name} task.
423
- This may be due conflicts with one of the task's user defined or internal methods/attributes.
424
-
425
- Use :as, :prefix, and/or :suffix attribute options to avoid conflicts with existing methods.
426
- MESSAGE
427
- end
428
-
429
- task.class.define_method(name_of_method) do
430
- attributes[name_of_method]
431
- end
432
- end
433
-
434
- attribute_value = AttributeValue.new(self)
435
- attribute_value.generate
436
- attribute_value.validate
437
- end
438
-
439
- end
440
- end