smart_message 0.0.8 → 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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.irbrc +24 -0
  4. data/CHANGELOG.md +96 -0
  5. data/Gemfile.lock +6 -1
  6. data/README.md +289 -15
  7. data/docs/README.md +3 -1
  8. data/docs/addressing.md +119 -13
  9. data/docs/architecture.md +68 -0
  10. data/docs/dead_letter_queue.md +673 -0
  11. data/docs/dispatcher.md +87 -0
  12. data/docs/examples.md +59 -1
  13. data/docs/getting-started.md +8 -1
  14. data/docs/logging.md +382 -326
  15. data/docs/message_filtering.md +451 -0
  16. data/examples/01_point_to_point_orders.rb +54 -53
  17. data/examples/02_publish_subscribe_events.rb +14 -10
  18. data/examples/03_many_to_many_chat.rb +16 -8
  19. data/examples/04_redis_smart_home_iot.rb +20 -10
  20. data/examples/05_proc_handlers.rb +12 -11
  21. data/examples/06_custom_logger_example.rb +95 -100
  22. data/examples/07_error_handling_scenarios.rb +4 -2
  23. data/examples/08_entity_addressing_basic.rb +18 -6
  24. data/examples/08_entity_addressing_with_filtering.rb +27 -9
  25. data/examples/09_dead_letter_queue_demo.rb +559 -0
  26. data/examples/09_regex_filtering_microservices.rb +407 -0
  27. data/examples/10_header_block_configuration.rb +263 -0
  28. data/examples/11_global_configuration_example.rb +219 -0
  29. data/examples/README.md +102 -0
  30. data/examples/dead_letters.jsonl +12 -0
  31. data/examples/performance_metrics/benchmark_results_ractor_20250818_205603.json +135 -0
  32. data/examples/performance_metrics/benchmark_results_ractor_20250818_205831.json +135 -0
  33. data/examples/performance_metrics/benchmark_results_test_20250818_204942.json +130 -0
  34. data/examples/performance_metrics/benchmark_results_threadpool_20250818_204942.json +130 -0
  35. data/examples/performance_metrics/benchmark_results_threadpool_20250818_204959.json +130 -0
  36. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205044.json +130 -0
  37. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205109.json +130 -0
  38. data/examples/performance_metrics/benchmark_results_threadpool_20250818_205252.json +130 -0
  39. data/examples/performance_metrics/benchmark_results_unknown_20250819_172852.json +130 -0
  40. data/examples/performance_metrics/compare_benchmarks.rb +519 -0
  41. data/examples/performance_metrics/dead_letters.jsonl +3100 -0
  42. data/examples/performance_metrics/performance_benchmark.rb +344 -0
  43. data/examples/show_logger.rb +367 -0
  44. data/examples/show_me.rb +145 -0
  45. data/examples/temp.txt +94 -0
  46. data/examples/tmux_chat/bot_agent.rb +4 -2
  47. data/examples/tmux_chat/human_agent.rb +4 -2
  48. data/examples/tmux_chat/room_monitor.rb +4 -2
  49. data/examples/tmux_chat/shared_chat_system.rb +6 -3
  50. data/lib/smart_message/addressing.rb +259 -0
  51. data/lib/smart_message/base.rb +121 -599
  52. data/lib/smart_message/circuit_breaker.rb +2 -1
  53. data/lib/smart_message/configuration.rb +199 -0
  54. data/lib/smart_message/dead_letter_queue.rb +27 -10
  55. data/lib/smart_message/dispatcher.rb +90 -49
  56. data/lib/smart_message/header.rb +5 -0
  57. data/lib/smart_message/logger/base.rb +21 -1
  58. data/lib/smart_message/logger/default.rb +88 -138
  59. data/lib/smart_message/logger/lumberjack.rb +324 -0
  60. data/lib/smart_message/logger/null.rb +81 -0
  61. data/lib/smart_message/logger.rb +17 -9
  62. data/lib/smart_message/messaging.rb +100 -0
  63. data/lib/smart_message/plugins.rb +132 -0
  64. data/lib/smart_message/serializer/base.rb +25 -8
  65. data/lib/smart_message/serializer/json.rb +5 -4
  66. data/lib/smart_message/subscription.rb +193 -0
  67. data/lib/smart_message/transport/base.rb +72 -41
  68. data/lib/smart_message/transport/memory_transport.rb +7 -5
  69. data/lib/smart_message/transport/redis_transport.rb +15 -45
  70. data/lib/smart_message/transport/stdout_transport.rb +18 -8
  71. data/lib/smart_message/transport.rb +1 -34
  72. data/lib/smart_message/utilities.rb +142 -0
  73. data/lib/smart_message/version.rb +1 -1
  74. data/lib/smart_message/versioning.rb +85 -0
  75. data/lib/smart_message/wrapper.rb.bak +132 -0
  76. data/lib/smart_message.rb +74 -28
  77. data/smart_message.gemspec +3 -0
  78. metadata +76 -3
  79. data/lib/smart_message/serializer.rb +0 -10
  80. data/lib/smart_message/wrapper.rb +0 -43
