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.
- checksums.yaml +4 -4
- data/.cursor/prompts/yardoc.md +1 -0
- data/CHANGELOG.md +6 -0
- data/LLM.md +9 -0
- data/README.md +6 -1
- data/docs/getting_started.md +9 -0
- data/docs/index.md +13 -1
- data/lib/cmdx/attribute.rb +82 -1
- data/lib/cmdx/attribute_registry.rb +20 -0
- data/lib/cmdx/attribute_value.rb +25 -0
- data/lib/cmdx/callback_registry.rb +19 -0
- data/lib/cmdx/chain.rb +34 -1
- data/lib/cmdx/coercion_registry.rb +18 -0
- data/lib/cmdx/coercions/array.rb +2 -0
- data/lib/cmdx/coercions/big_decimal.rb +3 -0
- data/lib/cmdx/coercions/boolean.rb +5 -0
- data/lib/cmdx/coercions/complex.rb +2 -0
- data/lib/cmdx/coercions/date.rb +4 -0
- data/lib/cmdx/coercions/date_time.rb +5 -0
- data/lib/cmdx/coercions/float.rb +2 -0
- data/lib/cmdx/coercions/hash.rb +2 -0
- data/lib/cmdx/coercions/integer.rb +2 -0
- data/lib/cmdx/coercions/rational.rb +2 -0
- data/lib/cmdx/coercions/string.rb +2 -0
- data/lib/cmdx/coercions/symbol.rb +2 -0
- data/lib/cmdx/coercions/time.rb +5 -0
- data/lib/cmdx/configuration.rb +111 -3
- data/lib/cmdx/context.rb +36 -0
- data/lib/cmdx/deprecator.rb +3 -0
- data/lib/cmdx/errors.rb +22 -0
- data/lib/cmdx/executor.rb +43 -0
- data/lib/cmdx/faults.rb +14 -0
- data/lib/cmdx/identifier.rb +2 -0
- data/lib/cmdx/locale.rb +3 -0
- data/lib/cmdx/log_formatters/json.rb +2 -0
- data/lib/cmdx/log_formatters/key_value.rb +2 -0
- data/lib/cmdx/log_formatters/line.rb +2 -0
- data/lib/cmdx/log_formatters/logstash.rb +2 -0
- data/lib/cmdx/log_formatters/raw.rb +2 -0
- data/lib/cmdx/middleware_registry.rb +20 -0
- data/lib/cmdx/middlewares/correlate.rb +11 -0
- data/lib/cmdx/middlewares/runtime.rb +4 -0
- data/lib/cmdx/middlewares/timeout.rb +4 -0
- data/lib/cmdx/pipeline.rb +20 -1
- data/lib/cmdx/railtie.rb +4 -0
- data/lib/cmdx/result.rb +123 -1
- data/lib/cmdx/task.rb +91 -1
- data/lib/cmdx/utils/call.rb +2 -0
- data/lib/cmdx/utils/condition.rb +3 -0
- data/lib/cmdx/utils/format.rb +5 -0
- data/lib/cmdx/validator_registry.rb +18 -0
- data/lib/cmdx/validators/exclusion.rb +2 -0
- data/lib/cmdx/validators/format.rb +2 -0
- data/lib/cmdx/validators/inclusion.rb +2 -0
- data/lib/cmdx/validators/length.rb +14 -0
- data/lib/cmdx/validators/numeric.rb +14 -0
- data/lib/cmdx/validators/presence.rb +2 -0
- data/lib/cmdx/version.rb +4 -1
- data/lib/cmdx/workflow.rb +10 -0
- data/lib/cmdx.rb +8 -0
- data/lib/generators/cmdx/locale_generator.rb +0 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
data/lib/cmdx/utils/call.rb
CHANGED
|
@@ -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, &)
|
data/lib/cmdx/utils/condition.rb
CHANGED
|
@@ -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
|
data/lib/cmdx/utils/format.rb
CHANGED
|
@@ -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
|
|