smart_message 0.0.7 → 0.0.9
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/.gitignore +1 -0
- data/.irbrc +24 -0
- data/CHANGELOG.md +143 -0
- data/Gemfile.lock +6 -1
- data/README.md +289 -15
- data/docs/README.md +3 -1
- data/docs/addressing.md +119 -13
- data/docs/architecture.md +68 -0
- data/docs/dead_letter_queue.md +673 -0
- data/docs/dispatcher.md +87 -0
- data/docs/examples.md +59 -1
- data/docs/getting-started.md +8 -1
- data/docs/logging.md +382 -326
- data/docs/message_filtering.md +451 -0
- data/examples/01_point_to_point_orders.rb +54 -53
- data/examples/02_publish_subscribe_events.rb +14 -10
- data/examples/03_many_to_many_chat.rb +16 -8
- data/examples/04_redis_smart_home_iot.rb +20 -10
- data/examples/05_proc_handlers.rb +12 -11
- data/examples/06_custom_logger_example.rb +95 -100
- data/examples/07_error_handling_scenarios.rb +4 -2
- data/examples/08_entity_addressing_basic.rb +18 -6
- data/examples/08_entity_addressing_with_filtering.rb +27 -9
- data/examples/09_dead_letter_queue_demo.rb +559 -0
- data/examples/09_regex_filtering_microservices.rb +407 -0
- data/examples/10_header_block_configuration.rb +263 -0
- data/examples/11_global_configuration_example.rb +219 -0
- data/examples/README.md +102 -0
- data/examples/dead_letters.jsonl +12 -0
- data/examples/performance_metrics/benchmark_results_ractor_20250818_205603.json +135 -0
- data/examples/performance_metrics/benchmark_results_ractor_20250818_205831.json +135 -0
- data/examples/performance_metrics/benchmark_results_test_20250818_204942.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json +130 -0
- data/examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json +130 -0
- data/examples/performance_metrics/benchmark_results_unknown_20250819_172852.json +130 -0
- data/examples/performance_metrics/compare_benchmarks.rb +519 -0
- data/examples/performance_metrics/dead_letters.jsonl +3100 -0
- data/examples/performance_metrics/performance_benchmark.rb +344 -0
- data/examples/show_logger.rb +367 -0
- data/examples/show_me.rb +145 -0
- data/examples/temp.txt +94 -0
- data/examples/tmux_chat/bot_agent.rb +4 -2
- data/examples/tmux_chat/human_agent.rb +4 -2
- data/examples/tmux_chat/room_monitor.rb +4 -2
- data/examples/tmux_chat/shared_chat_system.rb +6 -3
- data/lib/smart_message/addressing.rb +259 -0
- data/lib/smart_message/base.rb +121 -599
- data/lib/smart_message/circuit_breaker.rb +23 -6
- data/lib/smart_message/configuration.rb +199 -0
- data/lib/smart_message/dead_letter_queue.rb +361 -0
- data/lib/smart_message/dispatcher.rb +90 -49
- data/lib/smart_message/header.rb +5 -0
- data/lib/smart_message/logger/base.rb +21 -1
- data/lib/smart_message/logger/default.rb +88 -138
- data/lib/smart_message/logger/lumberjack.rb +324 -0
- data/lib/smart_message/logger/null.rb +81 -0
- data/lib/smart_message/logger.rb +17 -9
- data/lib/smart_message/messaging.rb +100 -0
- data/lib/smart_message/plugins.rb +132 -0
- data/lib/smart_message/serializer/base.rb +25 -8
- data/lib/smart_message/serializer/json.rb +5 -4
- data/lib/smart_message/subscription.rb +193 -0
- data/lib/smart_message/transport/base.rb +84 -53
- data/lib/smart_message/transport/memory_transport.rb +7 -5
- data/lib/smart_message/transport/redis_transport.rb +15 -45
- data/lib/smart_message/transport/stdout_transport.rb +18 -8
- data/lib/smart_message/transport.rb +1 -34
- data/lib/smart_message/utilities.rb +142 -0
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message/versioning.rb +85 -0
- data/lib/smart_message/wrapper.rb.bak +132 -0
- data/lib/smart_message.rb +74 -27
- data/smart_message.gemspec +3 -0
- metadata +77 -3
- data/lib/smart_message/serializer.rb +0 -10
- data/lib/smart_message/wrapper.rb +0 -43
@@ -27,13 +27,22 @@ module SmartMessage
|
|
27
27
|
@options[:loopback]
|
28
28
|
end
|
29
29
|
|
30
|
-
# Publish message to STDOUT
|
31
|
-
def do_publish(
|
32
|
-
|
30
|
+
# Publish message to STDOUT (single-tier serialization)
|
31
|
+
def do_publish(message_class, serialized_message)
|
32
|
+
logger.debug { "[SmartMessage::StdoutTransport] do_publish called" }
|
33
|
+
logger.debug { "[SmartMessage::StdoutTransport] message_class: #{message_class}" }
|
34
|
+
|
35
|
+
@output.puts format_message(message_class, serialized_message)
|
33
36
|
@output.flush
|
34
37
|
|
35
38
|
# If loopback is enabled, route the message back through the dispatcher
|
36
|
-
|
39
|
+
if loopback?
|
40
|
+
logger.debug { "[SmartMessage::StdoutTransport] Loopback enabled, calling receive" }
|
41
|
+
receive(message_class, serialized_message)
|
42
|
+
end
|
43
|
+
rescue => e
|
44
|
+
logger.error { "[SmartMessage] Error in stdout transport do_publish: #{e.class.name} - #{e.message}" }
|
45
|
+
raise
|
37
46
|
end
|
38
47
|
|
39
48
|
def connected?
|
@@ -46,17 +55,18 @@ module SmartMessage
|
|
46
55
|
|
47
56
|
private
|
48
57
|
|
49
|
-
def format_message(
|
58
|
+
def format_message(message_class, serialized_message)
|
50
59
|
<<~MESSAGE
|
51
60
|
|
52
61
|
===================================================
|
53
62
|
== SmartMessage Published via STDOUT Transport
|
54
|
-
==
|
55
|
-
==
|
63
|
+
== Single-Tier Serialization:
|
64
|
+
== Message Class: #{message_class}
|
65
|
+
== Serialized Message: #{serialized_message}
|
56
66
|
===================================================
|
57
67
|
|
58
68
|
MESSAGE
|
59
69
|
end
|
60
70
|
end
|
61
71
|
end
|
62
|
-
end
|
72
|
+
end
|
@@ -6,37 +6,4 @@ require_relative 'transport/base'
|
|
6
6
|
require_relative 'transport/registry'
|
7
7
|
require_relative 'transport/stdout_transport'
|
8
8
|
require_relative 'transport/memory_transport'
|
9
|
-
require_relative 'transport/redis_transport'
|
10
|
-
|
11
|
-
module SmartMessage
|
12
|
-
# Transport layer abstraction for SmartMessage
|
13
|
-
module Transport
|
14
|
-
class << self
|
15
|
-
# Get the transport registry instance
|
16
|
-
def registry
|
17
|
-
@registry ||= Registry.new
|
18
|
-
end
|
19
|
-
|
20
|
-
# Register a transport adapter
|
21
|
-
def register(name, transport_class)
|
22
|
-
registry.register(name, transport_class)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Get a transport by name
|
26
|
-
def get(name)
|
27
|
-
registry.get(name)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Create a transport instance with options
|
31
|
-
def create(name, **options)
|
32
|
-
transport_class = get(name)
|
33
|
-
transport_class&.new(**options)
|
34
|
-
end
|
35
|
-
|
36
|
-
# List all registered transports
|
37
|
-
def available
|
38
|
-
registry.list
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
9
|
+
require_relative 'transport/redis_transport'
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# lib/smart_message/utilities.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'set' # STDLIB
|
6
|
+
|
7
|
+
module SmartMessage
|
8
|
+
# Utilities module for SmartMessage::Base
|
9
|
+
# Provides utility methods for message introspection and debugging
|
10
|
+
module Utilities
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
#########################################################
|
16
|
+
## instance-level utility methods
|
17
|
+
|
18
|
+
# return this class' name as a string
|
19
|
+
def whoami
|
20
|
+
self.class.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
# return this class' description
|
24
|
+
def description
|
25
|
+
self.class.description
|
26
|
+
end
|
27
|
+
|
28
|
+
# Clean accessor to the SmartMessage header object
|
29
|
+
# Provides more intuitive API than _sm_header
|
30
|
+
# Note: Renamed to avoid conflict with class-level header DSL
|
31
|
+
def message_header
|
32
|
+
_sm_header
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns a collection of class Set that consists of
|
36
|
+
# the symbolized values of the property names of the message
|
37
|
+
# without the injected '_sm_' properties that support
|
38
|
+
# the behind-the-sceens operations of SmartMessage.
|
39
|
+
def fields
|
40
|
+
to_h.keys
|
41
|
+
.reject{|key| key.start_with?('_sm_')}
|
42
|
+
.map{|key| key.to_sym}
|
43
|
+
.to_set
|
44
|
+
end
|
45
|
+
|
46
|
+
# Pretty print the message content to STDOUT using amazing_print
|
47
|
+
# @param pp_or_include_header [PP, Boolean] Either a PP printer object (from Ruby's pp library)
|
48
|
+
# or include_header boolean (for our custom usage)
|
49
|
+
# @param include_header [Boolean] Whether to include the SmartMessage header (default: false)
|
50
|
+
def pretty_print(pp_or_include_header = nil, include_header: false)
|
51
|
+
# Handle Ruby's PP library calling convention: pretty_print(pp_object)
|
52
|
+
if pp_or_include_header.is_a?(Object) && pp_or_include_header.respond_to?(:text)
|
53
|
+
# This is Ruby's PP library calling us - delegate to standard object pretty printing
|
54
|
+
pp_or_include_header.text(self.inspect)
|
55
|
+
return
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handle our custom calling convention: pretty_print(include_header: true)
|
59
|
+
if pp_or_include_header.is_a?(TrueClass) || pp_or_include_header.is_a?(FalseClass)
|
60
|
+
include_header = pp_or_include_header
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'amazing_print'
|
64
|
+
|
65
|
+
if include_header
|
66
|
+
# Show both header and content
|
67
|
+
puts "Header:"
|
68
|
+
puts "-" * 20
|
69
|
+
|
70
|
+
# Get header data, converting to symbols and filtering out nils
|
71
|
+
header_data = _sm_header.to_h
|
72
|
+
.reject { |key, value| value.nil? }
|
73
|
+
header_data = deep_symbolize_keys(header_data)
|
74
|
+
ap header_data
|
75
|
+
|
76
|
+
puts "\nContent:"
|
77
|
+
puts "-" * 20
|
78
|
+
|
79
|
+
# Get payload data (message properties excluding header)
|
80
|
+
content_data = get_payload_data.reject { |key, value| value.nil? }
|
81
|
+
content_data = deep_symbolize_keys(content_data)
|
82
|
+
ap content_data
|
83
|
+
else
|
84
|
+
# Show only message content (excluding _sm_ properties and nil values)
|
85
|
+
content_data = get_payload_data.reject { |key, value| value.nil? }
|
86
|
+
content_data = deep_symbolize_keys(content_data)
|
87
|
+
ap content_data
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Extract payload data (all properties except _sm_header)
|
94
|
+
def get_payload_data
|
95
|
+
self.class.properties.each_with_object({}) do |prop, hash|
|
96
|
+
next if prop == :_sm_header
|
97
|
+
hash[prop.to_sym] = self[prop]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Recursively convert all string keys to symbols in nested hashes and arrays
|
102
|
+
def deep_symbolize_keys(obj)
|
103
|
+
case obj
|
104
|
+
when Hash
|
105
|
+
obj.each_with_object({}) do |(key, value), result|
|
106
|
+
result[key.to_sym] = deep_symbolize_keys(value)
|
107
|
+
end
|
108
|
+
when Array
|
109
|
+
obj.map { |item| deep_symbolize_keys(item) }
|
110
|
+
else
|
111
|
+
obj
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
module ClassMethods
|
116
|
+
#########################################################
|
117
|
+
## class-level description
|
118
|
+
|
119
|
+
def description(desc = nil)
|
120
|
+
if desc.nil?
|
121
|
+
@description || "#{self.name} is a SmartMessage"
|
122
|
+
else
|
123
|
+
@description = desc.to_s
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#########################################################
|
128
|
+
## class-level utility methods
|
129
|
+
|
130
|
+
# return this class' name as a string
|
131
|
+
def whoami
|
132
|
+
ancestors.first.to_s
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return a Set of symbols representing each defined property of
|
136
|
+
# this message class.
|
137
|
+
def fields
|
138
|
+
@properties.dup.delete_if{|item| item.to_s.start_with?('_sm_')}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# lib/smart_message/versioning.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module SmartMessage
|
6
|
+
# Versioning module for SmartMessage::Base
|
7
|
+
# Handles schema versioning and version validation
|
8
|
+
module Versioning
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Validate that the header version matches the expected version for this class
|
14
|
+
def validate_header_version!
|
15
|
+
expected = self.class.expected_header_version
|
16
|
+
actual = _sm_header.version
|
17
|
+
unless actual == expected
|
18
|
+
raise SmartMessage::Errors::ValidationError,
|
19
|
+
"#{self.class.name} expects version #{expected}, but header has version #{actual}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Override PropertyValidations validate! to include header and version validation
|
24
|
+
def validate!
|
25
|
+
# Validate message properties using PropertyValidations
|
26
|
+
super
|
27
|
+
|
28
|
+
# Validate header properties
|
29
|
+
_sm_header.validate!
|
30
|
+
|
31
|
+
# Validate header version matches expected class version
|
32
|
+
validate_header_version!
|
33
|
+
end
|
34
|
+
|
35
|
+
# Override PropertyValidations validation_errors to include header errors
|
36
|
+
def validation_errors
|
37
|
+
errors = []
|
38
|
+
|
39
|
+
# Get message property validation errors using PropertyValidations
|
40
|
+
errors.concat(super.map { |err|
|
41
|
+
err.merge(source: 'message')
|
42
|
+
})
|
43
|
+
|
44
|
+
# Get header validation errors
|
45
|
+
errors.concat(_sm_header.validation_errors.map { |err|
|
46
|
+
err.merge(source: 'header')
|
47
|
+
})
|
48
|
+
|
49
|
+
# Check version mismatch
|
50
|
+
expected = self.class.expected_header_version
|
51
|
+
actual = _sm_header.version
|
52
|
+
unless actual == expected
|
53
|
+
errors << {
|
54
|
+
property: :version,
|
55
|
+
value: actual,
|
56
|
+
message: "Expected version #{expected}, got: #{actual}",
|
57
|
+
source: 'version_mismatch'
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
errors
|
62
|
+
end
|
63
|
+
|
64
|
+
module ClassMethods
|
65
|
+
# Class-level version setting
|
66
|
+
attr_accessor :_version
|
67
|
+
|
68
|
+
def version(v = nil)
|
69
|
+
if v.nil?
|
70
|
+
@_version || 1 # Default to version 1 if not set
|
71
|
+
else
|
72
|
+
@_version = v
|
73
|
+
|
74
|
+
# Set up version validation for the header
|
75
|
+
# This ensures that the header version matches the expected class version
|
76
|
+
@expected_header_version = v
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def expected_header_version
|
81
|
+
@expected_header_version || 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# lib/smart_message/wrapper.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'securerandom' # STDLIB
|
6
|
+
require_relative './header.rb'
|
7
|
+
|
8
|
+
module SmartMessage
|
9
|
+
module Wrapper
|
10
|
+
# Every smart message has a common wrapper format that contains
|
11
|
+
# information used to support the dispatching of subscribed
|
12
|
+
# messages upon receipt from a transport as well as the serialized
|
13
|
+
# payload.
|
14
|
+
#
|
15
|
+
# The wrapper consolidates header and payload into a single object
|
16
|
+
# for cleaner method signatures throughout the SmartMessage dataflow.
|
17
|
+
class Base < Hashie::Dash
|
18
|
+
include Hashie::Extensions::IndifferentAccess
|
19
|
+
include Hashie::Extensions::MethodAccess
|
20
|
+
include Hashie::Extensions::DeepMerge
|
21
|
+
|
22
|
+
# Core wrapper properties
|
23
|
+
# Using '_sm_' prefix to avoid collision with user message definitions
|
24
|
+
property :_sm_header,
|
25
|
+
required: true,
|
26
|
+
description: "SmartMessage header containing routing and metadata information"
|
27
|
+
|
28
|
+
property :_sm_payload,
|
29
|
+
required: true,
|
30
|
+
description: "Serialized message payload containing the business data"
|
31
|
+
|
32
|
+
# Create wrapper from header and payload
|
33
|
+
def initialize(header: nil, payload: nil, **props, &block)
|
34
|
+
# Handle different initialization patterns
|
35
|
+
if header && payload
|
36
|
+
attributes = {
|
37
|
+
_sm_header: header,
|
38
|
+
_sm_payload: payload
|
39
|
+
}
|
40
|
+
else
|
41
|
+
# Create default header if not provided
|
42
|
+
default_header = SmartMessage::Header.new(
|
43
|
+
uuid: SecureRandom.uuid,
|
44
|
+
message_class: 'SmartMessage::Wrapper::Base',
|
45
|
+
published_at: Time.now,
|
46
|
+
publisher_pid: Process.pid,
|
47
|
+
version: 1
|
48
|
+
)
|
49
|
+
|
50
|
+
attributes = {
|
51
|
+
_sm_header: default_header,
|
52
|
+
_sm_payload: nil
|
53
|
+
}.merge(props)
|
54
|
+
end
|
55
|
+
|
56
|
+
super(attributes, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convenience accessors for header and payload
|
60
|
+
def header
|
61
|
+
_sm_header
|
62
|
+
end
|
63
|
+
|
64
|
+
def payload
|
65
|
+
_sm_payload
|
66
|
+
end
|
67
|
+
|
68
|
+
# Check if this is a broadcast message (to field is nil)
|
69
|
+
def broadcast?
|
70
|
+
_sm_header.to.nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check if this is a directed message (to field is present)
|
74
|
+
def directed?
|
75
|
+
!broadcast?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get message class from header
|
79
|
+
def message_class
|
80
|
+
_sm_header.message_class
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get sender from header
|
84
|
+
def from
|
85
|
+
_sm_header.from
|
86
|
+
end
|
87
|
+
|
88
|
+
# Get recipient from header
|
89
|
+
def to
|
90
|
+
_sm_header.to
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get reply destination from header
|
94
|
+
def reply_to
|
95
|
+
_sm_header.reply_to
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get message version from header
|
99
|
+
def version
|
100
|
+
_sm_header.version
|
101
|
+
end
|
102
|
+
|
103
|
+
# Get UUID from header
|
104
|
+
def uuid
|
105
|
+
_sm_header.uuid
|
106
|
+
end
|
107
|
+
|
108
|
+
# Convert wrapper to hash for serialization/transport
|
109
|
+
def to_hash
|
110
|
+
{
|
111
|
+
'_sm_header' => _sm_header.to_hash,
|
112
|
+
'_sm_payload' => _sm_payload
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
alias_method :to_h, :to_hash
|
117
|
+
|
118
|
+
# Outer-level JSON serialization for the wrapper
|
119
|
+
# This is level 2 serialization - always JSON for routing/monitoring
|
120
|
+
def to_json(*args)
|
121
|
+
require 'json'
|
122
|
+
to_hash.to_json(*args)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Split wrapper into header and payload components
|
126
|
+
# Enables destructuring assignment: header, payload = wrapper.split
|
127
|
+
def split
|
128
|
+
[_sm_header, _sm_payload]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/smart_message.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
# encoding: utf-8
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
+
require 'zeitwerk'
|
6
|
+
|
5
7
|
# FIXME: handle this better
|
6
8
|
class MilClass # IMO nil is the same as an empty String
|
7
9
|
def to_s
|
@@ -9,47 +11,92 @@ class MilClass # IMO nil is the same as an empty String
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
|
13
14
|
require 'active_support/core_ext/string/inflections'
|
14
15
|
require 'date' # STDLIB
|
15
|
-
|
16
|
-
# Production logging should use the logger framework, not debug_me
|
17
|
-
|
18
16
|
require 'hashie' # Your friendly neighborhood hash library.
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
# Set up Zeitwerk autoloader
|
19
|
+
loader = Zeitwerk::Loader.for_gem
|
20
|
+
loader.tag = "smart_message"
|
21
|
+
loader.ignore("#{__dir__}/simple_stats.rb")
|
22
|
+
loader.setup
|
25
23
|
|
26
|
-
|
27
|
-
require_relative './
|
28
|
-
require_relative './smart_message/base.rb'
|
24
|
+
# Load simple_stats manually since it's not following the naming convention
|
25
|
+
require_relative './simple_stats'
|
29
26
|
|
30
27
|
# SmartMessage abstracts messages from the backend transport process
|
31
28
|
module SmartMessage
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
29
|
+
class << self
|
30
|
+
# Global configuration for SmartMessage
|
31
|
+
#
|
32
|
+
# Usage:
|
33
|
+
# SmartMessage.configure do |config|
|
34
|
+
# config.logger = MyApp::Logger.new
|
35
|
+
# config.transport = MyApp::Transport.new
|
36
|
+
# config.serializer = MyApp::Serializer.new
|
37
|
+
# end
|
38
|
+
def configure
|
39
|
+
yield(configuration)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the global configuration instance
|
43
|
+
def configuration
|
44
|
+
@configuration ||= Configuration.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# Reset global configuration to defaults
|
48
|
+
def reset_configuration!
|
49
|
+
@configuration = Configuration.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# Module definitions for Zeitwerk to populate
|
40
53
|
module Serializer
|
41
|
-
|
42
|
-
|
54
|
+
class << self
|
55
|
+
def default
|
56
|
+
# Check global configuration first, then fall back to framework default
|
57
|
+
SmartMessage.configuration.default_serializer
|
58
|
+
end
|
43
59
|
end
|
44
60
|
end
|
45
61
|
|
46
|
-
# encapsulates the message logging capability
|
47
62
|
module Logger
|
48
|
-
|
49
|
-
|
63
|
+
class << self
|
64
|
+
def default
|
65
|
+
# Check global configuration first, then fall back to framework default
|
66
|
+
SmartMessage.configuration.default_logger
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Transport
|
72
|
+
class << self
|
73
|
+
def default
|
74
|
+
# Check global configuration first, then fall back to framework default
|
75
|
+
SmartMessage.configuration.default_transport
|
76
|
+
end
|
77
|
+
|
78
|
+
def registry
|
79
|
+
@registry ||= Registry.new
|
80
|
+
end
|
81
|
+
|
82
|
+
def register(name, transport_class)
|
83
|
+
registry.register(name, transport_class)
|
84
|
+
end
|
85
|
+
|
86
|
+
def get(name)
|
87
|
+
registry.get(name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def create(name, **options)
|
91
|
+
transport_class = get(name)
|
92
|
+
transport_class&.new(**options)
|
93
|
+
end
|
94
|
+
|
95
|
+
def available
|
96
|
+
registry.list
|
97
|
+
end
|
50
98
|
end
|
51
99
|
end
|
52
100
|
end # module SmartMessage
|
53
101
|
|
54
|
-
|
55
|
-
require_relative './smart_message/logger'
|
102
|
+
# Don't eager load initially - let Zeitwerk handle lazy loading
|
data/smart_message.gemspec
CHANGED
@@ -41,6 +41,9 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_dependency 'concurrent-ruby'
|
42
42
|
spec.add_dependency 'redis'
|
43
43
|
spec.add_dependency 'breaker_machines'
|
44
|
+
spec.add_dependency 'zeitwerk'
|
45
|
+
spec.add_dependency 'lumberjack'
|
46
|
+
spec.add_dependency 'colorize'
|
44
47
|
|
45
48
|
spec.add_development_dependency 'bundler'
|
46
49
|
spec.add_development_dependency 'rake'
|