cmdx 1.9.0 → 1.9.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/yardoc.md +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/LLM.md +9 -0
  5. data/README.md +6 -1
  6. data/docs/getting_started.md +9 -0
  7. data/docs/index.md +13 -1
  8. data/lib/cmdx/attribute.rb +82 -1
  9. data/lib/cmdx/attribute_registry.rb +20 -0
  10. data/lib/cmdx/attribute_value.rb +25 -0
  11. data/lib/cmdx/callback_registry.rb +19 -0
  12. data/lib/cmdx/chain.rb +34 -1
  13. data/lib/cmdx/coercion_registry.rb +18 -0
  14. data/lib/cmdx/coercions/array.rb +2 -0
  15. data/lib/cmdx/coercions/big_decimal.rb +3 -0
  16. data/lib/cmdx/coercions/boolean.rb +5 -0
  17. data/lib/cmdx/coercions/complex.rb +2 -0
  18. data/lib/cmdx/coercions/date.rb +4 -0
  19. data/lib/cmdx/coercions/date_time.rb +5 -0
  20. data/lib/cmdx/coercions/float.rb +2 -0
  21. data/lib/cmdx/coercions/hash.rb +2 -0
  22. data/lib/cmdx/coercions/integer.rb +2 -0
  23. data/lib/cmdx/coercions/rational.rb +2 -0
  24. data/lib/cmdx/coercions/string.rb +2 -0
  25. data/lib/cmdx/coercions/symbol.rb +2 -0
  26. data/lib/cmdx/coercions/time.rb +5 -0
  27. data/lib/cmdx/configuration.rb +111 -3
  28. data/lib/cmdx/context.rb +36 -0
  29. data/lib/cmdx/deprecator.rb +3 -0
  30. data/lib/cmdx/errors.rb +22 -0
  31. data/lib/cmdx/executor.rb +43 -0
  32. data/lib/cmdx/faults.rb +14 -0
  33. data/lib/cmdx/identifier.rb +2 -0
  34. data/lib/cmdx/locale.rb +3 -0
  35. data/lib/cmdx/log_formatters/json.rb +2 -0
  36. data/lib/cmdx/log_formatters/key_value.rb +2 -0
  37. data/lib/cmdx/log_formatters/line.rb +2 -0
  38. data/lib/cmdx/log_formatters/logstash.rb +2 -0
  39. data/lib/cmdx/log_formatters/raw.rb +2 -0
  40. data/lib/cmdx/middleware_registry.rb +20 -0
  41. data/lib/cmdx/middlewares/correlate.rb +11 -0
  42. data/lib/cmdx/middlewares/runtime.rb +4 -0
  43. data/lib/cmdx/middlewares/timeout.rb +4 -0
  44. data/lib/cmdx/pipeline.rb +20 -1
  45. data/lib/cmdx/railtie.rb +4 -0
  46. data/lib/cmdx/result.rb +123 -1
  47. data/lib/cmdx/task.rb +91 -1
  48. data/lib/cmdx/utils/call.rb +2 -0
  49. data/lib/cmdx/utils/condition.rb +3 -0
  50. data/lib/cmdx/utils/format.rb +5 -0
  51. data/lib/cmdx/validator_registry.rb +18 -0
  52. data/lib/cmdx/validators/exclusion.rb +2 -0
  53. data/lib/cmdx/validators/format.rb +2 -0
  54. data/lib/cmdx/validators/inclusion.rb +2 -0
  55. data/lib/cmdx/validators/length.rb +14 -0
  56. data/lib/cmdx/validators/numeric.rb +14 -0
  57. data/lib/cmdx/validators/presence.rb +2 -0
  58. data/lib/cmdx/version.rb +4 -1
  59. data/lib/cmdx/workflow.rb +10 -0
  60. data/lib/cmdx.rb +8 -0
  61. data/lib/generators/cmdx/locale_generator.rb +0 -1
  62. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ac9af727a0dd815dad8285e7e46d666e3f26f51eb03c3f1ec014c12e4af0e23
