cmdx 1.9.0 → 1.10.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/llms.md +3 -13
  3. data/.cursor/prompts/yardoc.md +1 -0
  4. data/CHANGELOG.md +16 -0
  5. data/LLM.md +436 -374
  6. data/README.md +7 -2
  7. data/docs/basics/setup.md +17 -0
  8. data/docs/callbacks.md +1 -1
  9. data/docs/getting_started.md +22 -2
  10. data/docs/index.md +13 -1
  11. data/docs/retries.md +121 -0
  12. data/docs/tips_and_tricks.md +2 -1
  13. data/examples/stoplight_circuit_breaker.md +36 -0
  14. data/lib/cmdx/attribute.rb +82 -1
  15. data/lib/cmdx/attribute_registry.rb +20 -0
  16. data/lib/cmdx/attribute_value.rb +25 -0
  17. data/lib/cmdx/callback_registry.rb +19 -0
  18. data/lib/cmdx/chain.rb +34 -1
  19. data/lib/cmdx/coercion_registry.rb +18 -0
  20. data/lib/cmdx/coercions/array.rb +2 -0
  21. data/lib/cmdx/coercions/big_decimal.rb +3 -0
  22. data/lib/cmdx/coercions/boolean.rb +5 -0
  23. data/lib/cmdx/coercions/complex.rb +2 -0
  24. data/lib/cmdx/coercions/date.rb +4 -0
  25. data/lib/cmdx/coercions/date_time.rb +5 -0
  26. data/lib/cmdx/coercions/float.rb +2 -0
  27. data/lib/cmdx/coercions/hash.rb +2 -0
  28. data/lib/cmdx/coercions/integer.rb +2 -0
  29. data/lib/cmdx/coercions/rational.rb +2 -0
  30. data/lib/cmdx/coercions/string.rb +2 -0
  31. data/lib/cmdx/coercions/symbol.rb +2 -0
  32. data/lib/cmdx/coercions/time.rb +5 -0
  33. data/lib/cmdx/configuration.rb +126 -3
  34. data/lib/cmdx/context.rb +36 -0
  35. data/lib/cmdx/deprecator.rb +3 -0
  36. data/lib/cmdx/errors.rb +22 -0
  37. data/lib/cmdx/executor.rb +71 -11
  38. data/lib/cmdx/faults.rb +14 -0
  39. data/lib/cmdx/identifier.rb +2 -0
  40. data/lib/cmdx/locale.rb +3 -0
  41. data/lib/cmdx/log_formatters/json.rb +2 -0
  42. data/lib/cmdx/log_formatters/key_value.rb +2 -0
  43. data/lib/cmdx/log_formatters/line.rb +2 -0
  44. data/lib/cmdx/log_formatters/logstash.rb +2 -0
  45. data/lib/cmdx/log_formatters/raw.rb +2 -0
  46. data/lib/cmdx/middleware_registry.rb +20 -0
  47. data/lib/cmdx/middlewares/correlate.rb +11 -0
  48. data/lib/cmdx/middlewares/runtime.rb +4 -0
  49. data/lib/cmdx/middlewares/timeout.rb +4 -0
  50. data/lib/cmdx/pipeline.rb +20 -1
  51. data/lib/cmdx/railtie.rb +4 -0
  52. data/lib/cmdx/result.rb +123 -1
  53. data/lib/cmdx/task.rb +91 -1
  54. data/lib/cmdx/utils/call.rb +2 -0
  55. data/lib/cmdx/utils/condition.rb +3 -0
  56. data/lib/cmdx/utils/format.rb +5 -0
  57. data/lib/cmdx/validator_registry.rb +18 -0
  58. data/lib/cmdx/validators/exclusion.rb +2 -0
  59. data/lib/cmdx/validators/format.rb +2 -0
  60. data/lib/cmdx/validators/inclusion.rb +2 -0
  61. data/lib/cmdx/validators/length.rb +14 -0
  62. data/lib/cmdx/validators/numeric.rb +14 -0
  63. data/lib/cmdx/validators/presence.rb +2 -0
  64. data/lib/cmdx/version.rb +4 -1
  65. data/lib/cmdx/workflow.rb +10 -0
  66. data/lib/cmdx.rb +8 -0
  67. data/lib/generators/cmdx/locale_generator.rb +0 -1
  68. data/mkdocs.yml +3 -1
  69. metadata +3 -1
