senro_usecaser 0.3.0 → 0.4.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.
@@ -89,30 +89,14 @@ module SenroUsecaser
89
89
  # organize StepA, StepB
90
90
  # end
91
91
  class Base
92
- # Declares a dependency to be injected from the container
93
- #
94
- # : (Symbol, ?Class) -> void
95
- def self.depends_on: (Symbol, ?Class) -> void
92
+ extend DependsOn
96
93
 
97
- # Returns the list of declared dependencies
98
- #
99
- # : () -> Array[Symbol]
100
- def self.dependencies: () -> Array[Symbol]
94
+ include DependsOn::InstanceMethods
101
95
 
102
- # Returns the dependency type mapping
103
- #
104
- # : () -> Hash[Symbol, Class]
105
- def self.dependency_types: () -> Hash[Symbol, Class]
106
-
107
- # Sets the namespace for dependency resolution
108
- #
109
- # : ((Symbol | String)) -> void
110
- def self.namespace: (Symbol | String) -> void
111
-
112
- # Returns the declared namespace
96
+ # Alias for backward compatibility
113
97
  #
114
98
  # : () -> (Symbol | String)?
115
- attr_reader use_case_namespace: untyped
99
+ alias self.use_case_namespace self.declared_namespace
116
100
 
117
101
  # Declares a sequence of UseCases to execute as a pipeline
118
102
  #
@@ -188,6 +172,73 @@ module SenroUsecaser
188
172
  # : () -> Array[Proc]
189
173
  def self.around_hooks: () -> Array[Proc]
190
174
 
175
+ # Adds an on_failure hook
176
+ #
177
+ # : () { (untyped, Result[untyped], ?RetryContext?) -> void } -> void
178
+ def self.on_failure: () { (untyped, Result[untyped], ?RetryContext?) -> void } -> void
179
+
180
+ # Returns the list of on_failure hooks
181
+ #
182
+ # : () -> Array[Proc]
183
+ def self.on_failure_hooks: () -> Array[Proc]
184
+
185
+ # Configures automatic retry for specific error types
186
+ #
187
+ # @example Retry on network errors
188
+ # retry_on :network_error, attempts: 3, wait: 1
189
+ #
190
+ # @example Retry on exception class
191
+ # retry_on Net::OpenTimeout, attempts: 5, wait: 2, backoff: :exponential
192
+ #
193
+ # @example Multiple error types with jitter
194
+ # retry_on :rate_limited, :timeout, attempts: 3, wait: 1, jitter: 0.1
195
+ #
196
+ # rubocop:disable Metrics/ParameterLists
197
+ # : (*(Symbol | Class), ?attempts: Integer, ?wait: (Float | Integer),
198
+ # : ?backoff: Symbol, ?max_wait: (Float | Integer)?, ?jitter: (Float | Integer)) -> void
199
+ def self.retry_on: (*untyped error_matchers, ?attempts: untyped, ?wait: untyped, ?backoff: untyped, ?max_wait: untyped, ?jitter: untyped) -> untyped
200
+
201
+ # Returns the list of retry configurations
202
+ #
203
+ # : () -> Array[RetryConfiguration]
204
+ def self.retry_configurations: () -> Array[RetryConfiguration]
205
+
206
+ # Configures errors that should immediately discard (no retry)
207
+ #
208
+ # @example Discard on validation errors
209
+ # discard_on :validation_error, :not_found
210
+ #
211
+ # @example Discard on exception class
212
+ # discard_on ArgumentError
213
+ #
214
+ # : (*(Symbol | Class)) -> void
215
+ def self.discard_on: (*Symbol | Class) -> void
216
+
217
+ # Returns the list of discard matchers
218
+ #
219
+ # : () -> Array[(Symbol | Class)]
220
+ def self.discard_matchers: () -> Array[Symbol | Class]
221
+
222
+ # Adds a before_retry hook
223
+ #
224
+ # : () { (untyped, Result[untyped], RetryContext) -> void } -> void
225
+ def self.before_retry: () { (untyped, Result[untyped], RetryContext) -> void } -> void
226
+
227
+ # Returns the list of before_retry hooks
228
+ #
229
+ # : () -> Array[Proc]
230
+ def self.before_retry_hooks: () -> Array[Proc]
231
+
232
+ # Adds an after_retries_exhausted hook
233
+ #
234
+ # : () { (untyped, Result[untyped], RetryContext) -> void } -> void
235
+ def self.after_retries_exhausted: () { (untyped, Result[untyped], RetryContext) -> void } -> void
236
+
237
+ # Returns the list of after_retries_exhausted hooks
238
+ #
239
+ # : () -> Array[Proc]
240
+ def self.after_retries_exhausted_hooks: () -> Array[Proc]
241
+
191
242
  # Declares the expected input type(s) for this UseCase
