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
data/lib/cmdx/result.rb CHANGED
@@ -11,18 +11,22 @@ module CMDx
11
11
 
12
12
  extend Forwardable
13
13
 
14
+ # @rbs STATES: Array[String]
14
15
  STATES = [
15
16
  INITIALIZED = "initialized", # Initial state before execution
16
17
  EXECUTING = "executing", # Currently executing task logic
17
18
  COMPLETE = "complete", # Successfully completed execution
18
19
  INTERRUPTED = "interrupted" # Execution was halted due to failure
19
20
  ].freeze
21
+
22
+ # @rbs STATUSES: Array[String]
20
23
  STATUSES = [
21
24
  SUCCESS = "success", # Task completed successfully
22
25
  SKIPPED = "skipped", # Task was skipped intentionally
23
26
  FAILED = "failed" # Task failed due to error or validation
24
27
  ].freeze
25
28
 
29
+ # @rbs STRIP_FAILURE: Proc
26
30
  STRIP_FAILURE = proc do |hash, result, key|
27
31
  unless result.send(:"#{key}?")
28
32
  # Strip caused/threw failures since its the same info as the log line
@@ -31,7 +35,65 @@ module CMDx
31
35
  end.freeze
32
36
  private_constant :STRIP_FAILURE
33
37
 
34
- attr_reader :task, :state, :status, :metadata, :reason, :cause
38
+ # Returns the task instance associated with this result.
39
+ #
40
+ # @return [CMDx::Task] The task instance
41
+ #
42
+ # @example
43
+ # result.task.id # => "users/create"
44
+ #
45
+ # @rbs @task: Task
46
+ attr_reader :task
47
+
48
+ # Returns the current execution state of the result.
49
+ #
50
+ # @return [String] One of: "initialized", "executing", "complete", "interrupted"
51
+ #
52
+ # @example
53
+ # result.state # => "complete"
54
+ #
55
+ # @rbs @state: String
56
+ attr_reader :state
57
+
58
+ # Returns the execution status of the result.
59
+ #
60
+ # @return [String] One of: "success", "skipped", "failed"
61
+ #
62
+ # @example
63
+ # result.status # => "success"
64
+ #
65
+ # @rbs @status: String
66
+ attr_reader :status
67
+
68
+ # Returns additional metadata about the result.
69
+ #
70
+ # @return [Hash{Symbol => Object}] Metadata hash
71
+ #
72
+ # @example
73
+ # result.metadata # => { duration: 1.5, retries: 2 }
74
+ #
75
+ # @rbs @metadata: Hash[Symbol, untyped]
76
+ attr_reader :metadata
77
+
78
+ # Returns the reason for interruption (skip or failure).
79
+ #
80
+ # @return [String, nil] The reason message, or nil if not interrupted
81
+ #
82
+ # @example
83
+ # result.reason # => "Validation failed"
84
+ #
85
+ # @rbs @reason: (String | nil)
86
+ attr_reader :reason
87
+
88
+ # Returns the exception that caused the interruption.
89
+ #
90
+ # @return [Exception, nil] The causing exception, or nil if not interrupted
91
+ #
92
+ # @example
93
+ # result.cause # => #<StandardError: Connection timeout>
94
+ #
95
+ # @rbs @cause: (Exception | nil)
96
+ attr_reader :cause
35
97
 
36
98
  def_delegators :task, :context, :chain, :errors
37
99
  alias ctx context
@@ -45,6 +107,8 @@ module CMDx
45
107
  # @example
46
108
  # result = CMDx::Result.new(my_task)
47
109
  # result.state # => "initialized"
110
+ #
111
+ # @rbs (Task) -> void
48
112
  def initialize(task)
49
113
  raise TypeError, "must be a CMDx::Task" unless task.is_a?(CMDx::Task)
50
114
 
@@ -62,6 +126,8 @@ module CMDx
62
126
  # @example
63
127
  # result.initialized? # => true
64
128
  # result.executing? # => false
129
+ #
130
+ # @rbs () -> bool
65
131
  define_method(:"#{s}?") { state == s }