@@ -36,7 +36,8 @@ class UserEventMessage < SmartMessage::Base
36
36
  end
37
37
 
38
38
  # Default processor - just logs the event
39
- def self.process(message_header, message_payload)
39
+ def self.process(wrapper)
40
+ message_header, message_payload = wrapper.split
40
41
  event_data = JSON.parse(message_payload)
41
42
  puts "📡 Event broadcasted: #{event_data['event_type']} for user #{event_data['user_id']}"
42
43
  end
@@ -50,12 +51,13 @@ class EmailService
50
51
  UserEventMessage.subscribe('EmailService.handle_user_event')
51
52
  end
52
53
 
53
- def self.handle_user_event(message_header, message_payload)
54
+ def self.handle_user_event(wrapper)
54
55
  service = new
55
- service.process_event(message_header, message_payload)
56
+ service.process_event(wrapper)
56
57
  end
57
58
 
58
- def process_event(message_header, message_payload)
59
+ def process_event(wrapper)
60
+ message_header, message_payload = wrapper.split
59
61
  event_data = JSON.parse(message_payload)
60
62
 
61
63
  case event_data['event_type']
@@ -105,12 +107,13 @@ class SMSService
105
107
  UserEventMessage.subscribe('SMSService.handle_user_event')
106
108
  end
107
109
 
108
- def self.handle_user_event(message_header, message_payload)
110
+ def self.handle_user_event(wrapper)
109
111
  service = new
110
- service.process_event(message_header, message_payload)
112
+ service.process_event(wrapper)
111
113
  end
112
114
 
113
- def process_event(message_header, message_payload)
115
+ def process_event(wrapper)
116
+ message_header, message_payload = wrapper.split
114
117
  event_data = JSON.parse(message_payload)
115
118
 
116
119
  case event_data['event_type']
@@ -170,17 +173,18 @@ class AuditService
170
173
  UserEventMessage.subscribe('AuditService.handle_user_event')
171
174
  end
172
175
 
173
- def self.handle_user_event(message_header, message_payload)
176
+ def self.handle_user_event(wrapper)
174
177
  # Use a singleton pattern for persistent audit log
175
178
  @@instance ||= new
176
- @@instance.process_event(message_header, message_payload)
179
+ @@instance.process_event(wrapper)
177
180
  end
178
181
 
179
182
  def self.get_summary
180
183
  @@instance&.get_audit_summary || {}
181
184
  end
182
185
 
183
- def process_event(message_header, message_payload)
186
+ def process_event(wrapper)
187
+ message_header, message_payload = wrapper.split
184
188
  event_data = JSON.parse(message_payload)
185
189
 
