smart_message 0.0.4 ā 0.0.6
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/CHANGELOG.md +118 -0
- data/Gemfile.lock +1 -1
- data/README.md +299 -10
- data/docs/README.md +1 -0
- data/docs/addressing.md +364 -0
- data/docs/architecture.md +2 -0
- data/docs/examples.md +2 -0
- data/docs/getting-started.md +49 -0
- data/docs/properties.md +213 -7
- data/examples/01_point_to_point_orders.rb +27 -11
- data/examples/02_publish_subscribe_events.rb +16 -7
- data/examples/03_many_to_many_chat.rb +56 -22
- data/examples/04_redis_smart_home_iot.rb +48 -21
- data/examples/05_proc_handlers.rb +12 -5
- data/examples/06_custom_logger_example.rb +34 -13
- data/examples/07_error_handling_scenarios.rb +477 -0
- data/examples/08_entity_addressing_basic.rb +366 -0
- data/examples/08_entity_addressing_with_filtering.rb +418 -0
- data/examples/README.md +68 -0
- data/examples/tmux_chat/bot_agent.rb +4 -1
- data/examples/tmux_chat/shared_chat_system.rb +50 -22
- data/lib/smart_message/base.rb +306 -20
- data/lib/smart_message/dispatcher.rb +53 -5
- data/lib/smart_message/errors.rb +3 -0
- data/lib/smart_message/header.rb +46 -5
- data/lib/smart_message/property_descriptions.rb +5 -4
- data/lib/smart_message/property_validations.rb +141 -0
- data/lib/smart_message/transport/base.rb +3 -2
- data/lib/smart_message/transport/redis_transport.rb +2 -2
- data/lib/smart_message/version.rb +1 -1
- metadata +6 -1
@@ -0,0 +1,418 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/08_entity_addressing_with_filtering.rb
|
3
|
+
#
|
4
|
+
# Demonstrates SmartMessage entity addressing and filtering capabilities including:
|
5
|
+
# - Point-to-point messaging with FROM/TO fields
|
6
|
+
# - Broadcast messaging (no TO field)
|
7
|
+
# - Entity-aware subscription filtering
|
8
|
+
# - Request-reply patterns with REPLY_TO
|
9
|
+
# - Instance-level addressing overrides
|
10
|
+
# - Gateway patterns
|
11
|
+
|
12
|
+
require_relative '../lib/smart_message'
|
13
|
+
|
14
|
+
puts "šÆ SmartMessage Entity Addressing & Filtering Demo"
|
15
|
+
puts "=" * 50
|
16
|
+
|
17
|
+
# Configure transport for demo
|
18
|
+
transport = SmartMessage::Transport.create(:stdout, loopback: true)
|
19
|
+
serializer = SmartMessage::Serializer::JSON.new
|
20
|
+
|
21
|
+
# =============================================================================
|
22
|
+
# Example 1: Entity-Aware Message Filtering
|
23
|
+
# =============================================================================
|
24
|
+
|
25
|
+
puts "\nš Example 1: Entity-Aware Message Filtering"
|
26
|
+
puts "-" * 40
|
27
|
+
|
28
|
+
class ServiceMessage < SmartMessage::Base
|
29
|
+
version 1
|
30
|
+
description "Messages between microservices with filtering"
|
31
|
+
|
32
|
+
from 'sender-service'
|
33
|
+
|
34
|
+
property :message_type, required: true
|
35
|
+
property :data, required: true
|
36
|
+
property :timestamp, default: -> { Time.now.to_s }
|
37
|
+
|
38
|
+
config do
|
39
|
+
transport SmartMessage::Transport.create(:stdout, loopback: true)
|
40
|
+
serializer SmartMessage::Serializer::JSON.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# Different handlers for different subscription filters
|
44
|
+
def self.process_broadcast(header, payload)
|
45
|
+
data = JSON.parse(payload)
|
46
|
+
puts " š» BROADCAST HANDLER received:"
|
47
|
+
puts " Type: #{data['message_type']}"
|
48
|
+
puts " From: #{header.from}, To: #{header.to || 'ALL'}"
|
49
|
+
puts " Data: #{data['data']}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.process_directed(header, payload)
|
53
|
+
data = JSON.parse(payload)
|
54
|
+
puts " šÆ DIRECTED HANDLER received:"
|
55
|
+
puts " Type: #{data['message_type']}"
|
56
|
+
puts " From: #{header.from} ā To: #{header.to}"
|
57
|
+
puts " Data: #{data['data']}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.process_from_admin(header, payload)
|
61
|
+
data = JSON.parse(payload)
|
62
|
+
puts " š® ADMIN HANDLER received:"
|
63
|
+
puts " Type: #{data['message_type']}"
|
64
|
+
puts " From: #{header.from} (ADMIN)"
|
65
|
+
puts " Data: #{data['data']}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Subscribe with different filters
|
70
|
+
puts "\nš Setting up filtered subscriptions:"
|
71
|
+
|
72
|
+
# Subscribe to broadcast messages only
|
73
|
+
puts " 1. Subscribing to broadcast messages only..."
|
74
|
+
ServiceMessage.subscribe('ServiceMessage.process_broadcast', broadcast: true)
|
75
|
+
|
76
|
+
# Subscribe to messages directed to 'my-service'
|
77
|
+
puts " 2. Subscribing to messages for 'my-service' only..."
|
78
|
+
ServiceMessage.subscribe('ServiceMessage.process_directed', to: 'my-service')
|
79
|
+
|
80
|
+
# Subscribe to messages from 'admin-service'
|
81
|
+
puts " 3. Subscribing to messages from 'admin-service'..."
|
82
|
+
ServiceMessage.subscribe('ServiceMessage.process_from_admin', from: 'admin-service')
|
83
|
+
|
84
|
+
# Test different message types
|
85
|
+
puts "\nš¤ Publishing test messages..."
|
86
|
+
|
87
|
+
# Broadcast message - should only be received by broadcast handler
|
88
|
+
broadcast_msg = ServiceMessage.new(
|
89
|
+
message_type: 'system_announcement',
|
90
|
+
data: 'System maintenance at 2 AM'
|
91
|
+
)
|
92
|
+
broadcast_msg.to(nil) # Explicitly set as broadcast
|
93
|
+
puts "\n1. Publishing broadcast message (no 'to' field)..."
|
94
|
+
broadcast_msg.publish
|
95
|
+
sleep(0.2) # Allow time for handlers to process
|
96
|
+
|
97
|
+
# Directed message to 'my-service' - should only be received by directed handler
|
98
|
+
directed_msg = ServiceMessage.new(
|
99
|
+
message_type: 'service_update',
|
100
|
+
data: 'Update your configuration'
|
101
|
+
)
|
102
|
+
directed_msg.to('my-service')
|
103
|
+
puts "\n2. Publishing message to 'my-service'..."
|
104
|
+
directed_msg.publish
|
105
|
+
sleep(0.2) # Allow time for handlers to process
|
106
|
+
|
107
|
+
# Directed message to different service - should NOT be received
|
108
|
+
other_msg = ServiceMessage.new(
|
109
|
+
message_type: 'other_update',
|
110
|
+
data: 'This is for another service'
|
111
|
+
)
|
112
|
+
other_msg.to('other-service')
|
113
|
+
puts "\n3. Publishing message to 'other-service' (should not be received)..."
|
114
|
+
other_msg.publish
|
115
|
+
sleep(0.2) # Allow time to confirm no handlers process this
|
116
|
+
|
117
|
+
# Message from admin - should only be received by admin handler
|
118
|
+
admin_msg = ServiceMessage.new(
|
119
|
+
message_type: 'admin_command',
|
120
|
+
data: 'Restart all services'
|
121
|
+
)
|
122
|
+
admin_msg.from('admin-service')
|
123
|
+
admin_msg.to('my-service')
|
124
|
+
puts "\n4. Publishing message from 'admin-service'..."
|
125
|
+
admin_msg.publish
|
126
|
+
sleep(0.2) # Allow time for handlers to process
|
127
|
+
|
128
|
+
# =============================================================================
|
129
|
+
# Example 2: Combined Filters
|
130
|
+
# =============================================================================
|
131
|
+
|
132
|
+
puts "\nš Example 2: Combined Subscription Filters"
|
133
|
+
puts "-" * 40
|
134
|
+
|
135
|
+
class AlertMessage < SmartMessage::Base
|
136
|
+
version 1
|
137
|
+
description "Alert messages with combined filtering"
|
138
|
+
|
139
|
+
from 'alert-service' # Default from field
|
140
|
+
|
141
|
+
property :severity, required: true
|
142
|
+
property :alert_text, required: true
|
143
|
+
property :source_system
|
144
|
+
|
145
|
+
config do
|
146
|
+
transport SmartMessage::Transport.create(:stdout, loopback: true)
|
147
|
+
serializer SmartMessage::Serializer::JSON.new
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.process_critical_or_broadcast(header, payload)
|
151
|
+
data = JSON.parse(payload)
|
152
|
+
icon = data['severity'] == 'critical' ? 'šØ' : 'š¢'
|
153
|
+
puts " #{icon} ALERT MONITOR received:"
|
154
|
+
puts " Severity: #{data['severity'].upcase}"
|
155
|
+
puts " From: #{header.from}, To: #{header.to || 'ALL'}"
|
156
|
+
puts " Alert: #{data['alert_text']}"
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.process_from_monitoring(header, payload)
|
160
|
+
data = JSON.parse(payload)
|
161
|
+
puts " š MONITORING TEAM received:"
|
162
|
+
puts " From: #{header.from} (monitoring system)"
|
163
|
+
puts " Severity: #{data['severity']}"
|
164
|
+
puts " Alert: #{data['alert_text']}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Clear previous subscriptions
|
169
|
+
AlertMessage.unsubscribe!
|
170
|
+
|
171
|
+
# Subscribe to broadcasts OR messages to 'alert-service'
|
172
|
+
puts "\nš Setting up combined filter subscriptions:"
|
173
|
+
puts " 1. Subscribe to broadcasts OR messages to 'alert-service'..."
|
174
|
+
AlertMessage.subscribe(
|
175
|
+
'AlertMessage.process_critical_or_broadcast',
|
176
|
+
broadcast: true,
|
177
|
+
to: 'alert-service'
|
178
|
+
)
|
179
|
+
|
180
|
+
# Subscribe to messages from specific monitoring systems
|
181
|
+
puts " 2. Subscribe to messages from monitoring systems..."
|
182
|
+
AlertMessage.subscribe(
|
183
|
+
'AlertMessage.process_from_monitoring',
|
184
|
+
from: ['monitoring-system-1', 'monitoring-system-2']
|
185
|
+
)
|
186
|
+
|
187
|
+
# Test combined filters
|
188
|
+
puts "\nš¤ Publishing alert messages..."
|
189
|
+
|
190
|
+
# Broadcast alert - should be received by first handler
|
191
|
+
broadcast_alert = AlertMessage.new(
|
192
|
+
severity: 'warning',
|
193
|
+
alert_text: 'CPU usage high across cluster',
|
194
|
+
source_system: 'cluster-monitor'
|
195
|
+
)
|
196
|
+
broadcast_alert.from('monitoring-system-1')
|
197
|
+
broadcast_alert.to(nil) # Broadcast
|
198
|
+
puts "\n1. Broadcasting alert..."
|
199
|
+
broadcast_alert.publish
|
200
|
+
sleep(0.2) # Allow time for handlers to process
|
201
|
+
|
202
|
+
# Directed alert to 'alert-service' - should be received by first handler
|
203
|
+
directed_alert = AlertMessage.new(
|
204
|
+
severity: 'critical',
|
205
|
+
alert_text: 'Database connection lost',
|
206
|
+
source_system: 'db-monitor'
|
207
|
+
)
|
208
|
+
directed_alert.from('monitoring-system-2')
|
209
|
+
directed_alert.to('alert-service')
|
210
|
+
puts "\n2. Sending critical alert to 'alert-service'..."
|
211
|
+
directed_alert.publish
|
212
|
+
sleep(0.2) # Allow time for handlers to process
|
213
|
+
|
214
|
+
# Alert to different service - should only be received by monitoring handler
|
215
|
+
other_alert = AlertMessage.new(
|
216
|
+
severity: 'info',
|
217
|
+
alert_text: 'Backup completed successfully',
|
218
|
+
source_system: 'backup-system'
|
219
|
+
)
|
220
|
+
other_alert.from('monitoring-system-1')
|
221
|
+
other_alert.to('backup-service')
|
222
|
+
puts "\n3. Sending info alert to 'backup-service'..."
|
223
|
+
other_alert.publish
|
224
|
+
sleep(0.2) # Allow time for handlers to process
|
225
|
+
|
226
|
+
# =============================================================================
|
227
|
+
# Example 3: Point-to-Point with Filtering
|
228
|
+
# =============================================================================
|
229
|
+
|
230
|
+
puts "\nš” Example 3: Point-to-Point Messaging with Filtering"
|
231
|
+
puts "-" * 40
|
232
|
+
|
233
|
+
class OrderMessage < SmartMessage::Base
|
234
|
+
version 1
|
235
|
+
description "Order processing with selective subscription"
|
236
|
+
|
237
|
+
from 'order-service'
|
238
|
+
to 'fulfillment-service'
|
239
|
+
reply_to 'order-service'
|
240
|
+
|
241
|
+
property :order_id, required: true
|
242
|
+
property :priority, default: 'normal'
|
243
|
+
property :items, required: true
|
244
|
+
property :total_amount, required: true
|
245
|
+
|
246
|
+
config do
|
247
|
+
transport SmartMessage::Transport.create(:stdout, loopback: true)
|
248
|
+
serializer SmartMessage::Serializer::JSON.new
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.process_high_priority(header, payload)
|
252
|
+
data = JSON.parse(payload)
|
253
|
+
puts " š HIGH PRIORITY ORDER HANDLER:"
|
254
|
+
puts " Order ID: #{data['order_id']} (PRIORITY: #{data['priority'].upcase})"
|
255
|
+
puts " From: #{header.from} ā To: #{header.to}"
|
256
|
+
puts " Total: $#{data['total_amount']}"
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.process_normal(header, payload)
|
260
|
+
data = JSON.parse(payload)
|
261
|
+
puts " š¦ NORMAL ORDER HANDLER:"
|
262
|
+
puts " Order ID: #{data['order_id']}"
|
263
|
+
puts " From: #{header.from} ā To: #{header.to}"
|
264
|
+
puts " Total: $#{data['total_amount']}"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Clear and set up filtered subscriptions
|
269
|
+
OrderMessage.unsubscribe!
|
270
|
+
|
271
|
+
puts "\nš Setting up order processing subscriptions:"
|
272
|
+
# Only the fulfillment service subscribes
|
273
|
+
puts " 1. Fulfillment service subscribes to orders..."
|
274
|
+
OrderMessage.subscribe('OrderMessage.process_normal', to: 'fulfillment-service')
|
275
|
+
|
276
|
+
# High-priority team also monitors high-value orders
|
277
|
+
puts " 2. Priority team subscribes to high-value orders..."
|
278
|
+
OrderMessage.subscribe('OrderMessage.process_high_priority', to: 'fulfillment-service')
|
279
|
+
|
280
|
+
# Send different types of orders
|
281
|
+
puts "\nš¤ Publishing orders..."
|
282
|
+
|
283
|
+
# Normal order to fulfillment
|
284
|
+
normal_order = OrderMessage.new(
|
285
|
+
order_id: "ORD-001",
|
286
|
+
priority: 'normal',
|
287
|
+
items: ["Widget A", "Widget B"],
|
288
|
+
total_amount: 99.99
|
289
|
+
)
|
290
|
+
puts "\n1. Publishing normal order to fulfillment..."
|
291
|
+
normal_order.publish
|
292
|
+
sleep(0.2) # Allow time for handlers to process
|
293
|
+
|
294
|
+
# High priority order
|
295
|
+
high_priority_order = OrderMessage.new(
|
296
|
+
order_id: "ORD-002",
|
297
|
+
priority: 'high',
|
298
|
+
items: ["Premium Widget", "Express Gadget"],
|
299
|
+
total_amount: 999.99
|
300
|
+
)
|
301
|
+
puts "\n2. Publishing high-priority order..."
|
302
|
+
high_priority_order.publish
|
303
|
+
sleep(0.2) # Allow time for handlers to process
|
304
|
+
|
305
|
+
# Order to different service (should not be received)
|
306
|
+
misrouted_order = OrderMessage.new(
|
307
|
+
order_id: "ORD-003",
|
308
|
+
priority: 'normal',
|
309
|
+
items: ["Test Item"],
|
310
|
+
total_amount: 50.00
|
311
|
+
)
|
312
|
+
misrouted_order.to('wrong-service')
|
313
|
+
puts "\n3. Publishing order to 'wrong-service' (should not be received)..."
|
314
|
+
misrouted_order.publish
|
315
|
+
sleep(0.2) # Allow time to confirm no handlers process this
|
316
|
+
|
317
|
+
# =============================================================================
|
318
|
+
# Example 4: Request-Reply with Filtering
|
319
|
+
# =============================================================================
|
320
|
+
|
321
|
+
puts "\nš Example 4: Request-Reply Pattern with Filtering"
|
322
|
+
puts "-" * 40
|
323
|
+
|
324
|
+
class ServiceRequest < SmartMessage::Base
|
325
|
+
version 1
|
326
|
+
description "Service requests with filtered responses"
|
327
|
+
|
328
|
+
from 'request-service' # Default from field
|
329
|
+
|
330
|
+
property :request_id, required: true
|
331
|
+
property :request_type, required: true
|
332
|
+
property :data
|
333
|
+
|
334
|
+
config do
|
335
|
+
transport SmartMessage::Transport.create(:stdout, loopback: true)
|
336
|
+
serializer SmartMessage::Serializer::JSON.new
|
337
|
+
end
|
338
|
+
|
339
|
+
def self.process_api_requests(header, payload)
|
340
|
+
data = JSON.parse(payload)
|
341
|
+
puts " š API SERVICE received request:"
|
342
|
+
puts " Request ID: #{data['request_id']}"
|
343
|
+
puts " Type: #{data['request_type']}"
|
344
|
+
puts " From: #{header.from} ā To: #{header.to}"
|
345
|
+
puts " Reply To: #{header.reply_to}"
|
346
|
+
end
|
347
|
+
|
348
|
+
def self.process_data_requests(header, payload)
|
349
|
+
data = JSON.parse(payload)
|
350
|
+
puts " š¾ DATA SERVICE received request:"
|
351
|
+
puts " Request ID: #{data['request_id']}"
|
352
|
+
puts " Type: #{data['request_type']}"
|
353
|
+
puts " From: #{header.from} ā To: #{header.to}"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
ServiceRequest.unsubscribe!
|
358
|
+
|
359
|
+
puts "\nš Setting up service request routing:"
|
360
|
+
# API service only handles requests directed to it
|
361
|
+
puts " 1. API service subscribes to its requests..."
|
362
|
+
ServiceRequest.subscribe('ServiceRequest.process_api_requests', to: 'api-service')
|
363
|
+
|
364
|
+
# Data service handles data requests
|
365
|
+
puts " 2. Data service subscribes to its requests..."
|
366
|
+
ServiceRequest.subscribe('ServiceRequest.process_data_requests', to: 'data-service')
|
367
|
+
|
368
|
+
puts "\nš¤ Publishing service requests..."
|
369
|
+
|
370
|
+
# API request
|
371
|
+
api_request = ServiceRequest.new(
|
372
|
+
request_id: SecureRandom.uuid,
|
373
|
+
request_type: 'user_lookup',
|
374
|
+
data: { user_id: 'USER-123' }
|
375
|
+
)
|
376
|
+
api_request.from('web-frontend')
|
377
|
+
api_request.to('api-service')
|
378
|
+
api_request.reply_to('web-frontend')
|
379
|
+
puts "\n1. Publishing API request..."
|
380
|
+
api_request.publish
|
381
|
+
sleep(0.2) # Allow time for handlers to process
|
382
|
+
|
383
|
+
# Data request
|
384
|
+
data_request = ServiceRequest.new(
|
385
|
+
request_id: SecureRandom.uuid,
|
386
|
+
request_type: 'query',
|
387
|
+
data: { table: 'orders', limit: 100 }
|
388
|
+
)
|
389
|
+
data_request.from('analytics-service')
|
390
|
+
data_request.to('data-service')
|
391
|
+
data_request.reply_to('analytics-service')
|
392
|
+
puts "\n2. Publishing data request..."
|
393
|
+
data_request.publish
|
394
|
+
sleep(0.2) # Allow time for handlers to process
|
395
|
+
|
396
|
+
# =============================================================================
|
397
|
+
# Summary
|
398
|
+
# =============================================================================
|
399
|
+
|
400
|
+
puts "\nšÆ Entity Addressing & Filtering Summary"
|
401
|
+
puts "=" * 50
|
402
|
+
puts "ā
Point-to-Point: FROM/TO specified for direct routing"
|
403
|
+
puts "ā
Broadcast: FROM only, TO=nil for all broadcast subscribers"
|
404
|
+
puts "ā
Filtered Subscriptions: Subscribe to specific message patterns:"
|
405
|
+
puts " ⢠broadcast: true - Only receive broadcast messages"
|
406
|
+
puts " ⢠to: 'service-name' - Only receive messages directed to you"
|
407
|
+
puts " ⢠from: 'sender' - Only receive from specific senders"
|
408
|
+
puts " ⢠from: ['sender1', 'sender2'] - Receive from multiple senders"
|
409
|
+
puts " ⢠Combined filters work with OR logic for broadcast/to"
|
410
|
+
puts "ā
Request-Reply: REPLY_TO for response routing"
|
411
|
+
puts "ā
Instance Override: Runtime addressing changes"
|
412
|
+
puts "ā
Gateway Pattern: Message transformation and routing"
|
413
|
+
puts "\nš” Filtering enables microservices to:"
|
414
|
+
puts " ⢠Ignore messages not meant for them"
|
415
|
+
puts " ⢠Handle broadcasts separately from directed messages"
|
416
|
+
puts " ⢠Route messages to appropriate handlers based on sender"
|
417
|
+
puts " ⢠Reduce processing overhead by filtering at subscription level"
|
418
|
+
puts "\nFor more details, see docs/addressing.md"
|
data/examples/README.md
CHANGED
@@ -13,6 +13,9 @@ ruby 02_publish_subscribe_events.rb
|
|
13
13
|
ruby 03_many_to_many_chat.rb
|
14
14
|
ruby 04_redis_smart_home_iot.rb
|
15
15
|
ruby 05_proc_handlers.rb
|
16
|
+
ruby 06_custom_logger_example.rb
|
17
|
+
ruby 07_error_handling_scenarios.rb
|
18
|
+
ruby 08_entity_addressing.rb
|
16
19
|
```
|
17
20
|
|
18
21
|
## Examples Overview
|
@@ -198,6 +201,71 @@ NotificationMessage.subscribe("NotificationService.handle")
|
|
198
201
|
- **Maintainability**: Choose the right abstraction level for each need
|
199
202
|
- **Performance**: Understand overhead of different handler approaches
|
200
203
|
|
204
|
+
---
|
205
|
+
|
206
|
+
### 8. Entity Addressing System (Advanced Routing)
|
207
|
+
**File:** `08_entity_addressing.rb`
|
208
|
+
|
209
|
+
**Scenario:** Comprehensive demonstration of SmartMessage's entity addressing system showing point-to-point messaging, broadcast patterns, request-reply workflows, and gateway patterns.
|
210
|
+
|
211
|
+
**Key Features:**
|
212
|
+
- FROM/TO/REPLY_TO addressing fields for sophisticated routing
|
213
|
+
- Point-to-point messaging with specific entity targeting
|
214
|
+
- Broadcast messaging to all subscribers
|
215
|
+
- Request-reply patterns with response routing
|
216
|
+
- Instance-level addressing overrides
|
217
|
+
- Gateway patterns for message transformation and routing
|
218
|
+
|
219
|
+
**Messages Used:**
|
220
|
+
- `OrderMessage` - Point-to-point order processing
|
221
|
+
- `SystemAnnouncementMessage` - Broadcast announcements
|
222
|
+
- `UserLookupRequest` & `UserLookupResponse` - Request-reply pattern
|
223
|
+
- `PaymentMessage` - Instance-level addressing override
|
224
|
+
- `ExternalAPIMessage` - Gateway pattern demonstration
|
225
|
+
|
226
|
+
**Addressing Patterns Shown:**
|
227
|
+
```ruby
|
228
|
+
# Point-to-point messaging
|
229
|
+
class OrderMessage < SmartMessage::Base
|
230
|
+
from 'order-service' # Required: sender identity
|
231
|
+
to 'fulfillment-service' # Optional: specific recipient
|
232
|
+
reply_to 'order-service' # Optional: response routing
|
233
|
+
end
|
234
|
+
|
235
|
+
# Broadcast messaging
|
236
|
+
class AnnouncementMessage < SmartMessage::Base
|
237
|
+
from 'admin-service' # Required sender
|
238
|
+
# No 'to' field = broadcast to all subscribers
|
239
|
+
end
|
240
|
+
|
241
|
+
# Instance-level override
|
242
|
+
payment = PaymentMessage.new(amount: 100.00)
|
243
|
+
payment.to('backup-gateway') # Override destination
|
244
|
+
payment.publish
|
245
|
+
```
|
246
|
+
|
247
|
+
**What You'll Learn:**
|
248
|
+
- Entity-to-entity communication patterns
|
249
|
+
- Point-to-point vs broadcast messaging
|
250
|
+
- Request-reply workflows with proper response routing
|
251
|
+
- Runtime addressing configuration and overrides
|
252
|
+
- Gateway patterns for cross-system integration
|
253
|
+
- How addressing enables sophisticated routing logic
|
254
|
+
|
255
|
+
**Routing Patterns Demonstrated:**
|
256
|
+
- **Point-to-Point**: Direct entity targeting with FROM/TO
|
257
|
+
- **Broadcast**: FROM only, TO=nil for all subscribers
|
258
|
+
- **Request-Reply**: REPLY_TO for response routing
|
259
|
+
- **Gateway**: Dynamic addressing for message transformation
|
260
|
+
- **Override**: Instance-level addressing changes
|
261
|
+
|
262
|
+
**Benefits:**
|
263
|
+
- **Flexible Routing**: Support multiple messaging patterns
|
264
|
+
- **Entity Identification**: Clear sender/recipient tracking
|
265
|
+
- **Response Management**: Structured request-reply workflows
|
266
|
+
- **Runtime Configuration**: Dynamic addressing based on conditions
|
267
|
+
- **Integration Patterns**: Gateway support for external systems
|
268
|
+
|
201
269
|
## Message Patterns Demonstrated
|
202
270
|
|
203
271
|
### Request-Response Pattern
|
@@ -73,6 +73,9 @@ class BotChatAgent < BaseAgent
|
|
73
73
|
return unless @active_rooms.include?(chat_data['room_id'])
|
74
74
|
return if chat_data['sender_id'] == @agent_id
|
75
75
|
|
76
|
+
# Don't respond to other bots to avoid infinite loops
|
77
|
+
return if chat_data['message_type'] == 'bot'
|
78
|
+
|
76
79
|
# Log the message
|
77
80
|
log_display("šļø [#{chat_data['room_id']}] #{chat_data['sender_name']}: #{chat_data['content']}")
|
78
81
|
|
@@ -80,7 +83,7 @@ class BotChatAgent < BaseAgent
|
|
80
83
|
if chat_data['content'].start_with?('/')
|
81
84
|
handle_inline_command(chat_data)
|
82
85
|
else
|
83
|
-
# Respond to certain keywords
|
86
|
+
# Respond to certain keywords from human users only
|
84
87
|
respond_to_keywords(chat_data)
|
85
88
|
end
|
86
89
|
end
|
@@ -102,15 +102,26 @@ SmartMessage::Transport.register(:file, FileTransport)
|
|
102
102
|
|
103
103
|
# Define the Chat Message
|
104
104
|
class ChatMessage < SmartMessage::Base
|
105
|
-
|
106
|
-
|
107
|
-
property :
|
108
|
-
|
109
|
-
property :
|
110
|
-
|
111
|
-
property :
|
112
|
-
|
113
|
-
property :
|
105
|
+
description "Chat messages for tmux-based multi-pane chat demonstration"
|
106
|
+
|
107
|
+
property :message_id,
|
108
|
+
description: "Unique identifier for this chat message"
|
109
|
+
property :room_id,
|
110
|
+
description: "Chat room identifier for message routing"
|
111
|
+
property :sender_id,
|
112
|
+
description: "Unique ID of the user or bot sending the message"
|
113
|
+
property :sender_name,
|
114
|
+
description: "Display name of the message sender"
|
115
|
+
property :content,
|
116
|
+
description: "The actual text content of the chat message"
|
117
|
+
property :message_type,
|
118
|
+
description: "Message type: 'user', 'bot', or 'system'"
|
119
|
+
property :timestamp,
|
120
|
+
description: "ISO8601 timestamp when message was sent"
|
121
|
+
property :mentions,
|
122
|
+
description: "Array of user IDs mentioned in the message"
|
123
|
+
property :metadata,
|
124
|
+
description: "Additional message metadata for tmux display"
|
114
125
|
|
115
126
|
config do
|
116
127
|
transport SmartMessage::Transport.create(:file)
|
@@ -124,13 +135,22 @@ end
|
|
124
135
|
|
125
136
|
# Define Bot Command Message
|
126
137
|
class BotCommandMessage < SmartMessage::Base
|
127
|
-
|
128
|
-
|
129
|
-
property :
|
130
|
-
|
131
|
-
property :
|
132
|
-
|
133
|
-
property :
|
138
|
+
description "Commands sent to chat bots in the tmux chat system"
|
139
|
+
|
140
|
+
property :command_id,
|
141
|
+
description: "Unique identifier for this bot command"
|
142
|
+
property :room_id,
|
143
|
+
description: "Chat room where the command was issued"
|
144
|
+
property :user_id,
|
145
|
+
description: "User who issued the bot command"
|
146
|
+
property :user_name,
|
147
|
+
description: "Display name of the user issuing the command"
|
148
|
+
property :command,
|
149
|
+
description: "Bot command name (help, joke, weather, etc.)"
|
150
|
+
property :parameters,
|
151
|
+
description: "Array of parameters for the bot command"
|
152
|
+
property :timestamp,
|
153
|
+
description: "ISO8601 timestamp when command was issued"
|
134
154
|
|
135
155
|
config do
|
136
156
|
transport SmartMessage::Transport.create(:file)
|
@@ -144,12 +164,20 @@ end
|
|
144
164
|
|
145
165
|
# Define System Notification Message
|
146
166
|
class SystemNotificationMessage < SmartMessage::Base
|
147
|
-
|
148
|
-
|
149
|
-
property :
|
150
|
-
|
151
|
-
property :
|
152
|
-
|
167
|
+
description "System notifications for tmux chat room events and status updates"
|
168
|
+
|
169
|
+
property :notification_id,
|
170
|
+
description: "Unique identifier for this system notification"
|
171
|
+
property :room_id,
|
172
|
+
description: "Chat room affected by this notification"
|
173
|
+
property :notification_type,
|
174
|
+
description: "Type of notification (user_joined, user_left, etc.)"
|
175
|
+
property :content,
|
176
|
+
description: "Human-readable description of the system event"
|
177
|
+
property :timestamp,
|
178
|
+
description: "ISO8601 timestamp when the event occurred"
|
179
|
+
property :metadata,
|
180
|
+
description: "Additional system event metadata for tmux display"
|
153
181
|
|
154
182
|
config do
|
155
183
|
transport SmartMessage::Transport.create(:file)
|