@@ -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)
@@ -10,6 +10,7 @@ module CMDx
10
10
 
11
11
  extend self
12
12
 
13
+ # @rbs DEFAULT_PRECISION: Integer
13
14
  DEFAULT_PRECISION = 14
14
15
 
15
16
  # Converts a value to a BigDecimal
@@ -28,6 +29,8 @@ module CMDx
28
29
  # @example Convert other numeric types
29
30
  # BigDecimal.call(42) # => #<BigDecimal:7f8b8c0d8e0f '0.42E2',9(18)>
30
31
  # BigDecimal.call(3.14159) # => #<BigDecimal:7f8b8c0d8e0f '0.314159E1',9(18)>
32
+ #
33
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> BigDecimal
31
34
  def call(value, options = {})
32
35
  BigDecimal(value, options[:precision] || DEFAULT_PRECISION)
33
36
  rescue ArgumentError, TypeError
@@ -10,7 +10,10 @@ module CMDx
10
10
 
11
11
  extend self
12
12
 
13
+ # @rbs FALSEY: Regexp
13
14
  FALSEY = /^(false|f|no|n|0)$/i
15
+
16
+ # @rbs TRUTHY: Regexp
14
17
  TRUTHY = /^(true|t|yes|y|1)$/i
15
18
 
16
19
  # Converts a value to a Boolean
@@ -34,6 +37,8 @@ module CMDx
34
37
  # @example Handle case-insensitive input
35
38
  # Boolean.call("TRUE") # => true
36
39
  # Boolean.call("False") # => false
40
+ #
41
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> bool
37
42
  def call(value, options = {})
38
43
  case value.to_s.downcase
39
44
  when FALSEY then false
@@ -26,6 +26,8 @@ module CMDx
26
26
  # Complex.call(5) # => (5+0i)
27
27
  # Complex.call(3.14) # => (3.14+0i)
28
28
  # Complex.call(Complex(1, 2)) # => (1+2i)
29
+ #
30
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Complex
29
31
  def call(value, options = {})
30
32
  Complex(value)
31
33
  rescue ArgumentError, TypeError
@@ -12,6 +12,8 @@ module CMDx
12
12
  extend self
13
13
 
14
14
  # Types that are already date-like and don't need conversion
15
+ #
16
+ # @rbs ANALOG_TYPES: Array[String]
15
17
  ANALOG_TYPES = %w[Date DateTime Time].freeze
16
18
 
17
19
  # Converts a value to a Date object
@@ -33,6 +35,8 @@ module CMDx
33
35
  # @example Return existing Date objects unchanged
34
36
  # Date.call(Date.new(2023, 12, 25)) # => #<Date: 2023-12-25>
35
37
  # Date.call(DateTime.new(2023, 12, 25)) # => #<Date: 2023-12-25>
38
+ #
39
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Date
36
40
  def call(value, options = {})
37
41
  return value if ANALOG_TYPES.include?(value.class.name)
38
42
  return ::Date.strptime(value, options[:strptime]) if options[:strptime]
@@ -11,6 +11,9 @@ module CMDx
11
11
 
12
12
  extend self
13
13
 
14
+ # Types that are already date-time-like and don't need conversion
15
+ #
16
+ # @rbs ANALOG_TYPES: Array[String]
14
17
  ANALOG_TYPES = %w[Date DateTime Time].freeze
15
18
 