66
132
 
67
133
  # @param block [Proc] Block to execute conditionally
@@ -75,6 +141,8 @@ module CMDx
75
141
  # @example
76
142
  # result.handle_initialized { |r| puts "Starting execution" }
77
143
  # result.handle_complete { |r| puts "Task completed" }
144
+ #
145
+ # @rbs () { (Result) -> void } -> self
78
146
  define_method(:"handle_#{s}") do |&block|
79
147
  raise ArgumentError, "block required" unless block
80
148
 
@@ -87,6 +155,8 @@ module CMDx
87
155
  #
88
156
  # @example
89
157
  # result.executed! # Transitions to complete or interrupted
158
+ #
159
+ # @rbs () -> self
90
160
  def executed!
91
161
  success? ? complete! : interrupt!
92
162
  end
@@ -95,6 +165,8 @@ module CMDx
95
165
  #
96
166
  # @example
97
167
  # result.executed? # => true if complete? || interrupted?
168
+ #
169
+ # @rbs () -> bool
98
170
  def executed?
99
171
  complete? || interrupted?
100
172
  end
@@ -109,6 +181,8 @@ module CMDx
109
181
  #
110
182
  # @example
111
183
  # result.handle_executed { |r| puts "Task finished: #{r.outcome}" }
184
+ #
185
+ # @rbs () { (Result) -> void } -> self
112
186
  def handle_executed(&)
113
187
  raise ArgumentError, "block required" unless block_given?
114
188
 
@@ -120,6 +194,8 @@ module CMDx
120
194
  #
121
195
  # @example
122
196
  # result.executing! # Transitions from initialized to executing
197
+ #
198
+ # @rbs () -> void
123
199
  def executing!
124
200
  return if executing?
125
201
 
@@ -132,6 +208,8 @@ module CMDx
132
208
  #
133
209
  # @example
134
210
  # result.complete! # Transitions from executing to complete
211
+ #
212
+ # @rbs () -> void
135
213
  def complete!
136
214
  return if complete?
137
215
 
@@ -144,6 +222,8 @@ module CMDx
144
222
  #
145
223
  # @example
146
224
  # result.interrupt! # Transitions from executing to interrupted
225
+ #
226
+ # @rbs () -> void
147
227
  def interrupt!
148
228
  return if interrupted?
149
229
 
@@ -158,6 +238,8 @@ module CMDx
158
238
  # @example
159
239
  # result.success? # => true
160
240
  # result.failed? # => false
241
+ #
242
+ # @rbs () -> bool
161
243
  define_method(:"#{s}?") { status == s }
162
244
 
163
245
  # @param block [Proc] Block to execute conditionally
@@ -171,6 +253,8 @@ module CMDx
171
253
  # @example
172
254
  # result.handle_success { |r| puts "Task succeeded" }
173
255
  # result.handle_failed { |r| puts "Task failed: #{r.reason}" }
256
+ #
257
+ # @rbs () { (Result) -> void } -> self
174
258
  define_method(:"handle_#{s}") do |&block|
175
259
  raise ArgumentError, "block required" unless block
176
260
 
@@ -183,6 +267,8 @@ module CMDx
183
267
  #
184
268
  # @example
185
269
  # result.good? # => true if !failed?
270
+ #
271
+ # @rbs () -> bool
186
272
  def good?
187
273
  !failed?
188
274
  end
@@ -198,6 +284,8 @@ module CMDx
198
284
  #
199
285
  # @example
200
286
  # result.handle_good { |r| puts "Task completed successfully" }
287
+ #
288
+ # @rbs () { (Result) -> void } -> self
201
289
  def handle_good(&)
202
290
  raise ArgumentError, "block required" unless block_given?
203
291
 
@@ -209,6 +297,8 @@ module CMDx
209
297
  #
210
298
  # @example
211
299
  # result.bad? # => true if !success?
300
+ #
301
+ # @rbs () -> bool
212
302
  def bad?
213
303
  !success?
214
304
  end
@@ -223,6 +313,8 @@ module CMDx
223
313
  #
224
314
  # @example