4
- data.tar.gz: 8279a9ebcf5339fb281cd685cb0484b005f72b2605749f1418baea282be1afdf
3
+ metadata.gz: 9220688eed061c4c48562665fe9c2c780a2ecc08642eeaedb095d6bfc312e643
4
+ data.tar.gz: f470f2ccebce524942277865f2967979244dde07ba1a43095c7f95209127c635
5
5
  SHA512:
6
- metadata.gz: f97ef5703cacbfa7c51e4e513e214322c330750066e2420727d1bdc6469dac092b4f9b601291c8b0a721cba6bca863d22d4189bf10a8aa4b0ec53683f6598ccc
7
- data.tar.gz: 3a1019a2f2db8088ec70821d51c5d13a10554f788a7732a70a8c14830cd9804fee354447e61dae3ca86fcf4a8a664932bdff5465a78256845d224870e78951ce
6
+ metadata.gz: 155e2d09a4ca6147a7df578b0f2952ab81e781d5c492d821a4b71b08b41ef510cfccade001cc2e593f08ea1876ba6e5b658595a2bddaf6919f243c8df07d0569
7
+ data.tar.gz: fd0dba94495d6d139dc13507169143ee83f13dfac521d6ef607800c6d6e4cfd13e47cc03e2f27e047b185a0b9ebfc0a9d080143f7c6c2ec61aec139f38ea17cd
@@ -12,3 +12,4 @@ Add yardoc to the active tab using the following guidelines:
12
12
  - Method level docs should include `@example`, `param`, `@options`, `@return`, and any `@raise`
13
13
  - Hash `@params` should expand with possible `@option`
14
14
  - Module and method level docs should NOT include `@since`
15
+ - Add RBS inline comments after YARDoc block
data/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [UNRELEASED]
8
8
 
9
+ ## [1.9.1] - 2025-10-22
10
+
11
+ ### Added
12
+ - Added RBS inlines type signatures
13
+ - Added YARDocs for `attr_reader` and `attr_accessor` methods
14
+
9
15
  ## [1.9.0] - 2025-10-21
10
16
 
11
17
  ### Added
data/LLM.md CHANGED
@@ -372,6 +372,15 @@ end
372
372
  > [!TIP]
373
373
  > Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
374
374
 
375
+ ## Type safety
376
+
377
+ CMDx includes built-in RBS (Ruby Type Signature) inline annotations throughout the codebase, providing type information for static analysis and editor support.
378
+
379
+ - **Type checking** — Catch type errors before runtime using tools like Steep or TypeProf
380
+ - **Better IDE support** — Enhanced autocomplete, navigation, and inline documentation
381
+ - **Self-documenting code** — Clear method signatures and return types
382
+ - **Refactoring confidence** — Type-aware refactoring reduces bugs
383
+
375
384
  ---
376
385
 
377
386
  url: https://github.com/drexed/cmdx/blob/main/docs/basics/setup.md
data/README.md CHANGED
@@ -17,6 +17,9 @@
17
17
 
18
18
  Say goodbye to messy service objects. CMDx helps you design business logic with clarity and consistency—build faster, debug easier, and ship with confidence.
19
19
 
20
+ > [!NOTE]
21
+ > Documentation reflects the latest code on `main`. For version-specific documentation, please refer to the `docs/` directory within that version's tag.
22
+
20
23
  ## Requirements
21
24
 
22
25
  - Ruby: MRI 3.1+ or JRuby 9.4+.