16
19
  # Converts a value to a DateTime
@@ -32,6 +35,8 @@ module CMDx
32
35
  # @example Convert existing date objects
33
36
  # DateTime.call(Date.new(2023, 12, 25)) # => #<DateTime: 2023-12-25T00:00:00+00:00>
34
37
  # DateTime.call(Time.new(2023, 12, 25)) # => #<DateTime: 2023-12-25T00:00:00+00:00>
38
+ #
39
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> DateTime
35
40
  def call(value, options = {})
36
41
  return value if ANALOG_TYPES.include?(value.class.name)
37
42
  return ::DateTime.strptime(value, options[:strptime]) if options[:strptime]
@@ -30,6 +30,8 @@ module CMDx
30
30
  # Float.call(BigDecimal("123.456")) # => 123.456
31
31
  # Float.call(Rational(3, 4)) # => 0.75
32
32
  # Float.call(Complex(5.0, 0)) # => 5.0
33
+ #
34
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Float
33
35
  def call(value, options = {})
34
36
  Float(value)
35
37
  rescue ArgumentError, RangeError, TypeError
@@ -30,6 +30,8 @@ module CMDx
30
30
  # Hash.call([:a, 1, :b, 2]) # => {a: 1, b: 2}
31
31
  # @example Coerce from JSON string
32
32
  # Hash.call('{"key": "value"}') # => {"key" => "value"}
33
+ #
34
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Hash[untyped, untyped]
33
35
  def call(value, options = {})
34
36
  if value.nil?
35
37
  {}
@@ -34,6 +34,8 @@ module CMDx
34
34
  # Integer.call(nil) # => 0
35
35
  # Integer.call(false) # => 0
36
36
  # Integer.call(true) # => 1
37
+ #
38
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Integer
37
39
  def call(value, options = {})
38
40
  Integer(value)
39
41
  rescue ArgumentError, FloatDomainError, RangeError, TypeError
@@ -33,6 +33,8 @@ module CMDx
33
33
  # Rational.call("") # => (0/1)
34
34
  # Rational.call(nil) # => (0/1)
35
35
  # Rational.call(0) # => (0/1)
36
+ #
37
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Rational
36
38
  def call(value, options = {})
37
39
  Rational(value)
38
40
  rescue ArgumentError, FloatDomainError, RangeError, TypeError, ZeroDivisionError
@@ -26,6 +26,8 @@ module CMDx
26
26
  # String.call([1, 2, 3]) # => "[1, 2, 3]"
27
27
  # String.call(nil) # => ""
28
28
  # String.call(true) # => "true"
29
+ #
30
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> String
29
31
  def call(value, options = {})
30
32
  String(value)
31
33
  end
@@ -25,6 +25,8 @@ module CMDx
25
25
  # Symbol.call("user_id") # => :user_id
26
26
  # Symbol.call("") # => :""
27
27
  # Symbol.call(:existing) # => :existing
28
+ #
29
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Symbol
28
30
  def call(value, options = {})
29
31
  value.to_sym
30
32
  rescue NoMethodError
@@ -11,6 +11,9 @@ module CMDx
11
11
 
12
12
  extend self
13
13
 
14
+ # Types that are already time-like and don't need conversion
15
+ #
16
+ # @rbs ANALOG_TYPES: Array[String]
14
17
  ANALOG_TYPES = %w[DateTime Time].freeze
15
18
 
16
19
  # Converts a value to a Time object
@@ -34,6 +37,8 @@ module CMDx
34
37
  # @example Convert strings with custom format
35
38
  # Time.call("25/12/2023", strptime: "%d/%m/%Y") # => Time object
36
39
  # Time.call("12-25-2023", strptime: "%m-%d-%Y") # => Time object
40
+ #
41
+ # @rbs (untyped value, ?Hash[Symbol, untyped] options) -> Time
37
42
  def call(value, options = {})