186
190
  audit_entry = {
@@ -40,7 +40,8 @@ class ChatMessage < SmartMessage::Base
40
40
  serializer SmartMessage::Serializer::JSON.new
41
41
  end
42
42
 
43
- def self.process(message_header, message_payload)
43
+ def self.process(wrapper)
44
+ message_header, message_payload = wrapper.split
44
45
  chat_data = JSON.parse(message_payload)
45
46
  puts "đŸ’Ŧ Chat message in #{chat_data['room_id']}: #{chat_data['content']}"
46
47
  end
@@ -68,7 +69,8 @@ class BotCommandMessage < SmartMessage::Base
68
69
  serializer SmartMessage::Serializer::JSON.new
69
70
  end
70
71
 
71
- def self.process(message_header, message_payload)
72
+ def self.process(wrapper)
73
+ message_header, message_payload = wrapper.split
72
74
  command_data = JSON.parse(message_payload)
73
75
  puts "🤖 Bot command: #{command_data['command']} in #{command_data['room_id']}"
74
76
  end
@@ -96,7 +98,8 @@ class SystemNotificationMessage < SmartMessage::Base
96
98
  serializer SmartMessage::Serializer::JSON.new
97
99
  end
98
100
 
99
- def self.process(message_header, message_payload)
101
+ def self.process(wrapper)
102
+ message_header, message_payload = wrapper.split
100
103
  notif_data = JSON.parse(message_payload)
101
104
  puts "🔔 System: #{notif_data['content']} in #{notif_data['room_id']}"
102
105
  end
@@ -192,7 +195,8 @@ class HumanChatAgent
192
195
  @@agents[agent.user_id] = agent
193
196
  end
194
197
 
195
- def handle_chat_message(message_header, message_payload)
198
+ def handle_chat_message(wrapper)
199
+ message_header, message_payload = wrapper.split
196
200
  chat_data = JSON.parse(message_payload)
197
201
 
198
202
  # Only process messages from rooms we're in and not our own messages
@@ -207,7 +211,8 @@ class HumanChatAgent
207
211
  end
208
212
  end
209
213
 
210
- def handle_system_notification(message_header, message_payload)
214
+ def handle_system_notification(wrapper)
215
+ message_header, message_payload = wrapper.split
211
216
  notif_data = JSON.parse(message_payload)
212
217
 
213
218
  # Only process notifications from rooms we're in
@@ -302,7 +307,8 @@ class BotAgent
302
307
  )
303
308
  end
304
309
 
305
- def self.handle_bot_command(message_header, message_payload)
310
+ def self.handle_bot_command(wrapper)
311
+ message_header, message_payload = wrapper.split
306
312
  command_data = JSON.parse(message_payload)
307
313
  @@bots ||= []
308
314
 
@@ -315,7 +321,8 @@ class BotAgent
315
321
  capable_bot&.process_command(command_data)
316
322
  end
317
323
 
318
- def self.handle_chat_message(message_header, message_payload)
324
+ def self.handle_chat_message(wrapper)
325
+ message_header, message_payload = wrapper.split
319
326
  chat_data = JSON.parse(message_payload)
320
327
  @@bots ||= []
321
328
 
@@ -491,7 +498,8 @@ class RoomManager
491
498
  notification.publish
492
499
  end
493
500
 
494
- def self.handle_system_notification(message_header, message_payload)
501
+ def self.handle_system_notification(wrapper)
502
+ message_header, message_payload = wrapper.split
495
503
  @@instance ||= new
496
504
  @@instance.process_system_notification(message_header, message_payload)
497
505
  end
@@ -82,7 +82,8 @@ class SensorDataMessage < SmartMessage::Base
82
82
  serializer SmartMessage::Serializer::JSON.new
83
83
  end
84
84
 
85
- def self.process(message_header, message_payload)
85
+ def self.process(wrapper)
86
+ message_header, message_payload = wrapper.split
86
87
  sensor_data = JSON.parse(message_payload)
