prosody 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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.cargo/config.toml +2 -0
  3. data/.release-please-manifest.json +3 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/.standard.yml +9 -0
  7. data/.taplo.toml +6 -0
  8. data/ARCHITECTURE.md +591 -0
  9. data/CHANGELOG.md +92 -0
  10. data/Cargo.lock +3513 -0
  11. data/Cargo.toml +77 -0
  12. data/LICENSE +21 -0
  13. data/Makefile +36 -0
  14. data/README.md +946 -0
  15. data/Rakefile +26 -0
  16. data/ext/prosody/Cargo.toml +38 -0
  17. data/ext/prosody/extconf.rb +6 -0
  18. data/ext/prosody/src/admin.rs +171 -0
  19. data/ext/prosody/src/bridge/callback.rs +60 -0
  20. data/ext/prosody/src/bridge/mod.rs +332 -0
  21. data/ext/prosody/src/client/config.rs +819 -0
  22. data/ext/prosody/src/client/mod.rs +379 -0
  23. data/ext/prosody/src/gvl.rs +149 -0
  24. data/ext/prosody/src/handler/context.rs +436 -0
  25. data/ext/prosody/src/handler/message.rs +144 -0
  26. data/ext/prosody/src/handler/mod.rs +338 -0
  27. data/ext/prosody/src/handler/trigger.rs +93 -0
  28. data/ext/prosody/src/lib.rs +82 -0
  29. data/ext/prosody/src/logging.rs +353 -0
  30. data/ext/prosody/src/scheduler/cancellation.rs +67 -0
  31. data/ext/prosody/src/scheduler/handle.rs +50 -0
  32. data/ext/prosody/src/scheduler/mod.rs +169 -0
  33. data/ext/prosody/src/scheduler/processor.rs +166 -0
  34. data/ext/prosody/src/scheduler/result.rs +197 -0
  35. data/ext/prosody/src/tracing_util.rs +56 -0
  36. data/ext/prosody/src/util.rs +219 -0
  37. data/lib/prosody/configuration.rb +333 -0
  38. data/lib/prosody/handler.rb +177 -0
  39. data/lib/prosody/native_stubs.rb +417 -0
  40. data/lib/prosody/processor.rb +321 -0
  41. data/lib/prosody/sentry.rb +36 -0
  42. data/lib/prosody/version.rb +10 -0
  43. data/lib/prosody.rb +42 -0
  44. data/release-please-config.json +10 -0
  45. data/sig/configuration.rbs +252 -0
  46. data/sig/handler.rbs +79 -0
  47. data/sig/processor.rbs +100 -0
  48. data/sig/prosody.rbs +171 -0
  49. data/sig/version.rbs +9 -0
  50. metadata +193 -0