@@ -105,6 +108,8 @@ I, [2022-07-17T18:43:15.000000 #3784] INFO -- CMDx:
105
108
  index=0 chain_id="018c2b95-b764-7615-a924-cc5b910ed1e5" type="Task" class="AnalyzeMetrics" state="complete" status="success" metadata={runtime: 187}
106
109
  ```
107
110
 
111
+ Ready to dive in? Check out the [Getting Started](https://drexed.github.io/cmdx/getting_started/) guide to learn more.
112
+
108
113
  ## Ecosystem
109
114
 
110
115
  - [cmdx-rspec](https://github.com/drexed/cmdx-rspec) - RSpec test matchers
@@ -116,7 +121,7 @@ For backwards compatibility of certain functionality:
116
121
 
117
122
  ## Contributing
118
123
 
119
- Bug reports and pull requests are welcome at https://github.com/drexed/cmdx. We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
124
+ Bug reports and pull requests are welcome at <https://github.com/drexed/cmdx>. We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
120
125
 
121
126
  ## License
122
127
 
@@ -367,3 +367,12 @@ end
367
367
  !!! tip
368
368
 
369
369
  Use **present tense verbs + noun** for task names, eg: `ModerateBlogPost`, `ScheduleAppointment`, `ValidateDocument`
370
+
371
+ ## Type safety
372
+
373
+ CMDx includes built-in RBS (Ruby Type Signature) inline annotations throughout the codebase, providing type information for static analysis and editor support.
374
+
375
+ - **Type checking** — Catch type errors before runtime using tools like Steep or TypeProf
376
+ - **Better IDE support** — Enhanced autocomplete, navigation, and inline documentation
377
+ - **Self-documenting code** — Clear method signatures and return types
378
+ - **Refactoring confidence** — Type-aware refactoring reduces bugs
data/docs/index.md CHANGED
@@ -6,8 +6,20 @@ Build business logic that's powerful, predictable, and maintainable.
6
6
  [![Build](https://github.com/drexed/cmdx/actions/workflows/ci.yml/badge.svg)](https://github.com/drexed/cmdx/actions/workflows/ci.yml)
7
7
  [![License](https://img.shields.io/github/license/drexed/cmdx)](https://github.com/drexed/cmdx/blob/main/LICENSE.txt)
8
8
 
9
+ ---
10
+
9
11
  Say goodbye to messy service objects. CMDx helps you design business logic with clarity and consistency—build faster, debug easier, and ship with confidence.
10
12
 
13
+ !!! note
14
+
15
+ Documentation reflects the latest code on `main`. For version-specific documentation, please refer to the `docs/` directory within that version's tag.
16
+
17
+ ## Requirements
18
+
19
+ - Ruby: MRI 3.1+ or JRuby 9.4+.
20
+
21
+ CMDx works with any Ruby framework. Rails support is built-in, but it's framework-agnostic at its core.
22
+
11
23
  ## Installation
12
24
 
13
25
  ```sh
@@ -113,7 +125,7 @@ For backwards compatibility of certain functionality:
113
125
 
114
126
  ## Contributing
115
127
 
116
- Bug reports and pull requests are welcome at https://github.com/drexed/cmdx. We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
128
+ Bug reports and pull requests are welcome at <https://github.com/drexed/cmdx>. We're committed to fostering a welcoming, collaborative community. Please follow our [code of conduct](CODE_OF_CONDUCT.md).
117
129
 
118
130
  ## License
119
131
 
@@ -6,14 +6,71 @@ module CMDx
6
6
  # They can be nested to create complex hierarchical data structures.
7
7
  class Attribute
8
8
 
9
+ # @rbs AFFIX: Proc
9
10
  AFFIX = proc do |value, &block|
10
11
  value == true ? block.call : value
11
12
  end.freeze
12
13
  private_constant :AFFIX
13
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
14
23
  attr_accessor :task
15
24
 
16
- attr_reader :name, :options, :children, :parent, :types
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
17
74
 
18
75
  # Creates a new attribute with the specified name and configuration.
19
76
  #
@@ -35,6 +92,8 @@ module CMDx
35
92
  # required :name, types: String
36
93
  # optional :email, types: String
37
94
  # end
95
+ #
96
+ # @rbs ((Symbol | String) name, ?Hash[Symbol, untyped] options) ?{ () -> void } -> void
38
97
  def initialize(name, options = {}, &)
39
98
  @parent = options.delete(:parent)
40
99
  @required = options.delete(:required) || false
@@ -62,6 +121,8 @@ module CMDx
62
121
  #
63
122
  # @example
64
123
  # Attribute.build(:first_name, :last_name, required: true, types: String)
124
+ #
125
+ # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
65
126
  def build(*names, **options, &)
66
127
  if names.none?
67
128
  raise ArgumentError, "no attributes given"
@@ -83,6 +144,8 @@ module CMDx
83
144
  #
84
145
  # @example
85
146
  # Attribute.optional(:description, :tags, types: String)
147
+ #
148
+ # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
86
149
  def optional(*names, **options, &)
87
150
  build(*names, **options.merge(required: false), &)
88
151
  end
@@ -98,6 +161,8 @@ module CMDx
98
161
  #
99
162
  # @example
100
163
  # Attribute.required(:id, :name, types: [Integer, String])
164
+ #
165
+ # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
101
166
  def required(*names, **options, &)
102
167
  build(*names, **options.merge(required: true), &)
103
168
  end
@@ -110,6 +175,8 @@ module CMDx
110
175
  #
111
176
  # @example
112
177
  # attribute.required? # => true
178
+ #
179
+ # @rbs () -> bool
113
180
  def required?
114
181
  !!@required
115
182
  end
@@ -120,6 +187,8 @@ module CMDx
120
187
  #
121
188
  # @example
122
189
  # attribute.source # => :context
190
+ #
191
+ # @rbs () -> untyped
123
192
  def source
124
193
  @source ||= parent&.method_name || begin
125
194
  value = options[:source]
@@ -140,6 +209,8 @@ module CMDx
140
209
  #
141
210
  # @example
142
211
  # attribute.method_name # => :user_name
212
+ #
213
+ # @rbs () -> Symbol
143
214
  def method_name
144
215
  @method_name ||= options[:as] || begin
145
216
  prefix = AFFIX.call(options[:prefix]) { "#{source}_" }
@@ -150,6 +221,8 @@ module CMDx
150
221
  end
151
222
 
152
223
  # Defines and verifies the entire attribute tree including nested children.
224
+ #
225
+ # @rbs () -> void
153
226
  def define_and_verify_tree
154
227
  define_and_verify
155
228
 
@@ -172,6 +245,8 @@ module CMDx
172
245
  #
173
246
  # @example
174
247
  # attributes :street, :city, :zip, types: String
248
+ #
249
+ # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
175
250
  def attributes(*names, **options, &)
176
251
  attrs = self.class.build(*names, **options.merge(parent: self), &)
177
252
  children.concat(attrs)
@@ -189,6 +264,8 @@ module CMDx
189
264
  #
190
265
  # @example
191
266
  # optional :middle_name, :nickname, types: String
267
+ #
268
+ # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
192
269
  def optional(*names, **options, &)
193
270
  attributes(*names, **options.merge(required: false), &)
194
271
  end
@@ -204,6 +281,8 @@ module CMDx
204
281
  #
205
282
  # @example
206
283
  # required :first_name, :last_name, types: String
284
+ #
285
+ # @rbs (*untyped names, **untyped options) ?{ () -> void } -> Array[Attribute]
207
286
  def required(*names, **options, &)
208
287
  attributes(*names, **options.merge(required: true), &)
209
288
  end
@@ -211,6 +290,8 @@ module CMDx
211
290
  # Defines the attribute method on the task and validates the configuration.
212
291
  #
213
292
  # @raise [RuntimeError] When the method name is already defined on the task
293
+ #
294
+ # @rbs () -> void
214
295
  def define_and_verify
215
296
  if task.respond_to?(method_name, true)
216
297
  raise <<~MESSAGE
@@ -6,6 +6,14 @@ module CMDx
6
6
  # in a hierarchical structure, supporting nested attribute definitions.
7
7
  class AttributeRegistry
8
8
 
9
+ # Returns the collection of registered attributes.
10
+ #
11
+ # @return [Array<Attribute>] Array of registered attributes
12
+ #
13
+ # @example
14
+ # registry.registry # => [#<Attribute @name=:name>, #<Attribute @name=:email>]
15
+ #
16
+ # @rbs @registry: Array[Attribute]
9
17
  attr_reader :registry
10
18
  alias to_a registry
11
19
 
@@ -18,6 +26,8 @@ module CMDx
18
26
  # @example
19
27
  # registry = AttributeRegistry.new
20
28
  # registry = AttributeRegistry.new([attr1, attr2])
29
+ #
30
+ # @rbs (?Array[Attribute] registry) -> void
21
31
  def initialize(registry = [])
22
32
  @registry = registry
23
33
  end
@@ -28,6 +38,8 @@ module CMDx
28
38
  #
29
39
  # @example
30
40
  # new_registry = registry.dup
41
+ #
42
+ # @rbs () -> AttributeRegistry
31
43
  def dup
32
44
  self.class.new(registry.dup)
33
45
  end
@@ -41,6 +53,8 @@ module CMDx
41
53
  # @example
42
54
  # registry.register(attribute)
43
55
  # registry.register([attr1, attr2])
56
+ #
57
+ # @rbs (Attribute | Array[Attribute] attributes) -> self
44
58
  def register(attributes)
45
59
  @registry.concat(Array(attributes))
46
60
  self
@@ -56,6 +70,8 @@ module CMDx
56
70
  # @example
57
71
  # registry.deregister(:name)
58
72
  # registry.deregister(['name1', 'name2'])
73
+ #
74
+ # @rbs ((Symbol | String | Array[Symbol | String]) names) -> self
59
75
  def deregister(names)
60
76
  Array(names).each do |name|
61
77
  @registry.reject! { |attribute| matches_attribute_tree?(attribute, name.to_sym) }
@@ -69,6 +85,8 @@ module CMDx
69
85
  # and validate the attribute hierarchy.
70
86
  #
71
87
  # @param task [Task] The task to associate with all attributes
88
+ #
89
+ # @rbs (Task task) -> void
72
90
  def define_and_verify(task)
73
91
  registry.each do |attribute|
74
92
  attribute.task = task
@@ -84,6 +102,8 @@ module CMDx
84
102
  # @param name [Symbol] The name to match against
85
103
  #
86
104
  # @return [Boolean] True if the attribute or any child matches the name
105
+ #
106
+ # @rbs (Attribute attribute, Symbol name) -> bool
87
107
  def matches_attribute_tree?(attribute, name)
88
108
  return true if attribute.method_name == name
89
109
 
@@ -8,6 +8,14 @@ module CMDx
8
8
 
9
9
  extend Forwardable
10
10
 
11
+ # Returns the attribute managed by this value handler.
12
+ #
13
+ # @return [Attribute] The attribute instance
14
+ #
15
+ # @example
16
+ # attr_value.attribute.name # => :user_id
17
+ #
18
+ # @rbs @attribute: Attribute
11
19
  attr_reader :attribute
12
20
 
13
21
  def_delegators :attribute, :task, :parent, :name, :options, :types, :source, :method_name, :required?
@@ -20,6 +28,8 @@ module CMDx
20
28
  # @example
21
29
  # attr = Attribute.new(:user_id, required: true)
22
30
  # attr_value = AttributeValue.new(attr)
31
+ #
32
+ # @rbs (Attribute attribute) -> void
23
33
  def initialize(attribute)
24
34
  @attribute = attribute
25
35
  end
@@ -30,6 +40,8 @@ module CMDx
30
40
  #
31
41
  # @example
32
42
  # attr_value.value # => "john_doe"
43
+ #
44
+ # @rbs () -> untyped
33
45
  def value
34
46
  attributes[method_name]
35
47
  end
@@ -41,6 +53,8 @@ module CMDx
41
53
  #
42
54
  # @example
43
55
  # attr_value.generate # => 42
56
+ #
57
+ # @rbs () -> untyped
44
58
  def generate
45
59
  return value if attributes.key?(method_name)
46
60
 
@@ -64,6 +78,8 @@ module CMDx
64
78
  # @example
65
79
  # attr_value.validate
66
80
  # # Validates value against :presence, :format, etc.
81
+ #
82
+ # @rbs () -> void
67
83
  def validate
68
84
  registry = task.class.settings[:validators]
69
85
 
@@ -86,6 +102,7 @@ module CMDx
86
102
  # @example
87
103
  # # Sources from task method, proc, or direct value
88
104
  # source_value # => "raw_value"
105
+ # @rbs () -> untyped
89
106
  def source_value
90
107
  sourced_value =
91
108
  case source
@@ -115,6 +132,8 @@ module CMDx
115
132
  # @example
116
133
  # # Default can be symbol, proc, or direct value
117
134
  # -> { rand(100) } # => 23
135
+ #
136
+ # @rbs () -> untyped
118
137
  def default_value
119
138
  default = options[:default]
120
139
 
@@ -140,6 +159,8 @@ module CMDx
140
159
  # @example
141
160
  # # Derives from hash key, method call, or proc execution
142
161
  # context.user_id # => 42
162
+ #
163
+ # @rbs (untyped source_value) -> untyped
143
164
  def derive_value(source_value)
144
165
  derived_value =
145
166
  case source_value
@@ -163,6 +184,8 @@ module CMDx
163
184
  #
164
185
  # @example
165
186
  # :downcase # => "hello"
187
+ #
188
+ # @rbs (untyped derived_value) -> untyped
166
189
  def transform_value(derived_value)
167
190
  transform = options[:transform]
168
191
 
@@ -186,6 +209,8 @@ module CMDx
186
209
  # @example
187
210
  # # Coerces "42" to Integer, "true" to Boolean, etc.
188
211
  # coerce_value("42") # => 42
212
+ #
213
+ # @rbs (untyped transformed_value) -> untyped
189
214
  def coerce_value(transformed_value)
190
215
  return transformed_value if types.empty?
191
216
 
@@ -7,6 +7,7 @@ module CMDx
7
7
  # Each callback type represents a specific execution phase or outcome.
8
8
  class CallbackRegistry
9
9
 
10
+ # @rbs TYPES: Array[Symbol]
10
11
  TYPES = %i[
11
12
  before_validation
12
13
  before_execution
@@ -20,10 +21,20 @@ module CMDx
20
21
  on_bad
21
22
  ].freeze
22
23
 
24
+ # Returns the internal registry of callbacks organized by type.
25
+ #
26
+ # @return [Hash{Symbol => Set<Array>}] Hash mapping callback types to their registered callables
27
+ #
28
+ # @example
29
+ # registry.registry # => { before_execution: #<Set: [[[:validate], {}]]> }
30
+ #
31
+ # @rbs @registry: Hash[Symbol, Set[Array[untyped]]]
23
32
  attr_reader :registry
24
33
  alias to_h registry
25
34
 
26
35
  # @param registry [Hash] Initial registry hash, defaults to empty
36
+ #
37
+ # @rbs (?Hash[Symbol, Set[Array[untyped]]] registry) -> void
27
38
  def initialize(registry = {})
28
39
  @registry = registry
29
40
  end
@@ -31,6 +42,8 @@ module CMDx
31
42
  # Creates a deep copy of the registry with duplicated callable sets
32
43
  #
33
44
  # @return [CallbackRegistry] A new instance with duplicated registry contents
45
+ #
46
+ # @rbs () -> CallbackRegistry
34
47
  def dup
35
48
  self.class.new(registry.transform_values(&:dup))
36
49
  end
@@ -54,6 +67,8 @@ module CMDx
54
67
  # registry.register(:on_success, if: { status: :completed }) do |task|
55
68
  # task.log("Success callback executed")
56
69
  # end
70
+ #
71
+ # @rbs (Symbol type, *untyped callables, **untyped options) ?{ (Task) -> void } -> self
57
72
  def register(type, *callables, **options, &block)
58
73
  callables << block if block_given?
59
74
 
@@ -73,6 +88,8 @@ module CMDx
73
88
  #
74
89
  # @example Remove a specific callback
75
90
  # registry.deregister(:before_execution, :validate_inputs)
91
+ #
92
+ # @rbs (Symbol type, *untyped callables, **untyped options) ?{ (Task) -> void } -> self
76
93
  def deregister(type, *callables, **options, &block)
77
94
  callables << block if block_given?
78
95
  return self unless registry[type]
@@ -91,6 +108,8 @@ module CMDx
91
108
  #
92
109
  # @example Invoke all before_execution callbacks
93
110
  # registry.invoke(:before_execution, task)
111
+ #
112
+ # @rbs (Symbol type, Task task) -> void
94
113
  def invoke(type, task)
95
114
  raise TypeError, "unknown callback type #{type.inspect}" unless TYPES.include?(type)
96
115
 
data/lib/cmdx/chain.rb CHANGED
@@ -8,9 +8,28 @@ module CMDx
8
8
 
9
9
  extend Forwardable
10
10
 
11
+ # @rbs THREAD_KEY: Symbol
11
12
  THREAD_KEY = :cmdx_chain
12
13
 
13
- attr_reader :id, :results
14
+ # Returns the unique identifier for this chain.
15
+ #
16
+ # @return [String] The chain identifier
17
+ #
18
+ # @example
19
+ # chain.id # => "abc123xyz"
20
+ #
21
+ # @rbs @id: String
22
+ attr_reader :id
23
+
24
+ # Returns the collection of execution results in this chain.
25
+ #
26
+ # @return [Array<Result>] Array of task results
27
+ #
28
+ # @example
29
+ # chain.results # => [#<Result>, #<Result>]
30
+ #
31
+ # @rbs @results: Array[Result]
32
+ attr_reader :results
14
33
 
15
34
  def_delegators :results, :index, :first, :last, :size
16
35
  def_delegators :first, :state, :status, :outcome, :runtime
@@ -18,6 +37,8 @@ module CMDx
18
37
  # Creates a new chain with a unique identifier and empty results collection.
19
38
  #
20
39
  # @return [Chain] A new chain instance
40
+ #
41
+ # @rbs () -> void
21
42
  def initialize
22
43
  @id = Identifier.generate
23
44
  @results = []
@@ -34,6 +55,8 @@ module CMDx
34
55
  # if chain
35
56
  # puts "Current chain: #{chain.id}"
36
57
  # end
58
+ #
59
+ # @rbs () -> Chain?
37
60
  def current
38
61
  Thread.current[THREAD_KEY]
39
62
  end
@@ -46,6 +69,8 @@ module CMDx
46
69
  #
47
70
  # @example
48
71
  # Chain.current = my_chain
72
+ #
73
+ # @rbs (Chain chain) -> Chain
49
74
  def current=(chain)
50
75
  Thread.current[THREAD_KEY] = chain
51
76
  end
@@ -56,6 +81,8 @@ module CMDx
56
81
  #
57
82
  # @example
58
83
  # Chain.clear
84
+ #
85
+ # @rbs () -> nil
59
86
  def clear
60
87
  Thread.current[THREAD_KEY] = nil
61
88
  end
@@ -73,6 +100,8 @@ module CMDx
73
100
  # result = task.execute
74
101
  # chain = Chain.build(result)
75
102
  # puts "Chain size: #{chain.size}"
103
+ #
104
+ # @rbs (Result result) -> Chain
76
105
  def build(result)
77
106
  raise TypeError, "must be a CMDx::Result" unless result.is_a?(Result)
78
107
 
@@ -95,6 +124,8 @@ module CMDx
95
124
  # chain_hash = chain.to_h
96
125
  # puts chain_hash[:id]
97
126
  # puts chain_hash[:results].size
127
+ #
128
+ # @rbs () -> Hash[Symbol, untyped]
98
129
  def to_h
99
130
  {
100
131
  id: id,
@@ -108,6 +139,8 @@ module CMDx
108
139
  #
109
140
  # @example
110
141
  # puts chain.to_s
142
+ #
143
+ # @rbs () -> String
111
144
  def to_s
112
145
  Utils::Format.to_str(to_h)
113
146
  end
@@ -7,6 +7,14 @@ module CMDx
7
7
  # for various data types including arrays, numbers, dates, and other primitives.
8
8
  class CoercionRegistry
9
9
 
10
+ # Returns the internal registry mapping coercion types to handler classes.
11
+ #
12
+ # @return [Hash{Symbol => Class}] Hash of coercion type names to coercion classes
13
+ #
14
+ # @example
15
+ # registry.registry # => { integer: Coercions::Integer, boolean: Coercions::Boolean }
16
+ #
17
+ # @rbs @registry: Hash[Symbol, Class]
10
18
  attr_reader :registry
11
19
  alias to_h registry
12
20
 
@@ -17,6 +25,8 @@ module CMDx
17
25
  # @example
18
26
  # registry = CoercionRegistry.new
19
27
  # registry = CoercionRegistry.new(custom: CustomCoercion)
28
+ #
29
+ # @rbs (?Hash[Symbol, Class]? registry) -> void
20
30
  def initialize(registry = nil)
21
31
  @registry = registry || {
22
32
  array: Coercions::Array,
@@ -40,6 +50,8 @@ module CMDx
40
50
  #
41
51
  # @example
42
52
  # new_registry = registry.dup
53
+ #
54
+ # @rbs () -> CoercionRegistry
43
55
  def dup
44
56
  self.class.new(registry.dup)
45
57
  end
@@ -54,6 +66,8 @@ module CMDx
54
66
  # @example
55
67
  # registry.register(:custom_type, CustomCoercion)
56
68
  # registry.register("another_type", AnotherCoercion)
69
+ #
70
+ # @rbs ((Symbol | String) name, Class coercion) -> self
57
71
  def register(name, coercion)
58
72
  registry[name.to_sym] = coercion
59
73
  self
@@ -68,6 +82,8 @@ module CMDx
68
82
  # @example
69
83
  # registry.deregister(:custom_type)
70
84
  # registry.deregister("another_type")
85
+ #
86
+ # @rbs ((Symbol | String) name) -> self
71
87
  def deregister(name)
72
88
  registry.delete(name.to_sym)
73
89
  self
@@ -87,6 +103,8 @@ module CMDx
87
103
  # @example
88
104
  # result = registry.coerce(:integer, task, "42")
89
105
  # result = registry.coerce(:boolean, task, "true", strict: true)
106
+ #
107
+ # @rbs (Symbol type, untyped task, untyped value, ?Hash[Symbol, untyped] options) -> untyped
90
108
  def coerce(type, task, value, options = {})
91
109
  raise TypeError, "unknown coercion type #{type.inspect}" unless registry.key?(type)
92
110
 
@@ -26,6 +26,8 @@ module CMDx
26
26
  # Array.call("hello") # => ["hello"]
27
27
  # Array.call(42) # => [42]
28
28
  # Array.call(nil) # => []
29
+ #
30
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Array[untyped]
29
31
  def call(value, options = {})
30
32
  if value.is_a?(::String) && value.start_with?("[")
31
33
  JSON.parse(value)