87
88
  icon = case sensor_data['device_type']
88
89
  when 'thermostat' then 'đŸŒĄī¸'
@@ -116,7 +117,8 @@ class DeviceCommandMessage < SmartMessage::Base
116
117
  serializer SmartMessage::Serializer::JSON.new
117
118
  end
118
119
 
119
- def self.process(message_header, message_payload)
120
+ def self.process(wrapper)
121
+ message_header, message_payload = wrapper.split
120
122
  command_data = JSON.parse(message_payload)
121
123
  puts "đŸŽ›ī¸ Command sent: #{command_data['command']} to #{command_data['device_id']} (requested by #{command_data['requested_by']})"
122
124
  end
@@ -148,7 +150,8 @@ class AlertMessage < SmartMessage::Base
148
150
  serializer SmartMessage::Serializer::JSON.new
149
151
  end
150
152
 
151
- def self.process(message_header, message_payload)
153
+ def self.process(wrapper)
154
+ message_header, message_payload = wrapper.split
152
155
  alert_data = JSON.parse(message_payload)
153
156
  severity_icon = case alert_data['severity']
154
157
  when 'low' then '💙'
@@ -176,7 +179,8 @@ class DashboardStatusMessage < SmartMessage::Base
176
179
  serializer SmartMessage::Serializer::JSON.new
177
180
  end
178
181
 
179
- def self.process(message_header, message_payload)
182
+ def self.process(wrapper)
183
+ message_header, message_payload = wrapper.split
180
184
  status_data = JSON.parse(message_payload)
181
185
  status_icon = case status_data['system_status']
182
186
  when 'normal' then '✅'
@@ -205,7 +209,8 @@ class SmartThermostat
205
209
  DeviceCommandMessage.subscribe("SmartThermostat.handle_command")
206
210
  end
207
211
 
208
- def self.handle_command(message_header, message_payload)
212
+ def self.handle_command(wrapper)
213
+ message_header, message_payload = wrapper.split
209
214
  command_data = JSON.parse(message_payload)
210
215
 
211
216
  # Only process commands intended for thermostats with our device ID
@@ -288,7 +293,8 @@ class SecurityCamera
288
293
  DeviceCommandMessage.subscribe("SecurityCamera.handle_command")
289
294
  end
290
295
 
291
- def self.handle_command(message_header, message_payload)
296
+ def self.handle_command(wrapper)
297
+ message_header, message_payload = wrapper.split
292
298
  command_data = JSON.parse(message_payload)
293
299
 
294
300
  # Only process commands intended for cameras with our device ID
@@ -398,7 +404,8 @@ class SmartDoorLock
398
404
  DeviceCommandMessage.subscribe("SmartDoorLock.handle_command")
399
405
  end
400
406
 
401
- def self.handle_command(message_header, message_payload)
407
+ def self.handle_command(wrapper)
408
+ message_header, message_payload = wrapper.split
402
409
  command_data = JSON.parse(message_payload)
403
410
 
404
411
  # Only process commands intended for door locks with our device ID
@@ -479,17 +486,20 @@ class IoTDashboard
479
486
  DeviceCommandMessage.subscribe("IoTDashboard.log_command")
480
487
  end
481
488
 
482
- def self.handle_sensor_data(message_header, message_payload)
489
+ def self.handle_sensor_data(wrapper)
490
+ message_header, message_payload = wrapper.split
483
491
  @@instance ||= new
484
492
  @@instance.process_sensor_data(message_header, message_payload)
485
493
  end
486
494
 
487
- def self.handle_alert(message_header, message_payload)
495
+ def self.handle_alert(wrapper)
496
+ message_header, message_payload = wrapper.split
488
497
  @@instance ||= new
489
498
  @@instance.process_alert(message_header, message_payload)
490
499
  end
491
500
 