data/sig/handler.rbs ADDED
@@ -0,0 +1,79 @@
1
+ module Prosody
2
+ # Abstract base for all errors raised by EventHandler methods.
3
+ # Subclasses must implement `#permanent?` to indicate retry behavior.
4
+ class EventHandlerError < Prosody::Error
5
+ # Indicates whether this error is permanent (no retry) or transient (retryable).
6
+ #
7
+ # @raise [NotImplementedError] if not overridden by subclass
8
+ def permanent?: () -> bool
9
+ end
10
+
11
+ # Error indicating that the failure is temporary and can be retried.
12
+ class TransientError < EventHandlerError
13
+ # @return [false] indicates this error is retryable
14
+ def permanent?: () -> false
15
+ end
16
+
17
+ # Error indicating that the failure is permanent and should not be retried.
18
+ class PermanentError < EventHandlerError
19
+ # @return [true] indicates this error is not retryable
20
+ def permanent?: () -> true
21
+ end
22
+
23
+ # Mixin providing class-level methods to wrap instance methods so that
24
+ # specified exceptions are re-wrapped as PermanentError or TransientError.
25
+ module ErrorClassification
26
+ # Wraps the given instance method so that specified exception types
27
+ # are caught and re-raised as Prosody::PermanentError.
28
+ #
29
+ # @param method_name the name of the method to wrap
30
+ # @param exception_classes one or more Exception subclasses to catch
31
+ # @raise [ArgumentError] if no exception classes given or invalid
32
+ # @raise [NameError] if method_name is not defined
33
+ def permanent: (Symbol method_name, *Class exception_classes) -> void
34
+
35
+ # Wraps the given instance method so that specified exception types
36
+ # are caught and re-raised as Prosody::TransientError.
37
+ #
38
+ # @param method_name the name of the method to wrap
39
+ # @param exception_classes one or more Exception subclasses to catch
40
+ # @raise [ArgumentError] if no exception classes given or invalid
41
+ # @raise [NameError] if method_name is not defined
42
+ def transient: (Symbol method_name, *Class exception_classes) -> void
43
+
44
+ private
45
+
46
+ # Core implementation: redefines `method_name` to catch listed exceptions
47
+ # and re-raise them as the specified error class.
48
+ #
49
+ # @param method_name the method to wrap
50
+ # @param exception_classes exceptions to catch
51
+ # @param error_class the error class to wrap caught exceptions in
52
+ def wrap_errors: (Symbol method_name, Array[Class] exception_classes, singleton(EventHandlerError) error_class) -> void
53
+ end
54
+
55
+ # Abstract base class for handling incoming messages from Prosody.
56
+ # Subclasses must implement `#on_message` to process received messages.
57
+ class EventHandler
58
+ extend ErrorClassification
59
+
60
+ # Process a single message received from Prosody.
61
+ # This method must be implemented by subclasses to define
62
+ # custom message handling logic.
63
+ #
64
+ # @param context the message context containing metadata and control capabilities
65
+ # @param message the message content with payload and metadata
66
+ # @raise [NotImplementedError] if not overridden by subclass
67
+ def on_message: (Context context, Message message) -> void
68
+
69
+ # Process a timer that has fired.
70
+ # This method is called when a previously scheduled timer fires.
71
+ # This method must be implemented by subclasses to define
72
+ # custom timer handling logic.
73
+ #
74
+ # @param context the timer context containing metadata and control capabilities
75
+ # @param timer the timer object with key and time information
76
+ # @raise [NotImplementedError] if not overridden by subclass
77
+ def on_timer: (Context context, Timer timer) -> void
78
+ end
79
+ end
data/sig/processor.rbs ADDED
@@ -0,0 +1,100 @@
1
+ # Type definitions for Prosody::Processor
2
+
3
+ module Prosody
4
+ # Provides a mechanism for canceling asynchronous tasks
5
+ class CancellationToken
6
+ @queue: Queue
7
+
8
+ # Creates a new token with an internal queue for signaling
9
+ def initialize: () -> void
10
+
11
+ # Signals that the associated task should be canceled
12
+ # Wakes up any threads waiting on #wait
13
+ def cancel: () -> void
14
+
15
+ # Blocks until cancellation is requested
16
+ # @return always returns true after cancellation is received
17
+ def wait: () -> bool
18
+ end
19
+
20
+ module Commands
21
+ # Base class for all processor commands
22
+ class Command
23
+
24
+ end
25
+
26
+ # Command to execute a task with the given parameters
27
+ class Execute < Command
28
+ # Task identifier for logging and debugging
29
+ attr_reader task_id: String
30
+
31
+ # The block of code to execute
32
+ attr_reader block: ^() -> void
33
+
34
+ # Callback to invoke when execution completes or fails
35
+ attr_reader callback: ^(bool, untyped) -> bool
36
+
37
+ # Cancellation token for this task
38
+ attr_reader token: CancellationToken
39
+
40
+ # OpenTelemetry context carrier for trace propagation
41
+ attr_reader carrier: Hash[String, String]
42
+
43
+ # Creates a new execute command with all required parameters
44
+ def initialize: (
45
+ task_id: String,
46
+ carrier: Hash[String, String],
47
+ block: ^() -> void,
48
+ callback: ^(bool, untyped) -> bool,
49
+ token: CancellationToken
50
+ ) -> void
51
+ end
52
+
53
+ # Command that signals the processor to shut down
54
+ class Shutdown < Command
55
+
56
+ end
57
+ end
58
+
59
+ class AsyncTaskProcessor
60
+ @logger: Logger
61
+ @command_queue: Queue
62
+ @processing_thread: Thread?
63
+ @tracer: untyped?
64
+
65
+ # Creates a new processor with the given logger
66
+ # @param logger logger for diagnostic messages (defaults to Prosody.logger)
67
+ def initialize: (?Logger) -> void
68
+
69
+ # Starts the processor by launching a dedicated thread
70
+ # Does nothing if the processor is already running
71
+ # @return the Thread if started, or nil if already running
72
+ def start: () -> Thread?
73
+
74
+ # Gracefully stops the processor
75
+ # Does nothing if the processor is already stopped
76
+ def stop: () -> void
77
+
78
+ # Submits a task for asynchronous execution
79
+ # @param task_id unique identifier for the task
80
+ # @param carrier OpenTelemetry context carrier for tracing
81
+ # @param callback called with (success, result) when task completes
82
+ # @yield the block to execute asynchronously
83
+ # @return token that can be used to cancel the task
84
+ def submit: (
85
+ task_id: String,
86
+ carrier: Hash[String, String],
87
+ callback: ^(bool, untyped) -> bool
88
+ ) { () -> untyped } -> CancellationToken
89
+
90
+ private
91
+
92
+ # Main processing loop for the async thread
93
+ def process_commands: () -> void
94
+
95
+ # Handles execution of a task with proper context propagation and error handling
96
+ # @param command the command containing task details
97
+ # @param barrier barrier for tracking active tasks
98
+ def handle_execute: (Commands::Execute, Async::Barrier) -> void
99
+ end
100
+ end
data/sig/prosody.rbs ADDED
@@ -0,0 +1,171 @@
1
+ module Prosody
2
+ @logger: Logger?
3
+
4
+ # Returns the module-level logger, creating a default if not yet set.
5
+ # The default writes to $stdout at INFO level.
6
+ def self.logger: () -> Logger
7
+
8
+ # Sets the module-level logger. Pass nil to reset to the default.
9
+ def self.logger=: (Logger?) -> Logger?
10
+
11
+ # Wrapper for dynamically-typed results from async operations.
12
+ # This is an internal class used for bridging Rust and Ruby.
13
+ class DynamicResult
14
+ # This class should not be instantiated directly.
15
+
16
+ private
17
+
18
+ def initialize: (untyped) -> void
19
+ end
20
+
21
+ # Represents the context of a Kafka message.
22
+ # Provides metadata and control capabilities for message processing.
23
+ class Context
24
+ # This class is instantiated internally by the client.
25
+
26
+ # Checks if cancellation has been requested.
27
+ # Cancellation includes both message-level cancellation (e.g., timeout)
28
+ # and partition shutdown.
29
+ # @return [bool] true if cancellation has been requested, false otherwise
30
+ def should_cancel?: () -> bool
31
+
32
+ # Blocks until cancellation is signaled.
33
+ # Cancellation includes both message-level cancellation (e.g., timeout)
34
+ # and partition shutdown.
35
+ # @return [void]
36
+ def on_cancel: () -> void
37
+
38
+ # Schedules a timer to fire at the specified time.
39
+ # @param time [Time] When the timer should fire
40
+ # @return [void]
41
+ def schedule: (Time time) -> void
42
+
43
+ # Clears all scheduled timers and schedules a new one at the specified time.
44
+ # @param time [Time] When the new timer should fire
45
+ # @return [void]
46
+ def clear_and_schedule: (Time time) -> void
47
+
48
+ # Unschedules a timer that was scheduled for the specified time.
49
+ # @param time [Time] The time for which to unschedule the timer
50
+ # @return [void]
51
+ def unschedule: (Time time) -> void
52
+
53
+ # Clears all scheduled timers.
54
+ # @return [void]
55
+ def clear_scheduled: () -> void
56
+
57
+ # Returns all currently scheduled timer times.
58
+ # @return [Array<Time>] Array of scheduled timer times
59
+ def scheduled: () -> Array[Time]
60
+
61
+ private
62
+
63
+ def initialize: (untyped) -> void
64
+ end
65
+
66
+ # Represents a Kafka message with its metadata and payload.
67
+ # Provides access to message properties like topic, partition, offset, and content.
68
+ class Message
69
+ # Returns the Kafka topic this message was published to.
70
+ # @return [String] The topic name
71
+ def topic: () -> String
72
+
73
+ # Returns the Kafka partition number for this message.
74
+ # @return [Integer] The partition number
75
+ def partition: () -> Integer
76
+
77
+ # Returns the Kafka offset of this message within its partition.
78
+ # @return [Integer] The message offset
79
+ def offset: () -> Integer
80
+
81
+ # Returns the message key used for partitioning.
82
+ # @return [String] The message key
83
+ def key: () -> String
84
+
85
+ # Returns the timestamp when the message was created.
86
+ # @return [Time] The message timestamp
87
+ def timestamp: () -> Time
88
+
89
+ # Returns the deserialized message payload.
90
+ # @return [untyped] The message content
91
+ def payload: () -> untyped
92
+ end
93
+
94
+ # Represents a timer that was scheduled to fire at a specific time.
95
+ # Timer instances are passed to EventHandler's on_timer method when timers fire.
96
+ class Timer
97
+ # Returns the entity key identifying what this timer belongs to.
98
+ # @return [String] The entity key
99
+ def key: () -> String
100
+
101
+ # Returns the time when this timer was scheduled to fire.
102
+ # @return [Time] The scheduled execution time
103
+ def time: () -> Time
104
+
105
+
106
+ private
107
+
108
+ def initialize: (untyped) -> void
109
+ end
110
+
111
+ # Main client for interacting with the Prosody messaging system.
112
+ # Provides methods for sending messages and subscribing to Kafka topics.
113
+ class Client
114
+ # Creates a new Prosody client with the given configuration.
115
+ #
116
+ # @param config [Hash, Configuration] Client configuration
117
+ # @return [Client] A new client instance
118
+ # @raise [ArgumentError] If the configuration is invalid
119
+ # @raise [RuntimeError] If client initialization fails
120
+ def self.new: (Hash[Symbol, untyped] | Configuration config) -> Client
121
+
122
+ # Returns the current state of the consumer.
123
+ #
124
+ # @return [Symbol] The consumer state: :unconfigured, :configured, or :running
125
+ def consumer_state: () -> Symbol
126
+
127
+ # Returns the number of Kafka partitions currently assigned to this consumer.
128
+ #
129
+ # @return [Integer] The number of assigned partitions
130
+ def assigned_partitions: () -> Integer
131
+
132
+ # Checks if the consumer is stalled.
133
+ #
134
+ # A stalled consumer is one that has stopped processing messages due to
135
+ # errors or reaching processing limits.
136
+ #
137
+ # @return [Boolean] true if the consumer is stalled, false otherwise
138
+ def is_stalled?: () -> bool
139
+
140
+ # Sends a message to the specified Kafka topic.
141
+ #
142
+ # @param topic [String] The destination topic name
143
+ # @param key [String] The message key for partitioning
144
+ # @param payload [untyped] The message payload (will be serialized)
145
+ # @return [void]
146
+ # @raise [RuntimeError] If the message cannot be sent
147
+ def send_message: (String topic, String key, untyped payload) -> void
148
+
149
+ # Subscribes to Kafka topics using the provided handler.
150
+ # The handler must implement an `on_message(context, message)` method.
151
+ #
152
+ # @param handler [EventHandler] A handler object that processes messages
153
+ # @return [void]
154
+ # @raise [RuntimeError] If subscription fails
155
+ def subscribe: (EventHandler) -> void
156
+
157
+ # Unsubscribes from all topics, stopping message processing.
158
+ #
159
+ # @return [void]
160
+ # @raise [RuntimeError] If unsubscription fails
161
+ def unsubscribe: () -> void
162
+
163
+ # Returns the configured source system identifier.
164
+ #
165
+ # The source system is used to identify the originating service or
166
+ # component in produced messages, enabling loop detection.
167
+ #
168
+ # @return [String] The source system identifier
169
+ def source_system: () -> String
170
+ end
171
+ end
data/sig/version.rbs ADDED
@@ -0,0 +1,9 @@
1
+ # Type definitions for Prosody version module
2
+
3
+ module Prosody
4
+ # Current version of the Prosody library.
5
+ #
6
+ # This version number follows semantic versioning and is used by the
7
+ # gem system to identify the library version.
8
+ VERSION: String
9
+ end
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prosody
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Griffith
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: async
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.39'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.39'
26
+ - !ruby/object:Gem::Dependency
27
+ name: opentelemetry-api
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.9'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.9'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rb_sys
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.9.126
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.9.126
54
+ - !ruby/object:Gem::Dependency
55
+ name: async-rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.17'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.17'
68
+ - !ruby/object:Gem::Dependency
69
+ name: opentelemetry-sdk
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.11'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.11'
82
+ - !ruby/object:Gem::Dependency
83
+ name: opentelemetry-exporter-otlp
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.33'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.33'
96
+ - !ruby/object:Gem::Dependency
97
+ name: sentry-ruby
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '6.5'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '6.5'
110
+ description: |
111
+ Prosody offers Ruby bindings to the Prosody Kafka client, providing features
112
+ for message production and consumption, including configurable retry
113
+ mechanisms, failure handling strategies, and integrated OpenTelemetry
114
+ support for distributed tracing.
115
+ executables: []
116
+ extensions:
117
+ - ext/prosody/extconf.rb
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".cargo/config.toml"
121
+ - ".release-please-manifest.json"
122
+ - ".rspec"
123
+ - ".ruby-version"
124
+ - ".standard.yml"
125
+ - ".taplo.toml"
126
+ - ARCHITECTURE.md
127
+ - CHANGELOG.md
128
+ - Cargo.lock
129
+ - Cargo.toml
130
+ - LICENSE
131
+ - Makefile
132
+ - README.md
133
+ - Rakefile
134
+ - ext/prosody/Cargo.toml
135
+ - ext/prosody/extconf.rb
136
+ - ext/prosody/src/admin.rs
137
+ - ext/prosody/src/bridge/callback.rs
138
+ - ext/prosody/src/bridge/mod.rs
139
+ - ext/prosody/src/client/config.rs
140
+ - ext/prosody/src/client/mod.rs
141
+ - ext/prosody/src/gvl.rs
142
+ - ext/prosody/src/handler/context.rs
143
+ - ext/prosody/src/handler/message.rs
144
+ - ext/prosody/src/handler/mod.rs
145
+ - ext/prosody/src/handler/trigger.rs
146
+ - ext/prosody/src/lib.rs
147
+ - ext/prosody/src/logging.rs
148
+ - ext/prosody/src/scheduler/cancellation.rs
149
+ - ext/prosody/src/scheduler/handle.rs
150
+ - ext/prosody/src/scheduler/mod.rs
151
+ - ext/prosody/src/scheduler/processor.rs
152
+ - ext/prosody/src/scheduler/result.rs
153
+ - ext/prosody/src/tracing_util.rs
154
+ - ext/prosody/src/util.rs
155
+ - lib/prosody.rb
156
+ - lib/prosody/configuration.rb
157
+ - lib/prosody/handler.rb
158
+ - lib/prosody/native_stubs.rb
159
+ - lib/prosody/processor.rb
160
+ - lib/prosody/sentry.rb
161
+ - lib/prosody/version.rb
162
+ - release-please-config.json
163
+ - sig/configuration.rbs
164
+ - sig/handler.rbs
165
+ - sig/processor.rbs
166
+ - sig/prosody.rbs
167
+ - sig/version.rbs
168
+ homepage: https://github.com/prosody-events/prosody-rb
169
+ licenses:
170
+ - MIT
171
+ metadata:
172
+ allowed_push_host: https://rubygems.org
173
+ homepage_uri: https://github.com/prosody-events/prosody-rb
174
+ source_code_uri: https://github.com/prosody-events/prosody-rb
175
+ changelog_uri: https://github.com/prosody-events/prosody-rb/blob/main/CHANGELOG.md
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: 3.2.0
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: 3.3.11
189
+ requirements: []
190
+ rubygems_version: 3.6.9
191
+ specification_version: 4
192
+ summary: Ruby bindings for the Prosody Kafka client library
193
+ test_files: []