192
243
  # Accepts a Class or one or more Modules that input must include
193
244
  #
@@ -261,6 +312,18 @@ module SenroUsecaser
261
312
  # : (?untyped input) -> Result[untyped]
262
313
  def call: (?untyped input) -> Result[untyped]
263
314
 
315
+ # Represents a record of a step execution in a pipeline
316
+ class StepExecutionRecord < Struct[untyped]
317
+ attr_accessor step(): untyped
318
+
319
+ attr_accessor input(): untyped
320
+
321
+ attr_accessor result(): untyped
322
+
323
+ def self.new: (?step: untyped, ?input: untyped, ?result: untyped) -> instance
324
+ | ({ ?step: untyped, ?input: untyped, ?result: untyped }) -> instance
325
+ end
326
+
264
327
  private
265
328
 
266
329
  # Creates a success Result with the given value
@@ -301,6 +364,36 @@ module SenroUsecaser
301
364
  # : (untyped) { () -> Result[untyped] } -> Result[untyped]
302
365
  def execute_with_hooks: (untyped) { () -> Result[untyped] } -> Result[untyped]
303
366
 
367
+ # Executes the UseCase with retry support
368
+ #
369
+ # : (untyped) -> Result[untyped]
370
+ def execute_with_retry: (untyped) -> Result[untyped]
371
+
372
+ # Builds a retry context with max attempts from configurations
373
+ #
374
+ # : () -> RetryContext
375
+ def build_retry_context: () -> RetryContext
376
+
377
+ # Finds a retry configuration that matches the result
378
+ #
379
+ # : (Result[untyped]) -> RetryConfiguration?
380
+ def find_matching_retry_config: (Result[untyped]) -> RetryConfiguration?
381
+
382
+ # Checks if the result should be discarded (no retry)
383
+ #
384
+ # : (Result[untyped]) -> bool
385
+ def should_discard?: (Result[untyped]) -> bool
386
+
387
+ # Runs before_retry hooks
388
+ #
389
+ # : (untyped, Result[untyped], RetryContext) -> void
390
+ def run_before_retry_hooks: (untyped, Result[untyped], RetryContext) -> void
391
+
392
+ # Runs after_retries_exhausted hooks
393
+ #
394
+ # : (untyped, Result[untyped], RetryContext) -> void
395
+ def run_after_retries_exhausted_hooks: (untyped, Result[untyped], RetryContext) -> void
396
+
304
397
  # Wraps a non-Result value in Result.success
305
398
  #
306
399
  # : (untyped) -> Result[untyped]
@@ -336,6 +429,16 @@ module SenroUsecaser
336
429
  # : (untyped, Result[untyped]) -> void
337
430
  def run_after_hooks: (untyped, Result[untyped]) -> void
338
431
 
432
+ # Runs all on_failure hooks when result is a failure
433
+ #
434
+ # : (untyped, Result[untyped], ?RetryContext?) -> void
435
+ def run_on_failure_hooks: (untyped, Result[untyped], ?RetryContext?) -> void
436
+
437
+ # Calls an on_failure hook with appropriate arguments
438
+ #
439
+ # : (untyped, Symbol, untyped, Result[untyped], RetryContext?) -> void
440
+ def call_on_failure_hook: (untyped, Symbol, untyped, Result[untyped], RetryContext?) -> void
441
+
339
442
  # Returns instantiated hook objects
340
443
  #
341
444
  # : () -> Array[Hook]
@@ -352,20 +455,11 @@ module SenroUsecaser
352
455
  def resolve_dependencies: (Container, Hash[Symbol, untyped]) -> void