225
315
  # result.handle_bad { |r| puts "Task had issues: #{r.reason}" }
316
+ #
317
+ # @rbs () { (Result) -> void } -> self
226
318
  def handle_bad(&)
227
319
  raise ArgumentError, "block required" unless block_given?
228
320
 
@@ -240,6 +332,8 @@ module CMDx
240
332
  # @example
241
333
  # result.skip!("Dependencies not met", cause: dependency_error)
242
334
  # result.skip!("Already processed", halt: false)
335
+ #
336
+ # @rbs (?String? reason, halt: bool, cause: Exception?, **untyped metadata) -> void
243
337
  def skip!(reason = nil, halt: true, cause: nil, **metadata)
244
338
  return if skipped?
245
339
 
@@ -264,6 +358,8 @@ module CMDx
264
358
  # @example
265
359
  # result.fail!("Validation failed", cause: validation_error)
266
360
  # result.fail!("Network timeout", halt: false, timeout: 30)
361
+ #
362
+ # @rbs (?String? reason, halt: bool, cause: Exception?, **untyped metadata) -> void
267
363
  def fail!(reason = nil, halt: true, cause: nil, **metadata)
268
364
  return if failed?
269
365
 
@@ -283,6 +379,8 @@ module CMDx
283
379
  #
284
380
  # @example
285
381
  # result.halt! # Raises appropriate fault based on status
382
+ #
383
+ # @rbs () -> void
286
384
  def halt!
287
385
  return if success?
288
386
 
@@ -315,6 +413,8 @@ module CMDx
315
413
  # @example
316
414
  # other_result = OtherTask.execute
317
415
  # result.throw!(other_result, cause: upstream_error)
416
+ #
417
+ # @rbs (Result result, halt: bool, cause: Exception?, **untyped metadata) -> void
318
418
  def throw!(result, halt: true, cause: nil, **metadata)
319
419
  raise TypeError, "must be a CMDx::Result" unless result.is_a?(Result)
320
420
 
@@ -332,6 +432,8 @@ module CMDx
332
432
  # @example
333
433
  # cause = result.caused_failure
334
434
  # puts "Caused by: #{cause.task.id}" if cause
435
+ #
436
+ # @rbs () -> Result?
335
437
  def caused_failure
336
438
  return unless failed?
337
439
 
@@ -344,6 +446,8 @@ module CMDx
344
446
  # if result.caused_failure?
345
447
  # puts "This task caused the failure"
346
448
  # end
449
+ #
450
+ # @rbs () -> bool
347
451
  def caused_failure?
348
452
  return false unless failed?
349
453
 
@@ -355,6 +459,8 @@ module CMDx
355
459
  # @example
356
460
  # thrown = result.threw_failure
357
461
  # puts "Thrown by: #{thrown.task.id}" if thrown
462
+ #
463
+ # @rbs () -> Result?
358
464
  def threw_failure
359
465
  return unless failed?
360
466
 
@@ -369,6 +475,8 @@ module CMDx
369
475
  # if result.threw_failure?
370
476
  # puts "This task threw the failure"
371
477
  # end
478
+ #
479
+ # @rbs () -> bool
372
480
  def threw_failure?
373
481
  return false unless failed?
374
482
 
@@ -381,6 +489,8 @@ module CMDx
381
489
  # if result.thrown_failure?
382
490
  # puts "This failure was thrown from another task"
383
491
  # end
492
+ #
493
+ # @rbs () -> bool
384
494
  def thrown_failure?
385
495
  failed? && !caused_failure?
386
496
  end
@@ -390,6 +500,8 @@ module CMDx
390
500
  # @example
391
501
  # position = result.index
392
502
  # puts "Task #{position + 1} of #{chain.results.count}"
503
+ #
504
+ # @rbs () -> Integer
393
505
  def index
394
506
  chain.index(self)
395
507
  end
@@ -398,6 +510,8 @@ module CMDx
398
510
  #
399
511
  # @example
400
512
  # result.outcome # => "success" or "interrupted"
513
+ #
514
+ # @rbs () -> String
401
515
  def outcome
