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
data/examples/show_me.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# show_me.rb - Demonstrates the pretty_print method on SmartMessage instances
|
3
|
+
|
4
|
+
require_relative '../lib/smart_message'
|
5
|
+
|
6
|
+
# Define a sample order message
|
7
|
+
class OrderMessage < SmartMessage::Base
|
8
|
+
version 1
|
9
|
+
description "A message representing an e-commerce order"
|
10
|
+
|
11
|
+
from 'e-commerce-api'
|
12
|
+
to 'order-processor'
|
13
|
+
|
14
|
+
property :order_id, required: true
|
15
|
+
property :customer_name, required: true
|
16
|
+
property :customer_email
|
17
|
+
property :items, required: true
|
18
|
+
property :total_amount, required: true
|
19
|
+
property :shipping_address
|
20
|
+
property :payment_info
|
21
|
+
property :order_date
|
22
|
+
end
|
23
|
+
|
24
|
+
# Define a sample user notification message
|
25
|
+
class UserNotificationMessage < SmartMessage::Base
|
26
|
+
version 2
|
27
|
+
description "A notification message for users"
|
28
|
+
|
29
|
+
from 'notification-service'
|
30
|
+
to 'user-app'
|
31
|
+
|
32
|
+
property :user_id, required: true
|
33
|
+
property :notification_type, required: true
|
34
|
+
property :title, required: true
|
35
|
+
property :message_body, required: true
|
36
|
+
property :metadata
|
37
|
+
property :priority
|
38
|
+
property :expires_at
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "=" * 60
|
42
|
+
puts "SmartMessage pretty_print Method Demonstration"
|
43
|
+
puts "=" * 60
|
44
|
+
|
45
|
+
# Create an order message with complex nested data
|
46
|
+
puts "\n1. Order Message Example:"
|
47
|
+
puts "-" * 30
|
48
|
+
|
49
|
+
order = OrderMessage.new(
|
50
|
+
order_id: "ORD-2024-001",
|
51
|
+
customer_name: "Jane Smith",
|
52
|
+
customer_email: "jane.smith@example.com",
|
53
|
+
items: [
|
54
|
+
{
|
55
|
+
product_id: "PROD-123",
|
56
|
+
name: "Wireless Headphones",
|
57
|
+
quantity: 2,
|
58
|
+
unit_price: 99.99,
|
59
|
+
subtotal: 199.98
|
60
|
+
},
|
61
|
+
{
|
62
|
+
product_id: "PROD-456",
|
63
|
+
name: "Phone Case",
|
64
|
+
quantity: 1,
|
65
|
+
unit_price: 24.99,
|
66
|
+
subtotal: 24.99
|
67
|
+
}
|
68
|
+
],
|
69
|
+
total_amount: 224.97,
|
70
|
+
shipping_address: {
|
71
|
+
street: "123 Main St",
|
72
|
+
city: "Anytown",
|
73
|
+
state: "CA",
|
74
|
+
zip: "90210",
|
75
|
+
country: "USA"
|
76
|
+
},
|
77
|
+
payment_info: {
|
78
|
+
method: "credit_card",
|
79
|
+
last_four: "1234",
|
80
|
+
status: "authorized"
|
81
|
+
},
|
82
|
+
order_date: Time.now
|
83
|
+
)
|
84
|
+
|
85
|
+
puts ">"*22
|
86
|
+
ap order.to_h
|
87
|
+
puts "<"*22
|
88
|
+
|
89
|
+
order.pretty_print
|
90
|
+
|
91
|
+
# Create a user notification message
|
92
|
+
puts "\n\n2. User Notification Message Example:"
|
93
|
+
puts "-" * 40
|
94
|
+
|
95
|
+
notification = UserNotificationMessage.new(
|
96
|
+
user_id: "user_789",
|
97
|
+
notification_type: "order_update",
|
98
|
+
title: "Your Order Has Shipped!",
|
99
|
+
message_body: "Great news! Your order #ORD-2024-001 has been shipped and is on its way.",
|
100
|
+
metadata: {
|
101
|
+
tracking_number: "1Z999AA1234567890",
|
102
|
+
carrier: "UPS",
|
103
|
+
estimated_delivery: "2024-08-21",
|
104
|
+
order_id: "ORD-2024-001",
|
105
|
+
push_notification: true,
|
106
|
+
email_notification: true
|
107
|
+
},
|
108
|
+
priority: "high",
|
109
|
+
expires_at: Time.now + (7 * 24 * 60 * 60) # 7 days from now
|
110
|
+
)
|
111
|
+
|
112
|
+
notification.pretty_print
|
113
|
+
|
114
|
+
# Create a simple message to show minimal data
|
115
|
+
puts "\n\n3. Simple Message Example (Content Only):"
|
116
|
+
puts "-" * 40
|
117
|
+
|
118
|
+
class SimpleMessage < SmartMessage::Base
|
119
|
+
from 'greeting-service'
|
120
|
+
|
121
|
+
property :greeting, required: true
|
122
|
+
property :recipient
|
123
|
+
end
|
124
|
+
|
125
|
+
simple = SimpleMessage.new(
|
126
|
+
greeting: "Hello, World!",
|
127
|
+
recipient: "Everyone"
|
128
|
+
)
|
129
|
+
|
130
|
+
simple.pretty_print
|
131
|
+
|
132
|
+
puts "\n\n4. Message with Header Information:"
|
133
|
+
puts "-" * 40
|
134
|
+
puts "Using pretty_print(include_header: true) to show both header and content:\n"
|
135
|
+
|
136
|
+
simple.pretty_print(include_header: true)
|
137
|
+
|
138
|
+
puts "\n" + "=" * 60
|
139
|
+
puts "Demonstration Complete!"
|
140
|
+
puts "=" * 60
|
141
|
+
puts "\nThe pretty_print method has two modes:"
|
142
|
+
puts "• pretty_print() - Shows only message content (default)"
|
143
|
+
puts "• pretty_print(include_header: true) - Shows header + content"
|
144
|
+
puts "\nThis filters out internal SmartMessage properties and presents"
|
145
|
+
puts "the data in a beautifully formatted, readable way using amazing_print."
|
data/examples/temp.txt
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
=== SmartMessage Example: Point-to-Point Order Processing ===
|
2
|
+
|
3
|
+
🚀 Starting Order Processing Demo
|
4
|
+
🏪 OrderService: Starting up...
|
5
|
+
💳 PaymentService: Starting up...
|
6
|
+
|
7
|
+
============================================================
|
8
|
+
Processing Sample Orders
|
9
|
+
============================================================
|
10
|
+
|
11
|
+
--- Order 1 ---
|
12
|
+
|
13
|
+
🏪 OrderService: Creating order ORD-1001
|
14
|
+
🏪 OrderService: Sending order to payment processing...
|
15
|
+
|
16
|
+
===================================================
|
17
|
+
== SmartMessage Published via STDOUT Transport
|
18
|
+
== Single-Tier Serialization:
|
19
|
+
== Message Class: OrderMessage
|
20
|
+
== Serialized Message: {"_sm_header":{"uuid":"d9a7f410-b902-4a67-9e22-694d3bf01b5f","message_class":"OrderMessage","published_at":"2025-08-20 12:02:21 -0500","publisher_pid":50033,"version":1,"from":"OrderService","serializer":"SmartMessage::Serializer::Json"},"_sm_payload":{"order_id":"ORD-1001","customer_id":"CUST-001","amount":99.99,"currency":"USD","payment_method":"credit_card","items":["Widget A","Widget B"]}}
|
21
|
+
===================================================
|
22
|
+
|
23
|
+
💳 PaymentService: Starting up...
|
24
|
+
💳 PaymentService: Processing payment for order ORD-1001
|
25
|
+
💳 PaymentService: Sending payment response...
|
26
|
+
|
27
|
+
===================================================
|
28
|
+
== SmartMessage Published via STDOUT Transport
|
29
|
+
== Single-Tier Serialization:
|
30
|
+
== Message Class: PaymentResponseMessage
|
31
|
+
== Serialized Message: {"_sm_header":{"uuid":"ca09a331-d5b9-4250-ab4c-542a1b1db9c7","message_class":"PaymentResponseMessage","published_at":"2025-08-20 12:02:21 -0500","publisher_pid":50033,"version":1,"from":"PaymentService","serializer":"SmartMessage::Serializer::Json"},"_sm_payload":{"order_id":"ORD-1001","payment_id":"PAY-5001","status":"success","message":"Payment processed successfully","processed_at":"2025-08-20T12:02:21-05:00"}}
|
32
|
+
===================================================
|
33
|
+
|
34
|
+
✅ Payment success: Order ORD-1001 - Payment processed successfully
|
35
|
+
|
36
|
+
--- Order 2 ---
|
37
|
+
|
38
|
+
🏪 OrderService: Creating order ORD-1002
|
39
|
+
🏪 OrderService: Sending order to payment processing...
|
40
|
+
|
41
|
+
===================================================
|
42
|
+
== SmartMessage Published via STDOUT Transport
|
43
|
+
== Single-Tier Serialization:
|
44
|
+
== Message Class: OrderMessage
|
45
|
+
== Serialized Message: {"_sm_header":{"uuid":"85ee7a93-ad19-48b3-9366-b6ed6dbb9e0e","message_class":"OrderMessage","published_at":"2025-08-20 12:02:22 -0500","publisher_pid":50033,"version":1,"from":"OrderService","serializer":"SmartMessage::Serializer::Json"},"_sm_payload":{"order_id":"ORD-1002","customer_id":"CUST-002","amount":1299.99,"currency":"USD","payment_method":"debit_card","items":["Premium Widget","Extended Warranty"]}}
|
46
|
+
===================================================
|
47
|
+
|
48
|
+
💳 PaymentService: Starting up...
|
49
|
+
💳 PaymentService: Processing payment for order ORD-1002
|
50
|
+
💳 PaymentService: Sending payment response...
|
51
|
+
|
52
|
+
===================================================
|
53
|
+
== SmartMessage Published via STDOUT Transport
|
54
|
+
== Single-Tier Serialization:
|
55
|
+
== Message Class: PaymentResponseMessage
|
56
|
+
== Serialized Message: {"_sm_header":{"uuid":"773c1dc9-5ee2-4866-a5d0-95701f436250","message_class":"PaymentResponseMessage","published_at":"2025-08-20 12:02:22 -0500","publisher_pid":50033,"version":1,"from":"PaymentService","serializer":"SmartMessage::Serializer::Json"},"_sm_payload":{"order_id":"ORD-1002","payment_id":"PAY-5001","status":"failed","message":"Insufficient funds","processed_at":"2025-08-20T12:02:22-05:00"}}
|
57
|
+
===================================================
|
58
|
+
|
59
|
+
❌ Payment failed: Order ORD-1002 - Insufficient funds
|
60
|
+
|
61
|
+
--- Order 3 ---
|
62
|
+
|
63
|
+
🏪 OrderService: Creating order ORD-1003
|
64
|
+
🏪 OrderService: Sending order to payment processing...
|
65
|
+
|
66
|
+
===================================================
|
67
|
+
== SmartMessage Published via STDOUT Transport
|
68
|
+
== Single-Tier Serialization:
|
69
|
+
== Message Class: OrderMessage
|
70
|
+
== Serialized Message: {"_sm_header":{"uuid":"5b3cb763-8c3e-470f-b6f9-dd5e0929f926","message_class":"OrderMessage","published_at":"2025-08-20 12:02:22 -0500","publisher_pid":50033,"version":1,"from":"OrderService","serializer":"SmartMessage::Serializer::Json"},"_sm_payload":{"order_id":"ORD-1003","customer_id":"CUST-003","amount":45.5,"currency":"USD","payment_method":"paypal","items":["Small Widget"]}}
|
71
|
+
===================================================
|
72
|
+
|
73
|
+
💳 PaymentService: Starting up...
|
74
|
+
💳 PaymentService: Processing payment for order ORD-1003
|
75
|
+
💳 PaymentService: Sending payment response...
|
76
|
+
|
77
|
+
===================================================
|
78
|
+
== SmartMessage Published via STDOUT Transport
|
79
|
+
== Single-Tier Serialization:
|
80
|
+
== Message Class: PaymentResponseMessage
|
81
|
+
== Serialized Message: {"_sm_header":{"uuid":"dc35ea12-28a5-4926-854a-d2cc52f6c91b","message_class":"PaymentResponseMessage","published_at":"2025-08-20 12:02:22 -0500","publisher_pid":50033,"version":1,"from":"PaymentService","serializer":"SmartMessage::Serializer::Json"},"_sm_payload":{"order_id":"ORD-1003","payment_id":"PAY-5001","status":"success","message":"Payment processed successfully","processed_at":"2025-08-20T12:02:22-05:00"}}
|
82
|
+
===================================================
|
83
|
+
|
84
|
+
✅ Payment success: Order ORD-1003 - Payment processed successfully
|
85
|
+
|
86
|
+
⏳ Waiting for all payments to process...
|
87
|
+
|
88
|
+
✨ Demo completed!
|
89
|
+
|
90
|
+
This example demonstrated:
|
91
|
+
• Point-to-point messaging between OrderService and PaymentService
|
92
|
+
• Bidirectional communication with request/response pattern
|
93
|
+
• JSON serialization of complex message data
|
94
|
+
• STDOUT transport with loopback for local demonstration
|
@@ -53,7 +53,8 @@ class BotChatAgent < BaseAgent
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
def handle_bot_command(
|
56
|
+
def handle_bot_command(wrapper)
|
57
|
+
message_header, message_payload = wrapper.split
|
57
58
|
command_data = JSON.parse(message_payload)
|
58
59
|
|
59
60
|
# Only handle commands in rooms we're in and commands we can handle
|
@@ -66,7 +67,8 @@ class BotChatAgent < BaseAgent
|
|
66
67
|
process_command(command_data)
|
67
68
|
end
|
68
69
|
|
69
|
-
def handle_chat_message(
|
70
|
+
def handle_chat_message(wrapper)
|
71
|
+
message_header, message_payload = wrapper.split
|
70
72
|
chat_data = JSON.parse(message_payload)
|
71
73
|
|
72
74
|
# Only process messages from rooms we're in and not our own messages
|
@@ -69,7 +69,8 @@ class HumanChatAgent < BaseAgent
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
def handle_chat_message(
|
72
|
+
def handle_chat_message(wrapper)
|
73
|
+
message_header, message_payload = wrapper.split
|
73
74
|
chat_data = JSON.parse(message_payload)
|
74
75
|
|
75
76
|
# Only process messages from rooms we're in and not our own messages
|
@@ -90,7 +91,8 @@ class HumanChatAgent < BaseAgent
|
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
93
|
-
def handle_system_notification(
|
94
|
+
def handle_system_notification(wrapper)
|
95
|
+
message_header, message_payload = wrapper.split
|
94
96
|
notif_data = JSON.parse(message_payload)
|
95
97
|
|
96
98
|
# Only process notifications from rooms we're in
|
@@ -62,7 +62,8 @@ class RoomMonitor < BaseAgent
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
def handle_chat_message(
|
65
|
+
def handle_chat_message(wrapper)
|
66
|
+
message_header, message_payload = wrapper.split
|
66
67
|
chat_data = JSON.parse(message_payload)
|
67
68
|
|
68
69
|
return unless chat_data['room_id'] == @room_id
|
@@ -93,7 +94,8 @@ class RoomMonitor < BaseAgent
|
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
96
|
-
def handle_system_notification(
|
97
|
+
def handle_system_notification(wrapper)
|
98
|
+
message_header, message_payload = wrapper.split
|
97
99
|
notif_data = JSON.parse(message_payload)
|
98
100
|
|
99
101
|
return unless notif_data['room_id'] == @room_id
|
@@ -128,7 +128,8 @@ class ChatMessage < SmartMessage::Base
|
|
128
128
|
serializer SmartMessage::Serializer::JSON.new
|
129
129
|
end
|
130
130
|
|
131
|
-
def self.process(
|
131
|
+
def self.process(wrapper)
|
132
|
+
message_header, message_payload = wrapper.split
|
132
133
|
# Default processing - agents will override this
|
133
134
|
end
|
134
135
|
end
|
@@ -157,7 +158,8 @@ class BotCommandMessage < SmartMessage::Base
|
|
157
158
|
serializer SmartMessage::Serializer::JSON.new
|
158
159
|
end
|
159
160
|
|
160
|
-
def self.process(
|
161
|
+
def self.process(wrapper)
|
162
|
+
message_header, message_payload = wrapper.split
|
161
163
|
# Default processing - bots will override this
|
162
164
|
end
|
163
165
|
end
|
@@ -184,7 +186,8 @@ class SystemNotificationMessage < SmartMessage::Base
|
|
184
186
|
serializer SmartMessage::Serializer::JSON.new
|
185
187
|
end
|
186
188
|
|
187
|
-
def self.process(
|
189
|
+
def self.process(wrapper)
|
190
|
+
message_header, message_payload = wrapper.split
|
188
191
|
# Default processing
|
189
192
|
end
|
190
193
|
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
# lib/smart_message/addressing.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module SmartMessage
|
6
|
+
# Addressing configuration module for SmartMessage::Base
|
7
|
+
# Handles from, to, and reply_to addressing at both class and instance levels
|
8
|
+
module Addressing
|
9
|
+
|
10
|
+
# DSL context for header block processing
|
11
|
+
class HeaderDSL
|
12
|
+
def initialize(message_class)
|
13
|
+
@message_class = message_class
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set default from value for this message class
|
17
|
+
def from(entity_id)
|
18
|
+
@message_class.from(entity_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set default to value for this message class
|
22
|
+
def to(entity_id)
|
23
|
+
@message_class.to(entity_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Set default reply_to value for this message class
|
27
|
+
def reply_to(entity_id)
|
28
|
+
@message_class.reply_to(entity_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
def self.included(base)
|
32
|
+
base.extend(ClassMethods)
|
33
|
+
base.class_eval do
|
34
|
+
# Class-level addressing configuration - use a registry for per-class isolation
|
35
|
+
class_variable_set(:@@addressing_registry, {}) unless class_variable_defined?(:@@addressing_registry)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#########################################################
|
40
|
+
## instance-level addressing configuration
|
41
|
+
|
42
|
+
def from(entity_id = nil)
|
43
|
+
if entity_id.nil?
|
44
|
+
@from || self.class.from
|
45
|
+
else
|
46
|
+
@from = entity_id
|
47
|
+
# Update the header with the new value
|
48
|
+
_sm_header.from = entity_id if _sm_header
|
49
|
+
self # Return self for method chaining
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Setter shortcut for cleaner assignment syntax
|
54
|
+
def from=(entity_id)
|
55
|
+
@from = entity_id
|
56
|
+
_sm_header.from = entity_id if _sm_header
|
57
|
+
end
|
58
|
+
|
59
|
+
def from_configured?; !from.nil?; end
|
60
|
+
def from_missing?; from.nil?; end
|
61
|
+
def reset_from;
|
62
|
+
@from = nil
|
63
|
+
_sm_header.from = nil if _sm_header
|
64
|
+
end
|
65
|
+
|
66
|
+
def to(entity_id = nil)
|
67
|
+
if entity_id.nil?
|
68
|
+
@to || self.class.to
|
69
|
+
else
|
70
|
+
@to = entity_id
|
71
|
+
# Update the header with the new value
|
72
|
+
_sm_header.to = entity_id if _sm_header
|
73
|
+
self # Return self for method chaining
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Setter shortcut for cleaner assignment syntax
|
78
|
+
def to=(entity_id)
|
79
|
+
@to = entity_id
|
80
|
+
_sm_header.to = entity_id if _sm_header
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_configured?; !to.nil?; end
|
84
|
+
def to_missing?; to.nil?; end
|
85
|
+
def reset_to;
|
86
|
+
@to = nil
|
87
|
+
_sm_header.to = nil if _sm_header
|
88
|
+
end
|
89
|
+
|
90
|
+
def reply_to(entity_id = nil)
|
91
|
+
if entity_id.nil?
|
92
|
+
@reply_to || self.class.reply_to
|
93
|
+
else
|
94
|
+
@reply_to = entity_id
|
95
|
+
# Update the header with the new value
|
96
|
+
_sm_header.reply_to = entity_id if _sm_header
|
97
|
+
self # Return self for method chaining
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Setter shortcut for cleaner assignment syntax
|
102
|
+
def reply_to=(entity_id)
|
103
|
+
@reply_to = entity_id
|
104
|
+
_sm_header.reply_to = entity_id if _sm_header
|
105
|
+
end
|
106
|
+
|
107
|
+
def reply_to_configured?; !reply_to.nil?; end
|
108
|
+
def reply_to_missing?; reply_to.nil?; end
|
109
|
+
def reset_reply_to;
|
110
|
+
@reply_to = nil
|
111
|
+
_sm_header.reply_to = nil if _sm_header
|
112
|
+
end
|
113
|
+
|
114
|
+
module ClassMethods
|
115
|
+
#########################################################
|
116
|
+
## Header DSL support
|
117
|
+
|
118
|
+
# Header DSL block processor
|
119
|
+
# Allows: header do; from "service"; to "target"; end
|
120
|
+
def header(*args, &block)
|
121
|
+
# Handle the case where this might be called with unexpected arguments
|
122
|
+
# This helps with IRB compatibility issues
|
123
|
+
if args.length > 0
|
124
|
+
# If called with arguments, this might be an IRB inspection issue
|
125
|
+
# Try to delegate gracefully or return nil
|
126
|
+
return nil
|
127
|
+
end
|
128
|
+
|
129
|
+
if block_given?
|
130
|
+
# Create a DSL context to capture header configuration
|
131
|
+
dsl = HeaderDSL.new(self)
|
132
|
+
dsl.instance_eval(&block)
|
133
|
+
else
|
134
|
+
# No block provided - this is an error for class-level usage
|
135
|
+
raise ArgumentError, "header() at class level requires a block. Use: header do; from 'value'; end"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#########################################################
|
140
|
+
## class-level addressing configuration
|
141
|
+
|
142
|
+
# Helper method to normalize filter values (string -> array, nil -> nil)
|
143
|
+
private def normalize_filter_value(value)
|
144
|
+
case value
|
145
|
+
when nil
|
146
|
+
nil
|
147
|
+
when String, Regexp
|
148
|
+
[value]
|
149
|
+
when Array
|
150
|
+
# Validate that array contains only Strings and Regexps
|
151
|
+
value.each do |item|
|
152
|
+
unless item.is_a?(String) || item.is_a?(Regexp)
|
153
|
+
raise ArgumentError, "Array filter values must be Strings or Regexps, got: #{item.class}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
value
|
157
|
+
else
|
158
|
+
raise ArgumentError, "Filter value must be a String, Regexp, Array, or nil, got: #{value.class}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Helper method to find addressing values in the inheritance chain
|
163
|
+
private def find_addressing_value(field)
|
164
|
+
# Start with current class
|
165
|
+
current_class = self
|
166
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
167
|
+
|
168
|
+
while current_class && current_class.respond_to?(:name)
|
169
|
+
class_name = current_class.name || current_class.to_s
|
170
|
+
|
171
|
+
# Check registry for this class
|
172
|
+
result = addressing_registry.dig(class_name, field)
|
173
|
+
return result if result
|
174
|
+
|
175
|
+
# If we have a proper name but no result, also check the to_s version
|
176
|
+
if current_class.name
|
177
|
+
alternative_key = current_class.to_s
|
178
|
+
result = addressing_registry.dig(alternative_key, field)
|
179
|
+
return result if result
|
180
|
+
end
|
181
|
+
|
182
|
+
# Move up the inheritance chain
|
183
|
+
current_class = current_class.superclass
|
184
|
+
|
185
|
+
# Stop if we reach SmartMessage::Base or above
|
186
|
+
break if current_class == SmartMessage::Base || current_class.nil?
|
187
|
+
end
|
188
|
+
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
|
192
|
+
def from(entity_id = nil)
|
193
|
+
class_name = self.name || self.to_s
|
194
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
195
|
+
if entity_id.nil?
|
196
|
+
# Try to find the value, checking inheritance chain
|
197
|
+
result = find_addressing_value(:from)
|
198
|
+
result
|
199
|
+
else
|
200
|
+
addressing_registry[class_name] ||= {}
|
201
|
+
addressing_registry[class_name][:from] = entity_id
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def from_configured?; !from.nil?; end
|
206
|
+
def from_missing?; from.nil?; end
|
207
|
+
def reset_from;
|
208
|
+
class_name = self.name || self.to_s
|
209
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
210
|
+
addressing_registry[class_name] ||= {}
|
211
|
+
addressing_registry[class_name][:from] = nil
|
212
|
+
end
|
213
|
+
|
214
|
+
def to(entity_id = nil)
|
215
|
+
class_name = self.name || self.to_s
|
216
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
217
|
+
if entity_id.nil?
|
218
|
+
# Try to find the value, checking inheritance chain
|
219
|
+
result = find_addressing_value(:to)
|
220
|
+
result
|
221
|
+
else
|
222
|
+
addressing_registry[class_name] ||= {}
|
223
|
+
addressing_registry[class_name][:to] = entity_id
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def to_configured?; !to.nil?; end
|
228
|
+
def to_missing?; to.nil?; end
|
229
|
+
def reset_to;
|
230
|
+
class_name = self.name || self.to_s
|
231
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
232
|
+
addressing_registry[class_name] ||= {}
|
233
|
+
addressing_registry[class_name][:to] = nil
|
234
|
+
end
|
235
|
+
|
236
|
+
def reply_to(entity_id = nil)
|
237
|
+
class_name = self.name || self.to_s
|
238
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
239
|
+
if entity_id.nil?
|
240
|
+
# Try to find the value, checking inheritance chain
|
241
|
+
result = find_addressing_value(:reply_to)
|
242
|
+
result
|
243
|
+
else
|
244
|
+
addressing_registry[class_name] ||= {}
|
245
|
+
addressing_registry[class_name][:reply_to] = entity_id
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def reply_to_configured?; !reply_to.nil?; end
|
250
|
+
def reply_to_missing?; reply_to.nil?; end
|
251
|
+
def reset_reply_to;
|
252
|
+
class_name = self.name || self.to_s
|
253
|
+
addressing_registry = class_variable_get(:@@addressing_registry)
|
254
|
+
addressing_registry[class_name] ||= {}
|
255
|
+
addressing_registry[class_name][:reply_to] = nil
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|