353
456
 
354
457
  # Resolves a single dependency from the container
458
+ # Overrides DependsOn::InstanceMethods to accept container as parameter
355
459
  #
356
460
  # : (Container, Symbol) -> untyped
357
461
  def resolve_from_container: (Container, Symbol) -> untyped
358
462
 
359
- # Returns the effective namespace for dependency resolution
360
- #
361
- # : () -> (Symbol | String)?
362
- def effective_namespace: () -> (Symbol | String)?
363
-
364
- # Infers namespace from the class's module structure
365
- #
366
- # : () -> String?
367
- def infer_namespace_from_class: () -> String?
368
-
369
463
  # Executes the organized UseCase pipeline
370
464
  #
371
465
  # : (untyped) -> Result[untyped]
@@ -408,8 +502,27 @@ module SenroUsecaser
408
502
 
409
503
  # Calls a single UseCase in the pipeline
410
504
  # Requires input type(s) to be defined for pipeline steps
505
+ # Note: on_failure hooks are not called here - they're called in pipeline rollback
411
506
  #
412
507
  # : (singleton(Base), untyped) -> Result[untyped]
413
508
  def call_use_case: (singleton(Base), untyped) -> Result[untyped]
509
+
510
+ # Performs the UseCase as a pipeline step (without on_failure hooks)
511
+ # on_failure hooks are handled by the pipeline's rollback mechanism instead
512
+ #
513
+ # : (untyped, ?capture_exceptions: bool) -> Result[untyped]
514
+ def perform_as_pipeline_step: (untyped, ?capture_exceptions: bool) -> Result[untyped]
515
+
516
+ # Executes rollback by calling on_failure hooks on executed steps in reverse order
517
+ # Unlike run_on_failure_hooks, this method calls hooks regardless of result status
518
+ # because we want to rollback even successfully completed steps when pipeline fails
519
+ #
520
+ # : (Array[StepExecutionRecord]) -> void
521
+ def execute_pipeline_rollback: (Array[StepExecutionRecord]) -> void
522
+
523
+ # Runs on_failure hooks for rollback purposes (regardless of result status)
524
+ #
525
+ # : (untyped, Result[untyped]) -> void
526
+ def run_rollback_hooks: (untyped, Result[untyped]) -> void
414
527
  end
415
528
  end