402
516
  initialized? || thrown_failure? ? state : status
403
517
  end
@@ -407,6 +521,8 @@ module CMDx
407
521
  # @example
408
522
  # result.to_h
409
523
  # # => {state: "complete", status: "success", outcome: "success", metadata: {}}
524
+ #
525
+ # @rbs () -> Hash[Symbol, untyped]
410
526
  def to_h
411
527
  task.to_h.merge!(
412
528
  state:,
@@ -430,6 +546,8 @@ module CMDx
430
546
  #
431
547
  # @example
432
548
  # result.to_s # => "task_id=my_task state=complete status=success"
549
+ #
550
+ # @rbs () -> String
433
551
  def to_s
434
552
  Utils::Format.to_str(to_h) do |key, value|
435
553
  case key
@@ -446,6 +564,8 @@ module CMDx
446
564
  # @example
447
565
  # state, status = result.deconstruct
448
566
  # puts "State: #{state}, Status: #{status}"
567
+ #
568
+ # @rbs (*untyped) -> Array[untyped]
449
569
  def deconstruct(*)
450
570
  [state, status, reason, cause, metadata]
451
571
  end
@@ -461,6 +581,8 @@ module CMDx
461
581
  # in {bad: true}
462
582
  # puts "Task had issues"
463
583
  # end
584
+ #
585
+ # @rbs (*untyped) -> Hash[Symbol, untyped]
464
586
  def deconstruct_keys(*)
465
587
  {
466
588
  state: state,
data/lib/cmdx/task.rb CHANGED
@@ -8,10 +8,68 @@ module CMDx
8
8
 
9
9
  extend Forwardable
10
10
 
11
- attr_reader :attributes, :errors, :id, :context, :result, :chain
11
+ # Returns the hash of processed attribute values for this task.
12
+ #
13
+ # @return [Hash{Symbol => Object}] Hash of attribute names to their values
14
+ #
15
+ # @example
16
+ # task.attributes # => { user_id: 42, user_name: "John" }
17
+ #
18
+ # @rbs @attributes: Hash[Symbol, untyped]
19
+ attr_reader :attributes
20
+
21
+ # Returns the collection of validation and execution errors.
22
+ #
23
+ # @return [Errors] The errors collection
24
+ #
25
+ # @example
26
+ # task.errors.to_h # => { email: ["must be valid"] }
27
+ #
28
+ # @rbs @errors: Errors
29
+ attr_reader :errors
30
+
31
+ # Returns the unique identifier for this task instance.
32
+ #
33
+ # @return [String] The task identifier
34
+ #
35
+ # @example
36
+ # task.id # => "abc123xyz"
37
+ #
38
+ # @rbs @id: String
39
+ attr_reader :id
40
+
41
+ # Returns the execution context for this task.
42
+ #
43
+ # @return [Context] The context instance
44
+ #
45
+ # @example
46
+ # task.context[:user_id] # => 42
47
+ #
48
+ # @rbs @context: Context
49
+ attr_reader :context
12
50
  alias ctx context
51
+
52
+ # Returns the execution result for this task.
53
+ #
54
+ # @return [Result] The result instance
55
+ #
56
+ # @example
57
+ # task.result.status # => "success"
58
+ #
59
+ # @rbs @result: Result
60
+ attr_reader :result
13
61
  alias res result
14
62
 
63
+ # Returns the execution chain containing all task results.
64
+ #
65
+ # @return [Chain] The chain instance
66
+ #
67
+ # @example
68
+ # task.chain.results.size # => 3
69
+ #
70
+ # @rbs @chain: Chain
71
+ attr_reader :chain
72
+
15
73
  def_delegators :result, :skip!, :fail!, :throw!
16
74
 
17
75
  # @param context [Hash, Context] The initial context for the task
@@ -25,6 +83,8 @@ module CMDx
25
83
  # @example
26
84
  # task = MyTask.new(name: "example", priority: :high)
27
85
  # task = MyTask.new(Context.build(name: "example"))
86
+ #
87
+ # @rbs (untyped context) -> void
28
88
  def initialize(context = {})
29
89
  Deprecator.restrict(self)
30
90
 
@@ -47,6 +107,8 @@ module CMDx
47
107
  # class MyTask < Task
48
108
  # settings deprecate: true, tags: [:experimental]
49
109
  # end
110
+ #
111
+ # @rbs (**untyped options) -> Hash[Symbol, untyped]
50
112
  def settings(**options)
51
113
  @settings ||= begin
52
114
  hash =
@@ -81,6 +143,8 @@ module CMDx
81
143
  # @example
82
144
  # register(:attribute, MyAttribute.new)
83
145
  # register(:callback, :before, -> { puts "before" })
146
+ #
147
+ # @rbs (Symbol type, untyped object, *untyped) -> void
84
148
  def register(type, object, ...)
85
149
  case type
86
150
  when :attribute then settings[:attributes].register(object, ...)
@@ -101,6 +165,8 @@ module CMDx
101
165
  # @example
102
166
  # deregister(:attribute, :name)
103
167
  # deregister(:callback, :before, MyCallback)
168
+ #
169
+ # @rbs (Symbol type, untyped object, *untyped) -> void
104
170
  def deregister(type, object, ...)
105
171
  case type
106
172
  when :attribute then settings[:attributes].deregister(object, ...)
@@ -117,6 +183,8 @@ module CMDx
117
183
  # @example
118
184
  # attributes :name, :email
119
185
  # attributes :age, type: Integer, default: 18
186
+ #
187
+ # @rbs (*untyped) -> void
120
188
  def attributes(...)
121
189
  register(:attribute, Attribute.build(...))
122
190
  end
@@ -127,6 +195,8 @@ module CMDx
127
195
  # @example
128
196
  # optional :description, :notes
129
197
  # optional :priority, type: Symbol, default: :normal
198
+ #
199
+ # @rbs (*untyped) -> void
130
200
  def optional(...)
131
201
  register(:attribute, Attribute.optional(...))
132
202
  end
@@ -136,6 +206,8 @@ module CMDx
136
206
  # @example
137
207
  # required :name, :email
138
208
  # required :age, type: Integer, min: 0
209
+ #
210
+ # @rbs (*untyped) -> void
139
211
  def required(...)
140
212
  register(:attribute, Attribute.required(...))
141
213
  end
@@ -144,6 +216,8 @@ module CMDx
144
216
  #
145
217
  # @example
146
218
  # remove_attributes :old_field, :deprecated_field
219
+ #
220
+ # @rbs (*Symbol names) -> void
147
221
  def remove_attributes(*names)
148
222
  deregister(:attribute, names)
149
223
  end
@@ -160,6 +234,8 @@ module CMDx
160
234
  # before { puts "before execution" }
161
235
  # after :cleanup, priority: :high
162
236
  # around ->(task) { task.logger.info("starting") }
237
+ #
238
+ # @rbs (*untyped callables, **untyped options) ?{ () -> void } -> void
163
239
  define_method(callback) do |*callables, **options, &block|
164
240
  register(:callback, callback, *callables, **options, &block)
165
241
  end
@@ -174,6 +250,8 @@ module CMDx
174
250
  # if result.success?
175
251
  # puts "Task completed successfully"
176
252
  # end
253
+ #
254
+ # @rbs (*untyped args, **untyped kwargs) ?{ (Result) -> void } -> Result
177
255
  def execute(*args, **kwargs)
178
256
  task = new(*args, **kwargs)
179
257
  task.execute(raise: false)
@@ -189,6 +267,8 @@ module CMDx
189
267
  # @example
190
268
  # result = MyTask.execute!(name: "example")
191
269
  # # Will raise an exception if execution fails
270
+ #
271
+ # @rbs (*untyped args, **untyped kwargs) ?{ (Result) -> void } -> Result
192
272
  def execute!(*args, **kwargs)
193
273
  task = new(*args, **kwargs)
194
274
  task.execute(raise: true)
@@ -204,6 +284,8 @@ module CMDx
204
284
  # @example
205
285
  # result = task.execute
206
286
  # result = task.execute(raise: true)
287
+ #
288
+ # @rbs (raise: bool) ?{ (Result) -> void } -> Result
207
289
  def execute(raise: false)
208
290
  Executor.execute(self, raise:)
209
291
  block_given? ? yield(result) : result
@@ -218,6 +300,8 @@ module CMDx
218
300
  # puts "Performing work..."
219
301
  # end
220
302
  # end
303
+ #
304
+ # @rbs () -> void
221
305
  def work
222
306
  raise UndefinedMethodError, "undefined method #{self.class.name}#work"
223
307
  end
@@ -227,6 +311,8 @@ module CMDx
227
311
  # @example
228
312
  # logger.info "Starting task execution"
229
313
  # logger.error "Task failed", error: exception
314
+ #
315
+ # @rbs () -> Logger
230
316
  def logger
231
317
  @logger ||= begin
232
318
  logger = self.class.settings[:logger] || CMDx.configuration.logger
@@ -249,6 +335,8 @@ module CMDx
249
335
  # task_hash = task.to_h
250
336
  # puts "Task type: #{task_hash[:type]}"
251
337
  # puts "Task tags: #{task_hash[:tags].join(', ')}"
338
+ #
339
+ # @rbs () -> Hash[Symbol, untyped]
252
340
  def to_h
253
341
  {
254
342
  index: result.index,
@@ -265,6 +353,8 @@ module CMDx
265
353
  # @example
266
354
  # puts task.to_s
267
355
  # # Output: "Task[MyTask] tags: [:important] id: abc123"
356
+ #
357
+ # @rbs () -> String
268
358
  def to_s
269
359
  Utils::Format.to_str(to_h)
270
360
  end
@@ -32,6 +32,8 @@ module CMDx
32
32
  # @example Invoking a callable object
33
33
  # callable = MyCallable.new
34
34
  # Call.invoke(user, callable, 'data')
35
+ #
36
+ # @rbs (untyped target, (Symbol | Proc | untyped) callable, *untyped args, **untyped kwargs) ?{ () -> untyped } -> untyped
35
37
  def invoke(target, callable, *args, **kwargs, &)
36
38
  if callable.is_a?(Symbol)
37
39
  target.send(callable, *args, **kwargs, &)
@@ -11,6 +11,7 @@ module CMDx
11
11
 
12
12
  extend self
13
13
 
14
+ # @rbs EVAL: Proc
14
15
  EVAL = proc do |target, callable, *args, **kwargs, &block|
15
16
  case callable
16
17
  when NilClass, FalseClass, TrueClass then !!callable
@@ -53,6 +54,8 @@ module CMDx
53
54
  # @example With arguments and block
54
55
  # Condition.evaluate(user, if: ->(u) { u.has_permission?(:admin) }, :admin)
55
56
  # # => true if the proc returns true when called with user and :admin
57
+ #
58
+ # @rbs (untyped target, Hash[Symbol, untyped] options, *untyped) ?{ () -> untyped } -> bool
56
59
  def evaluate(target, options, ...)
57
60
  case options
58
61
  in if: if_cond, unless: unless_cond
@@ -8,6 +8,7 @@ module CMDx
8
8
 
9
9
  extend self
10
10
 
11
+ # @rbs FORMATTER: Proc
11
12
  FORMATTER = proc do |key, value|
12
13
  "#{key}=#{value.inspect}"
13
14
  end.freeze
@@ -28,6 +29,8 @@ module CMDx
28
29
  # @example CMDx object
29
30
  # Format.to_log(CMDx::Task.new(name: "task1"))
30
31
  # # => {name: "task1"}
32
+ #
33
+ # @rbs (untyped message) -> untyped
31
34
  def to_log(message)
32
35
  if message.respond_to?(:to_h) && message.class.ancestors.any? { |a| a.to_s.start_with?("CMDx") }
33
36
  message.to_h
@@ -51,6 +54,8 @@ module CMDx
51
54
  # @example Custom formatter
52
55
  # Format.to_str({count: 5, total: 100}) { |k, v| "#{k}:#{v}" }
53
56
  # # => "count:5 total:100"
57
+ #
58
+ # @rbs (Hash[untyped, untyped] hash) ?{ (untyped, untyped) -> String } -> String
54
59
  def to_str(hash, &block)
55
60
  block ||= FORMATTER
56
61
  hash.map(&block).join(" ")
@@ -7,6 +7,14 @@ module CMDx
7
7
 
8
8
  extend Forwardable
9
9
 
10
+ # Returns the internal registry mapping validator types to classes.
11
+ #
12
+ # @return [Hash{Symbol => Class}] Hash of validator type names to validator classes
13
+ #
14
+ # @example
15
+ # registry.registry # => { presence: Validators::Presence, format: Validators::Format }
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
  # @param registry [Hash, nil] Optional hash mapping validator names to validator classes
18
26
  #
19
27
  # @return [ValidatorRegistry] A new validator registry instance
28
+ #
29
+ # @rbs (?Hash[Symbol, Class]? registry) -> void
20
30
  def initialize(registry = nil)
21
31
  @registry = registry || {
22
32
  exclusion: Validators::Exclusion,
@@ -31,6 +41,8 @@ module CMDx
31
41
  # Create a duplicate of the registry with copied internal state.
32
42
  #
33
43
  # @return [ValidatorRegistry] A new validator registry with duplicated registry hash
44
+ #
45
+ # @rbs () -> ValidatorRegistry
34
46
  def dup
35
47
  self.class.new(registry.dup)
36
48
  end
@@ -45,6 +57,8 @@ module CMDx
45
57
  # @example
46
58
  # registry.register(:custom, CustomValidator)
47
59
  # registry.register("email", EmailValidator)
60
+ #
61
+ # @rbs ((String | Symbol) name, Class validator) -> self
48
62
  def register(name, validator)
49
63
  registry[name.to_sym] = validator
50
64
  self
@@ -59,6 +73,8 @@ module CMDx
59
73
  # @example
60
74
  # registry.deregister(:format)
61
75
  # registry.deregister("presence")
76
+ #
77
+ # @rbs ((String | Symbol) name) -> self
62
78
  def deregister(name)
63
79
  registry.delete(name.to_sym)
64
80
  self
@@ -77,6 +93,8 @@ module CMDx
77
93
  # @example
78
94
  # registry.validate(:presence, task, user.name, presence: true)
79
95
  # registry.validate(:length, task, password, { min: 8, allow_nil: false })
96
+ #
97
+ # @rbs (Symbol type, Task task, untyped value, untyped options) -> untyped
80
98
  def validate(type, task, value, options = {})
81
99
  raise TypeError, "unknown validator type #{type.inspect}" unless registry.key?(type)
82
100
 
@@ -32,6 +32,8 @@ module CMDx
32
32
  # # => raises ValidationError if value is 5 (within 1..10)
33
33
  # @example Exclude with custom message
34
34
  # Exclusion.call("test", in: ["test", "demo"], message: "value %{values} is forbidden")
35
+ #
36
+ # @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
35
37
  def call(value, options = {})
36
38
  values = options[:in] || options[:within]
37
39
 
@@ -38,6 +38,8 @@ module CMDx
38
38
  # @example Validate with custom message
39
39
  # Format.call("invalid", with: /\A\d+\z/, message: "Must contain only digits")
40
40
  # # => raises ValidationError with custom message
41
+ #
42
+ # @rbs (untyped value, (Hash[Symbol, untyped] | Regexp) options) -> nil
41
43
  def call(value, options = {})
42
44
  match =
43
45
  if options.is_a?(Regexp)
@@ -34,6 +34,8 @@ module CMDx
34
34
  # # => nil (validation passes - 5 is within 1..10)
35
35
  # @example Include with custom message
36
36
  # Inclusion.call("test", in: ["admin", "user"], message: "must be one of: %{values}")
37
+ #
38
+ # @rbs (untyped value, Hash[Symbol, untyped] options) -> nil
37
39
  def call(value, options = {})
38
40
  values = options[:in] || options[:within]
39
41