492
- def self.log_command(message_header, message_payload)
501
+ def self.log_command(wrapper)
502
+ message_header, message_payload = wrapper.split
493
503
  @@instance ||= new
494
504
  @@instance.log_device_command(message_header, message_payload)
495
505
  end
@@ -33,7 +33,8 @@ class NotificationMessage < SmartMessage::Base
33
33
  end
34
34
 
35
35
  # Default handler
36
- def self.process(message_header, message_payload)
36
+ def self.process(wrapper)
37
+ message_header, message_payload = wrapper.split
37
38
  data = JSON.parse(message_payload)
38
39
  icon = case data['type']
39
40
  when 'info' then 'â„šī¸'
@@ -56,8 +57,8 @@ puts
56
57
 
57
58
  # 2. Block handler
58
59
  puts "2ī¸âƒŖ Block handler subscription:"
59
- block_id = NotificationMessage.subscribe do |header, payload|
60
- data = JSON.parse(payload)
60
+ block_id = NotificationMessage.subscribe do |wrapper|
61
+ data = JSON.parse(wrapper._sm_payload)
61
62
  if data['type'] == 'error'
62
63
  puts "đŸ”Ĩ [BLOCK] Critical error logged: #{data['title']}"
63
64
  # Could send to error tracking service here
@@ -68,9 +69,9 @@ puts
68
69
 
69
70
  # 3. Proc handler
70
71
  puts "3ī¸âƒŖ Proc handler subscription:"
71
- audit_logger = proc do |header, payload|
72
- data = JSON.parse(payload)
73
- timestamp = header.published_at.strftime('%Y-%m-%d %H:%M:%S')
72
+ audit_logger = proc do |wrapper|
73
+ data = JSON.parse(wrapper._sm_payload)
74
+ timestamp = wrapper._sm_header.published_at.strftime('%Y-%m-%d %H:%M:%S')
74
75
  puts "📝 [AUDIT] #{timestamp} - User #{data['user_id']}: #{data['type'].upcase}"
75
76
  end
76
77
 
@@ -80,8 +81,8 @@ puts
80
81
 
81
82
  # 4. Lambda handler
82
83
  puts "4ī¸âƒŖ Lambda handler subscription:"
83
- warning_filter = lambda do |header, payload|
84
- data = JSON.parse(payload)
84
+ warning_filter = lambda do |wrapper|
85
+ data = JSON.parse(wrapper._sm_payload)
85
86
  if data['type'] == 'warning'
86
87
  puts "⚡ [LAMBDA] Warning for user #{data['user_id']}: #{data['message']}"
87
88
  end
@@ -94,8 +95,8 @@ puts
94
95
  # 5. Method handler (traditional, but shown for comparison)
95
96
  puts "5ī¸âƒŖ Method handler subscription:"
96
97
  class NotificationService
97
- def self.handle_notifications(header, payload)
98
- data = JSON.parse(payload)
98
+ def self.handle_notifications(wrapper)
99
+ data = JSON.parse(wrapper._sm_payload)
99
100
  puts "đŸĸ [SERVICE] Processing #{data['type']} notification for user #{data['user_id']}"
100
101
  end
101
102
  end
@@ -174,7 +175,7 @@ puts "=" * 60
174
175
 
175
176
  puts "\nThis example demonstrated:"
176
177
  puts "â€ĸ ✅ Default self.process method (traditional)"
177
- puts "â€ĸ ✅ Block handlers with subscribe { |h,p| ... } (NEW!)"
178
+ puts "â€ĸ ✅ Block handlers with subscribe { |wrapper| ... } (NEW!)"
178
179
  puts "â€ĸ ✅ Proc handlers with subscribe(proc { ... }) (NEW!)"
179
180
  puts "â€ĸ ✅ Lambda handlers with subscribe(lambda { ... }) (NEW!)"
180
181
  puts "â€ĸ ✅ Method handlers with subscribe('Class.method') (traditional)"