@@ -0,0 +1,197 @@
1
+ # Generated from lib/senro_usecaser/depends_on.rb with RBS::Inline
2
+
3
+ module SenroUsecaser
4
+ # Module that provides dependency injection support
5
+ #
6
+ # This module can be extended into any class to enable the full DI functionality
7
+ # similar to UseCase and Hook classes, including:
8
+ # - `depends_on` for declaring dependencies
9
+ # - `namespace` for scoped dependency resolution
10
+ # - Automatic `infer_namespace_from_module` support
11
+ # - Default `initialize` that sets up dependency injection (uses SenroUsecaser.container if not provided)
12
+ #
13
+ # @example Basic usage (no initialize needed)
14
+ # class MyService
15
+ # extend SenroUsecaser::DependsOn
16
+ #
17
+ # depends_on :logger, Logger
18
+ # depends_on :repository
19
+ #
20
+ # # No initialize needed! Default is provided automatically.
21
+ #
22
+ # def perform
23
+ # logger.info("Performing...")
24
+ # repository.find(1)
25
+ # end
26
+ # end
27
+ #
28
+ # service = MyService.new # Uses SenroUsecaser.container
29
+ # service.logger # => Logger instance
30
+ #
31
+ # @example Custom initialize with super
32
+ # class MyService
33
+ # extend SenroUsecaser::DependsOn
34
+ #
35
+ # depends_on :logger
36
+ # attr_reader :extra
37
+ #
38
+ # def initialize(extra:, container: nil)
39
+ # super(container: container) # Handles dependency resolution
40
+ # @extra = extra
41
+ # end
42
+ # end
43
+ #
44
+ # @example With explicit namespace
45
+ # class Admin::UserService
46
+ # extend SenroUsecaser::DependsOn
47
+ #
48
+ # namespace :admin
49
+ # depends_on :user_repository, UserRepository
50
+ # end
51
+ #
52
+ # @example With infer_namespace_from_module (when configured)
53
+ # # When SenroUsecaser.configuration.infer_namespace_from_module = true
54
+ # class Admin::Orders::ProcessService
55
+ # extend SenroUsecaser::DependsOn
56
+ #
57
+ # depends_on :order_repository # resolved from "admin::orders" namespace
58
+ # end
59
+ module DependsOn
60
+ # Hook called when module is extended into a class
61
+ # Automatically includes InstanceMethods
62
+ def self.extended: (untyped base) -> untyped
63
+
64
+ # Declares a dependency to be injected from the container
65
+ #
66
+ # @param name [Symbol] The name of the dependency
67
+ # @param type [Class, nil] Optional expected type for the dependency
68
+ #
69
+ # @example Basic dependency
70
+ # depends_on :logger
71
+ #
72
+ # @example Typed dependency
73
+ # depends_on :repository, UserRepository
74
+ #
75
+ # : (Symbol, ?Class) -> void
76
+ def depends_on: (Symbol, ?Class) -> void
77
+
78
+ # Returns the list of declared dependencies
79
+ #
80
+ # : () -> Array[Symbol]
81
+ def dependencies: () -> Array[Symbol]
82
+
83
+ # Returns the dependency type mapping
84
+ #
85
+ # : () -> Hash[Symbol, Class]
86
+ def dependency_types: () -> Hash[Symbol, Class]
87
+
88
+ # Sets or returns the namespace for dependency resolution
89
+ #
90
+ # @param name [Symbol, String, nil] The namespace to set
91
+ # @return [Symbol, String, nil] The current namespace when no argument given
92
+ #
93
+ # @example Setting namespace
94
+ # namespace :admin
95
+ #
96
+ # @example Getting namespace
97
+ # current_ns = namespace
98
+ #
99
+ # : (?(Symbol | String)) -> (Symbol | String)?
100
+ def namespace: (?Symbol | String) -> (Symbol | String)?
101
+
102
+ # Returns the declared namespace
103
+ #
104
+ # : () -> (Symbol | String)?
105
+ def declared_namespace: () -> (Symbol | String)?
106
+
107
+ # Copies dependency configuration to a subclass
108
+ #
109
+ # @param subclass [Class] The subclass to copy dependencies to
110
+ #
111
+ # @example In inherited hook
112
+ # def self.inherited(subclass)
113
+ # super
114
+ # copy_depends_on_to(subclass)
115
+ # end
116
+ #
117
+ # : (Class) -> void
118
+ def copy_depends_on_to: (Class) -> void
119
+
120
+ # Instance methods for dependency resolution
121
+ #
122
+ # These methods are automatically included when DependsOn is extended.
123
+ # They require @_container and @_dependencies instance variables to be set.
124
+ module InstanceMethods
125
+ # Default initialize for classes using DependsOn
126
+ #
127
+ # This provides a default initialize that sets up dependency injection.
128
+ # Classes can override this and call super to extend the behavior.
129
+ #
130
+ # @param container [Container, nil] The DI container to resolve dependencies from.
131
+ # If nil, uses SenroUsecaser.container.
132
+ #
133
+ # @example Default usage (no arguments needed)
134
+ # class MyService
135
+ # extend SenroUsecaser::DependsOn
136
+ # depends_on :logger
137
+ # end
138
+ # service = MyService.new # Uses SenroUsecaser.container
139
+ #
140
+ # @example With explicit container
141
+ # service = MyService.new(container: custom_container)
142
+ #
143
+ # @example Custom initialize with super
144
+ # class MyService
145
+ # extend SenroUsecaser::DependsOn
146
+ # depends_on :logger
147
+ # attr_reader :extra
148
+ #
149
+ # def initialize(extra:, container: nil)
150
+ # super(container: container)
151
+ # @extra = extra
152
+ # end
153
+ # end
154
+ #
155
+ # : (?container: Container?) -> void
156
+ def initialize: (?container: Container?) -> void
157
+
158
+ # Resolves all declared dependencies from the container
159
+ #
160
+ # Call this in your initialize method after setting @_container and @_dependencies.
161
+ #
162
+ # @example
163
+ # def initialize(container:)
164
+ # @_container = container
165
+ # @_dependencies = {}
166
+ # resolve_dependencies
167
+ # end
168
+ #
169
+ # : () -> void
170
+ def resolve_dependencies: () -> void
171
+
172
+ private
173
+
174
+ # Returns the effective namespace for dependency resolution
175
+ #
176
+ # Priority:
177
+ # 1. Explicitly declared namespace via `namespace :name`
178
+ # 2. Inferred namespace from module structure (if configured)
179
+ #
180
+ # : () -> (Symbol | String)?
181
+ def effective_namespace: () -> (Symbol | String)?
182
+
183
+ # Infers namespace from the class's module structure
184
+ #
185
+ # Converts CamelCase module names to snake_case and joins with "::"
186
+ # e.g., Admin::Orders::ProcessService -> "admin::orders"
187
+ #
188
+ # : () -> String?
189
+ def infer_namespace_from_class: () -> String?
190
+
191
+ # Resolves a single dependency from the container
192
+ #
193
+ # : (Symbol) -> untyped
194
+ def resolve_from_container: (Symbol) -> untyped
195
+ end
196
+ end
197
+ end
@@ -36,30 +36,14 @@ module SenroUsecaser
36
36
  # end
