tasker-rb 0.1.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 +7 -0
- data/DEVELOPMENT.md +548 -0
- data/README.md +87 -0
- data/ext/tasker_core/Cargo.lock +4720 -0
- data/ext/tasker_core/Cargo.toml +76 -0
- data/ext/tasker_core/extconf.rb +38 -0
- data/ext/tasker_core/src/CLAUDE.md +7 -0
- data/ext/tasker_core/src/bootstrap.rs +320 -0
- data/ext/tasker_core/src/bridge.rs +400 -0
- data/ext/tasker_core/src/client_ffi.rs +173 -0
- data/ext/tasker_core/src/conversions.rs +131 -0
- data/ext/tasker_core/src/diagnostics.rs +57 -0
- data/ext/tasker_core/src/event_handler.rs +179 -0
- data/ext/tasker_core/src/event_publisher_ffi.rs +239 -0
- data/ext/tasker_core/src/ffi_logging.rs +245 -0
- data/ext/tasker_core/src/global_event_system.rs +16 -0
- data/ext/tasker_core/src/in_process_event_ffi.rs +319 -0
- data/ext/tasker_core/src/lib.rs +41 -0
- data/ext/tasker_core/src/observability_ffi.rs +339 -0
- data/lib/tasker_core/batch_processing/batch_aggregation_scenario.rb +85 -0
- data/lib/tasker_core/batch_processing/batch_worker_context.rb +238 -0
- data/lib/tasker_core/bootstrap.rb +394 -0
- data/lib/tasker_core/domain_events/base_publisher.rb +220 -0
- data/lib/tasker_core/domain_events/base_subscriber.rb +178 -0
- data/lib/tasker_core/domain_events/publisher_registry.rb +253 -0
- data/lib/tasker_core/domain_events/subscriber_registry.rb +152 -0
- data/lib/tasker_core/domain_events.rb +43 -0
- data/lib/tasker_core/errors/CLAUDE.md +7 -0
- data/lib/tasker_core/errors/common.rb +305 -0
- data/lib/tasker_core/errors/error_classifier.rb +61 -0
- data/lib/tasker_core/errors.rb +4 -0
- data/lib/tasker_core/event_bridge.rb +330 -0
- data/lib/tasker_core/handlers.rb +159 -0
- data/lib/tasker_core/internal.rb +31 -0
- data/lib/tasker_core/logger.rb +234 -0
- data/lib/tasker_core/models.rb +337 -0
- data/lib/tasker_core/observability/types.rb +158 -0
- data/lib/tasker_core/observability.rb +292 -0
- data/lib/tasker_core/registry/handler_registry.rb +453 -0
- data/lib/tasker_core/registry/resolver_chain.rb +258 -0
- data/lib/tasker_core/registry/resolvers/base_resolver.rb +90 -0
- data/lib/tasker_core/registry/resolvers/class_constant_resolver.rb +156 -0
- data/lib/tasker_core/registry/resolvers/explicit_mapping_resolver.rb +146 -0
- data/lib/tasker_core/registry/resolvers/method_dispatch_wrapper.rb +144 -0
- data/lib/tasker_core/registry/resolvers/registry_resolver.rb +229 -0
- data/lib/tasker_core/registry/resolvers.rb +42 -0
- data/lib/tasker_core/registry.rb +12 -0
- data/lib/tasker_core/step_handler/api.rb +48 -0
- data/lib/tasker_core/step_handler/base.rb +354 -0
- data/lib/tasker_core/step_handler/batchable.rb +50 -0
- data/lib/tasker_core/step_handler/decision.rb +53 -0
- data/lib/tasker_core/step_handler/mixins/api.rb +452 -0
- data/lib/tasker_core/step_handler/mixins/batchable.rb +465 -0
- data/lib/tasker_core/step_handler/mixins/decision.rb +252 -0
- data/lib/tasker_core/step_handler/mixins.rb +66 -0
- data/lib/tasker_core/subscriber.rb +212 -0
- data/lib/tasker_core/task_handler/base.rb +254 -0
- data/lib/tasker_core/tasker_rb.so +0 -0
- data/lib/tasker_core/template_discovery.rb +181 -0
- data/lib/tasker_core/tracing.rb +166 -0
- data/lib/tasker_core/types/batch_processing_outcome.rb +301 -0
- data/lib/tasker_core/types/client_types.rb +145 -0
- data/lib/tasker_core/types/decision_point_outcome.rb +177 -0
- data/lib/tasker_core/types/error_types.rb +72 -0
- data/lib/tasker_core/types/simple_message.rb +151 -0
- data/lib/tasker_core/types/step_context.rb +328 -0
- data/lib/tasker_core/types/step_handler_call_result.rb +307 -0
- data/lib/tasker_core/types/step_message.rb +112 -0
- data/lib/tasker_core/types/step_types.rb +207 -0
- data/lib/tasker_core/types/task_template.rb +240 -0
- data/lib/tasker_core/types/task_types.rb +148 -0
- data/lib/tasker_core/types.rb +132 -0
- data/lib/tasker_core/version.rb +13 -0
- data/lib/tasker_core/worker/CLAUDE.md +7 -0
- data/lib/tasker_core/worker/event_poller.rb +224 -0
- data/lib/tasker_core/worker/in_process_domain_event_poller.rb +271 -0
- data/lib/tasker_core.rb +160 -0
- metadata +322 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TaskerCore
|
|
4
|
+
module Registry
|
|
5
|
+
module Resolvers
|
|
6
|
+
# TAS-93: Method Dispatch Wrapper
|
|
7
|
+
#
|
|
8
|
+
# Wraps a handler to redirect the standard `.call()` interface to
|
|
9
|
+
# a specified method name. This enables handlers with multiple
|
|
10
|
+
# entry points (e.g., process, refund, validate) to be invoked
|
|
11
|
+
# through the unified `.call()` interface.
|
|
12
|
+
#
|
|
13
|
+
# == Background
|
|
14
|
+
#
|
|
15
|
+
# The worker execution system always invokes `handler.call(context)`.
|
|
16
|
+
# When a HandlerDefinition specifies `method: refund`, we need to
|
|
17
|
+
# redirect that call to `handler.refund(context)`.
|
|
18
|
+
#
|
|
19
|
+
# == Usage
|
|
20
|
+
#
|
|
21
|
+
# # Original handler
|
|
22
|
+
# class PaymentHandler
|
|
23
|
+
# def process(context); end
|
|
24
|
+
# def refund(context); end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# # Wrap for refund dispatch
|
|
28
|
+
# handler = PaymentHandler.new
|
|
29
|
+
# wrapped = MethodDispatchWrapper.new(handler, :refund)
|
|
30
|
+
#
|
|
31
|
+
# # Now call() invokes refund()
|
|
32
|
+
# wrapped.call(context) # => handler.refund(context)
|
|
33
|
+
#
|
|
34
|
+
# == When Wrapping Occurs
|
|
35
|
+
#
|
|
36
|
+
# The ResolverChain automatically wraps handlers when:
|
|
37
|
+
# - HandlerDefinition has `method` set to something other than 'call'
|
|
38
|
+
# - The resolved handler doesn't already respond to `.call()`
|
|
39
|
+
#
|
|
40
|
+
class MethodDispatchWrapper
|
|
41
|
+
attr_reader :handler, :target_method
|
|
42
|
+
|
|
43
|
+
# Create a method dispatch wrapper
|
|
44
|
+
#
|
|
45
|
+
# @param handler [Object] The underlying handler instance
|
|
46
|
+
# @param target_method [Symbol, String] Method to invoke on call()
|
|
47
|
+
def initialize(handler, target_method)
|
|
48
|
+
@handler = handler
|
|
49
|
+
@target_method = target_method.to_sym
|
|
50
|
+
validate!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Invoke the target method on the wrapped handler
|
|
54
|
+
#
|
|
55
|
+
# @param args [Array] Arguments to pass through
|
|
56
|
+
# @param kwargs [Hash] Keyword arguments to pass through
|
|
57
|
+
# @param block [Proc] Block to pass through
|
|
58
|
+
# @return [Object] Result from the target method
|
|
59
|
+
def call(*, **kwargs, &)
|
|
60
|
+
if kwargs.empty?
|
|
61
|
+
@handler.public_send(@target_method, *, &)
|
|
62
|
+
else
|
|
63
|
+
@handler.public_send(@target_method, *, **kwargs, &)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Delegate respond_to? to the underlying handler
|
|
68
|
+
#
|
|
69
|
+
# @param method_name [Symbol] Method to check
|
|
70
|
+
# @param include_private [Boolean] Include private methods
|
|
71
|
+
# @return [Boolean]
|
|
72
|
+
# rubocop:disable Style/OptionalBooleanParameter -- Must match Ruby's respond_to? signature
|
|
73
|
+
def respond_to?(method_name, include_private = false)
|
|
74
|
+
method_name.to_sym == :call || @handler.respond_to?(method_name, include_private)
|
|
75
|
+
end
|
|
76
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
|
77
|
+
|
|
78
|
+
# Delegate respond_to_missing? to the underlying handler
|
|
79
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
80
|
+
@handler.respond_to_missing?(method_name, include_private)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Forward method_missing to underlying handler for delegation
|
|
84
|
+
#
|
|
85
|
+
# @param method_name [Symbol] Method name
|
|
86
|
+
# @param args [Array] Arguments
|
|
87
|
+
# @param block [Proc] Block
|
|
88
|
+
def method_missing(method_name, *, &)
|
|
89
|
+
if @handler.respond_to?(method_name)
|
|
90
|
+
@handler.public_send(method_name, *, &)
|
|
91
|
+
else
|
|
92
|
+
super
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# String representation for debugging
|
|
97
|
+
#
|
|
98
|
+
# @return [String]
|
|
99
|
+
def to_s
|
|
100
|
+
"#<MethodDispatchWrapper handler=#{@handler.class} method=#{@target_method}>"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Inspect for debugging
|
|
104
|
+
#
|
|
105
|
+
# @return [String]
|
|
106
|
+
def inspect
|
|
107
|
+
to_s
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Access the underlying handler class
|
|
111
|
+
#
|
|
112
|
+
# @return [Class]
|
|
113
|
+
def handler_class
|
|
114
|
+
@handler.class
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Check if this is wrapping a particular class
|
|
118
|
+
#
|
|
119
|
+
# @param klass [Class] Class to check
|
|
120
|
+
# @return [Boolean]
|
|
121
|
+
def wraps?(klass)
|
|
122
|
+
@handler.is_a?(klass)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Unwrap to get the original handler
|
|
126
|
+
#
|
|
127
|
+
# @return [Object] The underlying handler
|
|
128
|
+
def unwrap
|
|
129
|
+
@handler
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# Validate the wrapper can dispatch to target method
|
|
135
|
+
def validate!
|
|
136
|
+
return if @handler.respond_to?(@target_method)
|
|
137
|
+
|
|
138
|
+
raise ArgumentError,
|
|
139
|
+
"Handler #{@handler.class} does not respond to ##{@target_method}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_resolver'
|
|
4
|
+
|
|
5
|
+
module TaskerCore
|
|
6
|
+
module Registry
|
|
7
|
+
module Resolvers
|
|
8
|
+
# TAS-93: Developer-Friendly Base Class for Custom Resolvers
|
|
9
|
+
#
|
|
10
|
+
# RegistryResolver provides a DSL for creating custom handler resolvers
|
|
11
|
+
# with pattern matching, prefix matching, and explicit handler registration.
|
|
12
|
+
#
|
|
13
|
+
# == Usage Examples
|
|
14
|
+
#
|
|
15
|
+
# === Pattern-Based Resolution
|
|
16
|
+
#
|
|
17
|
+
# class PaymentResolver < RegistryResolver
|
|
18
|
+
# # Match handlers by pattern
|
|
19
|
+
# handles_pattern(/^Payments::.*Handler$/)
|
|
20
|
+
#
|
|
21
|
+
# # Custom resolution logic
|
|
22
|
+
# def resolve_handler(definition, config)
|
|
23
|
+
# klass = definition.callable.constantize
|
|
24
|
+
# klass.new(config: definition.initialization)
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# === Prefix-Based Resolution
|
|
29
|
+
#
|
|
30
|
+
# class LegacyResolver < RegistryResolver
|
|
31
|
+
# handles_prefix 'Legacy::'
|
|
32
|
+
# priority_value 50
|
|
33
|
+
#
|
|
34
|
+
# def resolve_handler(definition, config)
|
|
35
|
+
# # Custom instantiation for legacy handlers
|
|
36
|
+
# LegacyAdapter.wrap(definition.callable.constantize)
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# === Explicit Handler Registration
|
|
41
|
+
#
|
|
42
|
+
# class ExplicitResolver < RegistryResolver
|
|
43
|
+
# register_handler 'payment.process', PaymentHandler
|
|
44
|
+
# register_handler 'payment.refund', RefundHandler
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# == DSL Methods
|
|
48
|
+
#
|
|
49
|
+
# - `handles_pattern(regex)` - Match callable names by regex
|
|
50
|
+
# - `handles_prefix(prefix)` - Match callable names by prefix
|
|
51
|
+
# - `register_handler(key, handler)` - Explicit key → handler mapping
|
|
52
|
+
# - `priority_value(n)` - Set resolver priority (default: 50)
|
|
53
|
+
# - `resolver_name(name)` - Set human-readable name
|
|
54
|
+
#
|
|
55
|
+
class RegistryResolver < BaseResolver
|
|
56
|
+
class << self
|
|
57
|
+
# DSL: Set pattern to match callable names
|
|
58
|
+
def handles_pattern(pattern)
|
|
59
|
+
@pattern = pattern
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# DSL: Set prefix to match callable names
|
|
63
|
+
def handles_prefix(prefix)
|
|
64
|
+
@prefix = prefix
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# DSL: Register explicit handler mapping
|
|
68
|
+
def register_handler(key, handler_or_class)
|
|
69
|
+
@handlers ||= {}
|
|
70
|
+
@handlers[key.to_s] = handler_or_class
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# DSL: Set resolver priority
|
|
74
|
+
def priority_value(value)
|
|
75
|
+
@priority = value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# DSL: Set resolver name
|
|
79
|
+
def resolver_name(value)
|
|
80
|
+
@resolver_name = value
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Accessors for DSL values
|
|
84
|
+
attr_reader :pattern, :prefix, :handlers
|
|
85
|
+
|
|
86
|
+
def configured_priority
|
|
87
|
+
@priority || 50 # Default: between explicit (10) and inferential (100)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def configured_name
|
|
91
|
+
@resolver_name || name.demodulize.underscore
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Inherit DSL configuration in subclasses
|
|
95
|
+
def inherited(subclass)
|
|
96
|
+
super
|
|
97
|
+
subclass.instance_variable_set(:@handlers, @handlers&.dup || {})
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def initialize
|
|
102
|
+
super
|
|
103
|
+
@handlers = self.class.handlers&.dup || {}
|
|
104
|
+
@logger = TaskerCore::Logger.instance
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @return [String] Human-readable resolver name
|
|
108
|
+
def name
|
|
109
|
+
self.class.configured_name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @return [Integer] Resolution priority
|
|
113
|
+
def priority
|
|
114
|
+
self.class.configured_priority
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Check if this resolver can handle the definition
|
|
118
|
+
#
|
|
119
|
+
# Resolution order:
|
|
120
|
+
# 1. Check explicit handler registration
|
|
121
|
+
# 2. Check pattern match
|
|
122
|
+
# 3. Check prefix match
|
|
123
|
+
#
|
|
124
|
+
# @param definition [TaskerCore::Types::HandlerDefinition] Handler configuration
|
|
125
|
+
# @param config [Hash] Additional context
|
|
126
|
+
# @return [Boolean]
|
|
127
|
+
def can_resolve?(definition, _config)
|
|
128
|
+
callable = definition.callable.to_s
|
|
129
|
+
|
|
130
|
+
# Check explicit registration
|
|
131
|
+
return true if @handlers.key?(callable)
|
|
132
|
+
|
|
133
|
+
# Check pattern
|
|
134
|
+
return true if self.class.pattern && callable.match?(self.class.pattern)
|
|
135
|
+
|
|
136
|
+
# Check prefix
|
|
137
|
+
return true if self.class.prefix && callable.start_with?(self.class.prefix)
|
|
138
|
+
|
|
139
|
+
false
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Resolve the handler from the definition
|
|
143
|
+
#
|
|
144
|
+
# @param definition [TaskerCore::Types::HandlerDefinition] Handler configuration
|
|
145
|
+
# @param config [Hash] Additional context
|
|
146
|
+
# @return [Object, nil] Handler instance or nil
|
|
147
|
+
def resolve(definition, config)
|
|
148
|
+
callable = definition.callable.to_s
|
|
149
|
+
|
|
150
|
+
# Try explicit registration first
|
|
151
|
+
if @handlers.key?(callable)
|
|
152
|
+
handler = @handlers[callable]
|
|
153
|
+
return instantiate_handler(handler, definition.initialization)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Otherwise, delegate to subclass implementation
|
|
157
|
+
resolve_handler(definition, config)
|
|
158
|
+
rescue StandardError => e
|
|
159
|
+
@logger.debug("RegistryResolver #{name}: Failed to resolve '#{callable}': #{e.message}")
|
|
160
|
+
nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @return [Array<String>] List of explicitly registered callables
|
|
164
|
+
def registered_callables
|
|
165
|
+
@handlers.keys
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Register a handler at runtime
|
|
169
|
+
#
|
|
170
|
+
# @param key [String] Handler key/callable name
|
|
171
|
+
# @param handler [Object] Handler class or instance
|
|
172
|
+
def register(key, handler)
|
|
173
|
+
@handlers[key.to_s] = handler
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Unregister a handler at runtime
|
|
177
|
+
#
|
|
178
|
+
# @param key [String] Handler key to remove
|
|
179
|
+
# @return [Object, nil] Removed handler or nil
|
|
180
|
+
def unregister(key)
|
|
181
|
+
@handlers.delete(key.to_s)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
protected
|
|
185
|
+
|
|
186
|
+
# Override in subclass to provide custom resolution logic
|
|
187
|
+
#
|
|
188
|
+
# This is called when no explicit handler is registered.
|
|
189
|
+
#
|
|
190
|
+
# @param definition [TaskerCore::Types::HandlerDefinition] Handler configuration
|
|
191
|
+
# @param config [Hash] Additional context
|
|
192
|
+
# @return [Object, nil] Handler instance or nil
|
|
193
|
+
def resolve_handler(_definition, _config)
|
|
194
|
+
nil # Default: no custom resolution
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Instantiate a handler with configuration
|
|
198
|
+
#
|
|
199
|
+
# Handles:
|
|
200
|
+
# - Classes with (config:) keyword arg
|
|
201
|
+
# - Classes with no-arg constructor
|
|
202
|
+
# - Already-instantiated objects
|
|
203
|
+
#
|
|
204
|
+
# @param handler [Class, Object] Handler class or instance
|
|
205
|
+
# @param initialization [Hash] Configuration to pass
|
|
206
|
+
# @return [Object] Handler instance
|
|
207
|
+
def instantiate_handler(handler, initialization)
|
|
208
|
+
return handler unless handler.is_a?(Class)
|
|
209
|
+
|
|
210
|
+
arity = handler.instance_method(:initialize).arity
|
|
211
|
+
|
|
212
|
+
if arity.positive? || (arity.negative? && accepts_config_kwarg?(handler))
|
|
213
|
+
handler.new(config: initialization || {})
|
|
214
|
+
else
|
|
215
|
+
handler.new
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
private
|
|
220
|
+
|
|
221
|
+
# Check if the class accepts config: keyword argument
|
|
222
|
+
def accepts_config_kwarg?(klass)
|
|
223
|
+
params = klass.instance_method(:initialize).parameters
|
|
224
|
+
params.any? { |type, name| %i[key keyreq].include?(type) && name == :config }
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# TAS-93: Step Handler Resolver Infrastructure
|
|
4
|
+
#
|
|
5
|
+
# This module provides the resolver chain pattern for step handler resolution.
|
|
6
|
+
# Resolvers are tried in priority order until one successfully resolves.
|
|
7
|
+
#
|
|
8
|
+
# == Built-in Resolvers
|
|
9
|
+
#
|
|
10
|
+
# - ExplicitMappingResolver (priority 10): Explicit key → handler mappings
|
|
11
|
+
# - ClassConstantResolver (priority 100): Class path inference via const_get
|
|
12
|
+
#
|
|
13
|
+
# == Custom Resolvers
|
|
14
|
+
#
|
|
15
|
+
# Extend RegistryResolver for developer-friendly custom resolution:
|
|
16
|
+
#
|
|
17
|
+
# class PaymentResolver < TaskerCore::Registry::Resolvers::RegistryResolver
|
|
18
|
+
# handles_pattern(/^Payments::/)
|
|
19
|
+
# priority_value 50
|
|
20
|
+
#
|
|
21
|
+
# def resolve_handler(definition, config)
|
|
22
|
+
# # Custom resolution logic
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# == Method Dispatch
|
|
27
|
+
#
|
|
28
|
+
# When HandlerDefinition specifies `method: refund`, the chain automatically
|
|
29
|
+
# wraps the handler with MethodDispatchWrapper to redirect .call() → .refund()
|
|
30
|
+
#
|
|
31
|
+
require_relative 'resolvers/base_resolver'
|
|
32
|
+
require_relative 'resolvers/registry_resolver'
|
|
33
|
+
require_relative 'resolvers/explicit_mapping_resolver'
|
|
34
|
+
require_relative 'resolvers/class_constant_resolver'
|
|
35
|
+
require_relative 'resolvers/method_dispatch_wrapper'
|
|
36
|
+
|
|
37
|
+
module TaskerCore
|
|
38
|
+
module Registry
|
|
39
|
+
module Resolvers
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# TAS-93: Resolver chain infrastructure (load first - used by HandlerRegistry)
|
|
4
|
+
require_relative 'registry/resolvers'
|
|
5
|
+
require_relative 'registry/resolver_chain'
|
|
6
|
+
|
|
7
|
+
require_relative 'registry/handler_registry'
|
|
8
|
+
|
|
9
|
+
module TaskerCore
|
|
10
|
+
module Registry
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'mixins'
|
|
5
|
+
|
|
6
|
+
module TaskerCore
|
|
7
|
+
module StepHandler
|
|
8
|
+
# API step handler that provides HTTP functionality.
|
|
9
|
+
#
|
|
10
|
+
# ## TAS-112: Composition Pattern (DEPRECATED CLASS)
|
|
11
|
+
#
|
|
12
|
+
# This class is provided for backward compatibility. For new code, use the mixin pattern:
|
|
13
|
+
#
|
|
14
|
+
# ```ruby
|
|
15
|
+
# class MyApiHandler < TaskerCore::StepHandler::Base
|
|
16
|
+
# include TaskerCore::StepHandler::Mixins::API
|
|
17
|
+
#
|
|
18
|
+
# def call(context)
|
|
19
|
+
# response = get('/users')
|
|
20
|
+
# success(result: response.body)
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
# ## Key Features
|
|
26
|
+
# - Faraday HTTP client with full configuration support
|
|
27
|
+
# - Automatic error classification (RetryableError vs PermanentError)
|
|
28
|
+
# - Retry-After header support for server-requested backoff
|
|
29
|
+
# - SSL configuration, headers, query parameters
|
|
30
|
+
class Api < Base
|
|
31
|
+
include Mixins::API
|
|
32
|
+
|
|
33
|
+
# Legacy process method for Rails engine compatibility
|
|
34
|
+
# New handlers should implement call(context) instead
|
|
35
|
+
#
|
|
36
|
+
# @param _task [Object] Task (unused, for Rails compatibility)
|
|
37
|
+
# @param _sequence [Object] Sequence (unused, for Rails compatibility)
|
|
38
|
+
# @param _step [Object] Step (unused, for Rails compatibility)
|
|
39
|
+
# @return [Faraday::Response] HTTP response object
|
|
40
|
+
def process(_task, _sequence, _step)
|
|
41
|
+
url = config[:url] || config['url']
|
|
42
|
+
response = connection.get(url)
|
|
43
|
+
process_response(response)
|
|
44
|
+
response
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|