38
43
  return value if ANALOG_TYPES.include?(value.class.name)
39
44
  return value.to_time if value.respond_to?(:to_time)
@@ -6,11 +6,122 @@ module CMDx
6
6
  # callbacks, coercions, validators, breakpoints, backtraces, and logging.
7
7
  class Configuration
8
8
 
9
+ # @rbs DEFAULT_BREAKPOINTS: Array[String]
9
10
  DEFAULT_BREAKPOINTS = %w[failed].freeze
10
11
 
11
- attr_accessor :middlewares, :callbacks, :coercions, :validators,
12
- :task_breakpoints, :workflow_breakpoints, :logger,
13
- :backtrace, :backtrace_cleaner, :exception_handler
12
+ # @rbs DEFAULT_ROLLPOINTS: Array[String]
13
+ DEFAULT_ROLLPOINTS = %w[failed].freeze
14
+
15
+ # Returns the middleware registry for task execution.
16
+ #
17
+ # @return [MiddlewareRegistry] The middleware registry
18
+ #
19
+ # @example
20
+ # config.middlewares.register(CustomMiddleware)
21
+ #
22
+ # @rbs @middlewares: MiddlewareRegistry
23
+ attr_accessor :middlewares
24
+
25
+ # Returns the callback registry for task lifecycle hooks.
26
+ #
27
+ # @return [CallbackRegistry] The callback registry
28
+ #
29
+ # @example
30
+ # config.callbacks.register(:before_execution, :log_start)
31
+ #
32
+ # @rbs @callbacks: CallbackRegistry
33
+ attr_accessor :callbacks
34
+
35
+ # Returns the coercion registry for type conversions.
36
+ #
37
+ # @return [CoercionRegistry] The coercion registry
38
+ #
39
+ # @example
40
+ # config.coercions.register(:custom, CustomCoercion)
41
+ #
42
+ # @rbs @coercions: CoercionRegistry
43
+ attr_accessor :coercions
44
+
45
+ # Returns the validator registry for attribute validation.
46
+ #
47
+ # @return [ValidatorRegistry] The validator registry
48
+ #
49
+ # @example
50
+ # config.validators.register(:email, EmailValidator)
51
+ #
52
+ # @rbs @validators: ValidatorRegistry
53
+ attr_accessor :validators
54
+
55
+ # Returns the breakpoint statuses for task execution interruption.
56
+ #
57
+ # @return [Array<String>] Array of status names that trigger breakpoints
58
+ #
59
+ # @example
60
+ # config.task_breakpoints = ["failed", "skipped"]
61
+ #
62
+ # @rbs @task_breakpoints: Array[String]
63
+ attr_accessor :task_breakpoints
64
+
65
+ # Returns the breakpoint statuses for workflow execution interruption.
66
+ #
67
+ # @return [Array<String>] Array of status names that trigger breakpoints
68
+ #
69
+ # @example
70
+ # config.workflow_breakpoints = ["failed", "skipped"]
71
+ #
72
+ # @rbs @task_breakpoints: Array[String]
73
+ # @rbs @workflow_breakpoints: Array[String]
74
+ attr_accessor :workflow_breakpoints
75
+
76
+ # Returns the logger instance for CMDx operations.
77
+ #
78
+ # @return [Logger] The logger instance
79
+ #
80
+ # @example
81
+ # config.logger.level = Logger::DEBUG
82
+ #
83
+ # @rbs @logger: Logger
84
+ attr_accessor :logger
85
+
86
+ # Returns whether to log backtraces for failed tasks.
87
+ #
88
+ # @return [Boolean] true if backtraces should be logged
89
+ #
90
+ # @example
91
+ # config.backtrace = true
92
+ #
93
+ # @rbs @backtrace: bool
94
+ attr_accessor :backtrace
95
+
96
+ # Returns the proc used to clean backtraces before logging.
97
+ #
98
+ # @return [Proc, nil] The backtrace cleaner proc, or nil if not set
99
+ #
100
+ # @example
101
+ # config.backtrace_cleaner = ->(bt) { bt.first(5) }
102
+ #
103
+ # @rbs @backtrace_cleaner: (Proc | nil)
104
+ attr_accessor :backtrace_cleaner
105
+
106
+ # Returns the proc called when exceptions occur during execution.
107
+ #
108
+ # @return [Proc, nil] The exception handler proc, or nil if not set
109
+ #
110
+ # @example
111
+ # config.exception_handler = ->(task, error) { Sentry.capture_exception(error) }
112
+ #
113
+ # @rbs @exception_handler: (Proc | nil)
114
+ attr_accessor :exception_handler
115
+
116
+ # Returns the statuses that trigger a task execution rollback.
117
+ #
118
+ # @return [Array<String>] Array of status names that trigger rollback
119
+ #
120
+ # @example
121
+ # config.rollback_on = ["failed", "skipped"]
122
+ #
123
+ # @rbs @rollback_on: Array[String]
124
+ attr_accessor :rollback_on
14
125
 