37
37
  # end
38
38
  class Hook
39
- # Declares a dependency to be injected from the container
40
- #
41
- # : (Symbol, ?Class) -> void
42
- def self.depends_on: (Symbol, ?Class) -> void
43
-
44
- # Returns the list of declared dependencies
45
- #
46
- # : () -> Array[Symbol]
47
- def self.dependencies: () -> Array[Symbol]
48
-
49
- # Returns the dependency type mapping
50
- #
51
- # : () -> Hash[Symbol, Class]
52
- def self.dependency_types: () -> Hash[Symbol, Class]
39
+ extend DependsOn
53
40
 
54
- # Sets or returns the namespace for dependency resolution
55
- #
56
- # : (?(Symbol | String)) -> (Symbol | String)?
57
- def self.namespace: (?Symbol | String) -> (Symbol | String)?
41
+ include DependsOn::InstanceMethods
58
42
 
59
- # Alias for namespace() without arguments
43
+ # Alias for backward compatibility
60
44
  #
61
45
  # : () -> (Symbol | String)?
62
- def self.hook_namespace: () -> (Symbol | String)?
46
+ alias self.hook_namespace self.declared_namespace
63
47
 
64
48
  # @api private
65
49
  def self.inherited: (untyped subclass) -> untyped
@@ -87,26 +71,30 @@ module SenroUsecaser
87
71
  # : (untyped) { () -> Result[untyped] } -> Result[untyped]
88
72
  def around: (untyped) { () -> Result[untyped] } -> Result[untyped]
89
73
 
74
+ # Called when the UseCase fails
75
+ # Override in subclass to add failure handling or rollback logic
76
+ #
77
+ # @example Basic logging
78
+ # def on_failure(input, result)
79
+ # logger.error("Failed: #{result.errors.first&.message}")
80
+ # end
81
+ #
82
+ # @example Request retry
83
+ # def on_failure(input, result, context)
84
+ # if result.errors.first&.code == :network_error && context.attempt < 3
85
+ # context.retry!(wait: 2.0)
86
+ # end
87
+ # end
88
+ #
89
+ # : (untyped, Result[untyped], ?RetryContext?) -> void
90
+ def on_failure: (untyped, Result[untyped], ?RetryContext?) -> void
91
+
90
92
  private
91
93
 
92
94
  # Returns the effective namespace for dependency resolution
95
+ # Overrides DependsOn::InstanceMethods to add use_case_namespace fallback
93
96
  #
94
97
  # : () -> (Symbol | String)?
95
98
  def effective_namespace: () -> (Symbol | String)?