15
126
  # Initializes a new Configuration instance with default values.
16
127
  #
@@ -23,6 +134,8 @@ module CMDx
23
134
  # config = Configuration.new
24
135
  # config.middlewares.class # => MiddlewareRegistry
25
136
  # config.task_breakpoints # => ["failed"]
137
+ #
138
+ # @rbs () -> void
26
139
  def initialize
27
140
  @middlewares = MiddlewareRegistry.new
28
141
  @callbacks = CallbackRegistry.new
@@ -31,6 +144,7 @@ module CMDx
31
144
 
32
145
  @task_breakpoints = DEFAULT_BREAKPOINTS
33
146
  @workflow_breakpoints = DEFAULT_BREAKPOINTS
147
+ @rollback_on = DEFAULT_ROLLPOINTS
34
148
 
35
149
  @backtrace = false
36
150
  @backtrace_cleaner = nil
@@ -52,6 +166,8 @@ module CMDx
52
166
  # config = Configuration.new
53
167
  # config.to_h
54
168
  # # => { middlewares: #<MiddlewareRegistry>, callbacks: #<CallbackRegistry>, ... }
169
+ #
170
+ # @rbs () -> Hash[Symbol, untyped]
55
171
  def to_h
56
172
  {
57
173
  middlewares: @middlewares,
@@ -60,6 +176,7 @@ module CMDx
60
176
  validators: @validators,
61
177
  task_breakpoints: @task_breakpoints,
62
178
  workflow_breakpoints: @workflow_breakpoints,
179
+ rollback_on: @rollback_on,
63
180
  backtrace: @backtrace,
64
181
  backtrace_cleaner: @backtrace_cleaner,
65
182
  exception_handler: @exception_handler,
@@ -78,6 +195,8 @@ module CMDx
78
195
  # @example
79
196
  # config = CMDx.configuration
80
197
  # config.middlewares # => #<MiddlewareRegistry>
198
+ #
199
+ # @rbs () -> Configuration
81
200
  def configuration
82
201
  return @configuration if @configuration
83
202
 
@@ -99,6 +218,8 @@ module CMDx
99
218
  # config.task_breakpoints = ["failed", "skipped"]
100
219
  # config.logger.level = Logger::DEBUG
101
220
  # end
221
+ #
222
+ # @rbs () { (Configuration) -> void } -> Configuration
102
223
  def configure
103
224
  raise ArgumentError, "block required" unless block_given?
104
225
 
@@ -114,6 +235,8 @@ module CMDx
114
235
  # @example
115
236
  # CMDx.reset_configuration!
116
237
  # # Configuration is now reset to defaults
238
+ #
239
+ # @rbs () -> Configuration
117
240
  def reset_configuration!
118
241
  @configuration = Configuration.new
119
242
  end