96
-
97
- # Infers namespace from the class's module structure
98
- #
99
- # : () -> String?
100
- def infer_namespace_from_class: () -> String?
101
-
102
- # Resolves dependencies from the container
103
- #
104
- # : () -> void
105
- def resolve_dependencies: () -> void
106
-
107
- # Resolves a single dependency from the container
108
- #
109
- # : (Symbol) -> untyped
110
- def resolve_from_container: (Symbol) -> untyped
111
99
  end
112
100
  end
@@ -0,0 +1,90 @@
1
+ # Generated from lib/senro_usecaser/retry_configuration.rb with RBS::Inline
2
+
3
+ module SenroUsecaser
4
+ # Configuration for automatic retry behavior
5
+ #
6
+ # This class defines when and how retries should occur based on
7
+ # error codes or exception classes, with configurable backoff strategies.
8
+ #
9
+ # @example Basic retry configuration
10
+ # RetryConfiguration.new(
11
+ # matchers: [:network_error, Net::OpenTimeout],
12
+ # attempts: 3,
13
+ # wait: 1.0
14
+ # )
15
+ #
16
+ # @example With exponential backoff
17
+ # RetryConfiguration.new(
18
+ # matchers: [:rate_limited],
19
+ # attempts: 5,
20
+ # wait: 2.0,
21
+ # backoff: :exponential,
22
+ # max_wait: 60
23
+ # )
24
+ class RetryConfiguration
25
+ # Returns the list of error matchers (Symbols for error codes, Classes for exceptions)
26
+ # : () -> Array[(Symbol | Class)]
27
+ attr_reader matchers: untyped
28
+
29
+ # Returns the maximum number of attempts
30
+ # : () -> Integer
31
+ attr_reader attempts: untyped
32
+
33
+ # Returns the base wait time in seconds
34
+ # : () -> (Float | Integer)
35
+ attr_reader wait: untyped
36
+
37
+ # Returns the backoff strategy (:fixed, :linear, :exponential)
38
+ # : () -> Symbol
39
+ attr_reader backoff: untyped
40
+
41
+ # Returns the maximum wait time in seconds
42
+ # : () -> (Float | Integer)
43
+ attr_reader max_wait: untyped
44
+
45
+ # Returns the jitter factor (0.0 to 1.0)
46
+ # : () -> (Float | Integer)
47
+ attr_reader jitter: untyped
48
+
49
+ # Initializes a new retry configuration
50
+ #
51
+ # @param matchers [Array<Symbol, Class>] Error codes or exception classes to match
52
+ # @param attempts [Integer] Maximum number of attempts (default: 3)
53
+ # @param wait [Numeric] Base wait time in seconds (default: 0)
54
+ # @param backoff [Symbol] Backoff strategy: :fixed, :linear, or :exponential (default: :fixed)
55
+ # @param max_wait [Numeric] Maximum wait time in seconds (default: 3600)
56
+ # @param jitter [Numeric] Jitter factor 0.0-1.0 to randomize wait times (default: 0)
57
+ #
58
+ # rubocop:disable Metrics/ParameterLists
59
+ # : (matchers: Array[(Symbol | Class)], ?attempts: Integer, ?wait: (Float | Integer),
60
+ # : ?backoff: Symbol, ?max_wait: (Float | Integer)?, ?jitter: (Float | Integer)) -> void
61
+ def initialize: (matchers: untyped, ?attempts: untyped, ?wait: untyped, ?backoff: untyped, ?max_wait: untyped, ?jitter: untyped) -> untyped
62
+
63
+ # Checks if this configuration matches the given result
64
+ #
65
+ # : (Result[untyped]) -> bool
66
+ def matches?: (Result[untyped]) -> bool
67
+
68
+ # Calculates the wait time for the given attempt number
69
+ #
70
+ # : (Integer) -> Float
71
+ def calculate_wait: (Integer) -> Float
72
+
73
+ private
74
+
75
+ # Checks if an error matches any of the configured matchers
76
+ #
77
+ # : (Error) -> bool
78
+ def matches_error?: (Error) -> bool
79
+
80
+ # Calculates the base wait time based on backoff strategy
81
+ #
82
+ # : (Integer) -> (Float | Integer)
83
+ def calculate_base_wait: (Integer) -> (Float | Integer)
84
+
85
+ # Applies jitter to the wait time
86
+ #
87
+ # : ((Float | Integer)) -> Float
88
+ def apply_jitter: (Float | Integer) -> Float
89
+ end
90
+ end
@@ -0,0 +1,101 @@
1
+ # Generated from lib/senro_usecaser/retry_context.rb with RBS::Inline
2
+
3
+ module SenroUsecaser
4
+ # Represents the context of a retry operation
5
+ #
6
+ # This class tracks the state of retry attempts including:
7
+ # - Current attempt number
8
+ # - Maximum attempts allowed
9
+ # - Elapsed time since first attempt
10
+ # - Whether a retry should occur
11
+ #
12
+ # @example Basic usage in on_failure hook
13
+ # on_failure do |input, result, context|
14
+ # if context.attempt < 3
15
+ # context.retry!(wait: 1.0)
16
+ # end
17
+ # end
18
+ #
19
+ # @example With modified input
20
+ # on_failure do |input, result, context|
21
+ # if result.errors.first&.code == :rate_limited
22
+ # context.retry!(input: input.with_reduced_batch_size, wait: 5.0)
23
+ # end
24
+ # end
25
+ class RetryContext
26
+ # Returns the current attempt number (1-indexed)
27
+ # : () -> Integer
28
+ attr_reader attempt: untyped
29
+
30
+ # Returns the maximum number of attempts allowed
31
+ # : () -> Integer?
32
+ attr_reader max_attempts: untyped
33
+
34
+ # Returns the time when the first attempt started
35
+ # : () -> Time
36
+ attr_reader started_at: untyped
37
+
38
+ # Returns the error from the last failed attempt
39
+ # : () -> Error?
40
+ attr_reader last_error: untyped
41
+
42
+ # Returns the input to use for the retry (nil means use original)
43
+ # : () -> untyped
44
+ attr_reader retry_input: untyped
45
+
46
+ # Returns the wait time before retrying
47
+ # : () -> (Float | Integer)?
48
+ attr_reader retry_wait: untyped
49
+
50
+ # Initializes a new retry context
51
+ #
52
+ # : (?max_attempts: Integer?) -> void
53
+ def initialize: (?max_attempts: Integer?) -> void
54
+
55
+ # Returns true if this is a retry (attempt > 1)
56
+ #
57
+ # : () -> bool
58
+ def retried?: () -> bool
59
+
60
+ # Returns the elapsed time since the first attempt
61
+ #
62
+ # : () -> Float
63
+ def elapsed_time: () -> Float
64
+
65
+ # Returns true if max_attempts has been reached
66
+ #
67
+ # : () -> bool
68
+ def exhausted?: () -> bool
69
+
70
+ # Returns true if a retry has been requested
71
+ #
72
+ # : () -> bool
73
+ def should_retry?: () -> bool
74
+
75
+ # Requests a retry with optional modified input and wait time
76
+ #
77
+ # @example Retry with default settings
78
+ # context.retry!
79
+ #
80
+ # @example Retry with wait time
81
+ # context.retry!(wait: 2.0)
82
+ #
83
+ # @example Retry with modified input
84
+ # context.retry!(input: modified_input, wait: 1.0)
85
+ #
86
+ # : (?input: untyped, ?wait: (Float | Integer)?) -> void
87
+ def retry!: (?input: untyped, ?wait: (Float | Integer)?) -> void
88
+
89
+ # Increments the attempt counter and resets retry state
90
+ # Called internally between retry attempts
91
+ #
92
+ # : (?last_error: Error?) -> void
93
+ def increment!: (?last_error: Error?) -> void
94
+
95
+ # Resets the retry request state
96
+ # Called internally after processing retry decision
97
+ #
98
+ # : () -> void
99
+ def reset_retry_state!: () -> void
100
+ end
101
+ end
data/sig/overrides.rbs CHANGED
@@ -10,7 +10,6 @@ module SenroUsecaser
10
10
  class Base
11
11
  # Class methods that rbs-inline doesn't generate correctly
12
12
  def self.organized_steps: () -> Array[Step]?
13
- def self.use_case_namespace: () -> (Symbol | String)?
14
13
  def self.output_schema: () -> (Class | Hash[Symbol, Class])?
15
14
  end
16
15
  end