smart_message 0.0.10 → 0.0.12

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/deploy-github-pages.yml +38 -0
  3. data/.gitignore +5 -0
  4. data/CHANGELOG.md +30 -0
  5. data/Gemfile.lock +35 -4
  6. data/README.md +169 -71
  7. data/Rakefile +29 -4
  8. data/docs/assets/images/ddq_architecture.svg +130 -0
  9. data/docs/assets/images/dlq_architecture.svg +115 -0
  10. data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
  11. data/docs/assets/images/enhanced-fluent-api.svg +149 -0
  12. data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
  13. data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
  14. data/docs/assets/images/fluent-api-demo.svg +59 -0
  15. data/docs/assets/images/performance-comparison.svg +161 -0
  16. data/docs/assets/images/redis-basic-architecture.svg +53 -0
  17. data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
  18. data/docs/assets/images/redis-queue-architecture.svg +101 -0
  19. data/docs/assets/images/smart_message.jpg +0 -0
  20. data/docs/assets/images/smart_message_walking.jpg +0 -0
  21. data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
  22. data/docs/assets/images/transport-comparison-matrix.svg +171 -0
  23. data/docs/assets/javascripts/mathjax.js +17 -0
  24. data/docs/assets/stylesheets/extra.css +51 -0
  25. data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
  26. data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
  27. data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
  28. data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
  29. data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
  30. data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
  31. data/docs/{examples.md → getting-started/examples.md} +115 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
  33. data/docs/guides/redis-queue-getting-started.md +697 -0
  34. data/docs/guides/redis-queue-patterns.md +889 -0
  35. data/docs/guides/redis-queue-production.md +1091 -0
  36. data/docs/index.md +64 -0
  37. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  38. data/docs/{logging.md → reference/logging.md} +1 -1
  39. data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
  40. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  41. data/docs/{serializers.md → reference/serializers.md} +3 -5
  42. data/docs/{transports.md → reference/transports.md} +133 -11
  43. data/docs/transports/memory-transport.md +374 -0
  44. data/docs/transports/redis-enhanced-transport.md +524 -0
  45. data/docs/transports/redis-queue-transport.md +1304 -0
  46. data/docs/transports/redis-transport-comparison.md +496 -0
  47. data/docs/transports/redis-transport.md +509 -0
  48. data/examples/README.md +98 -5
  49. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  50. data/examples/city_scenario/README.md +515 -0
  51. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  52. data/examples/city_scenario/citizen.rb +195 -0
  53. data/examples/city_scenario/city_diagram.svg +125 -0
  54. data/examples/city_scenario/common/health_monitor.rb +80 -0
  55. data/examples/city_scenario/common/logger.rb +30 -0
  56. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  57. data/examples/city_scenario/fire_department.rb +446 -0
  58. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  59. data/examples/city_scenario/health_department.rb +100 -0
  60. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  61. data/examples/city_scenario/house.rb +244 -0
  62. data/examples/city_scenario/local_bank.rb +217 -0
  63. data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
  64. data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
  65. data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
  66. data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
  67. data/examples/city_scenario/messages/health_check_message.rb +22 -0
  68. data/examples/city_scenario/messages/health_status_message.rb +35 -0
  69. data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
  70. data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
  71. data/examples/city_scenario/police_department.rb +316 -0
  72. data/examples/city_scenario/redis_monitor.rb +129 -0
  73. data/examples/city_scenario/redis_stats.rb +743 -0
  74. data/examples/city_scenario/room_for_improvement.md +240 -0
  75. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  76. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  77. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  78. data/examples/city_scenario/start_demo.sh +236 -0
  79. data/examples/city_scenario/stop_demo.sh +106 -0
  80. data/examples/city_scenario/visitor.rb +631 -0
  81. data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
  82. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
  83. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
  84. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
  85. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
  86. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  87. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
  88. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
  89. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
  90. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
  91. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
  92. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
  93. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
  94. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
  95. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
  96. data/examples/memory/README.md +163 -0
  97. data/examples/memory/memory_transport_architecture.svg +90 -0
  98. data/examples/memory/point_to_point_pattern.svg +94 -0
  99. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  100. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
  101. data/examples/redis/README.md +230 -0
  102. data/examples/redis/alert_system_flow.svg +127 -0
  103. data/examples/redis/dashboard_status_flow.svg +107 -0
  104. data/examples/redis/device_command_flow.svg +113 -0
  105. data/examples/redis/redis_transport_architecture.svg +115 -0
  106. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  107. data/examples/redis/smart_home_system_architecture.svg +133 -0
  108. data/examples/redis_enhanced/README.md +319 -0
  109. data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
  110. data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
  111. data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
  112. data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
  113. data/examples/redis_queue/01_basic_messaging.rb +221 -0
  114. data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
  115. data/examples/redis_queue/02_pattern_routing.rb +405 -0
  116. data/examples/redis_queue/03_fluent_api.rb +422 -0
  117. data/examples/redis_queue/04_load_balancing.rb +486 -0
  118. data/examples/redis_queue/05_microservices.rb +735 -0
  119. data/examples/redis_queue/06_emergency_alerts.rb +777 -0
  120. data/examples/redis_queue/07_queue_management.rb +587 -0
  121. data/examples/redis_queue/README.md +366 -0
  122. data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
  123. data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
  124. data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
  125. data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
  126. data/examples/redis_queue/redis_queue_architecture.svg +148 -0
  127. data/ideas/README.md +41 -0
  128. data/ideas/agents.md +1001 -0
  129. data/ideas/database_transport.md +980 -0
  130. data/ideas/improvement.md +359 -0
  131. data/ideas/meshage.md +1788 -0
  132. data/ideas/message_discovery.md +178 -0
  133. data/ideas/message_schema.md +1381 -0
  134. data/lib/smart_message/.idea/.gitignore +8 -0
  135. data/lib/smart_message/.idea/markdown.xml +6 -0
  136. data/lib/smart_message/.idea/misc.xml +4 -0
  137. data/lib/smart_message/.idea/modules.xml +8 -0
  138. data/lib/smart_message/.idea/smart_message.iml +16 -0
  139. data/lib/smart_message/.idea/vcs.xml +6 -0
  140. data/lib/smart_message/addressing.rb +15 -0
  141. data/lib/smart_message/base.rb +0 -2
  142. data/lib/smart_message/configuration.rb +1 -1
  143. data/lib/smart_message/logger.rb +15 -4
  144. data/lib/smart_message/plugins.rb +5 -2
  145. data/lib/smart_message/serializer.rb +14 -0
  146. data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
  147. data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
  148. data/lib/smart_message/transport/registry.rb +1 -0
  149. data/lib/smart_message/transport.rb +34 -1
  150. data/lib/smart_message/version.rb +1 -1
  151. data/lib/smart_message.rb +5 -52
  152. data/mkdocs.yml +184 -0
  153. data/p2p_plan.md +326 -0
  154. data/p2p_roadmap.md +287 -0
  155. data/smart_message.gemspec +2 -0
  156. data/smart_message.svg +51 -0
  157. metadata +170 -44
  158. data/docs/README.md +0 -57
  159. data/examples/dead_letters.jsonl +0 -12
  160. data/examples/temp.txt +0 -94
  161. data/examples/tmux_chat/README.md +0 -283
  162. data/examples/tmux_chat/bot_agent.rb +0 -278
  163. data/examples/tmux_chat/human_agent.rb +0 -199
  164. data/examples/tmux_chat/room_monitor.rb +0 -160
  165. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  166. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  167. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  168. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  169. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -16,54 +16,7 @@ SmartMessage is designed around the principle that **messages should be independ
16
16
 
17
17
  ## Architecture Overview
18
18
 
19
- ```mermaid
20
- graph TB
21
- subgraph "SmartMessage Core"
22
- Base[SmartMessage::Base]
23
- Header[Message Header<br/>• UUID<br/>• Timestamps<br/>• Addressing]
24
- Props[Message Properties<br/>• Business Data<br/>• Validation<br/>• Versioning]
25
- end
26
-
27
- subgraph "Plugin System"
28
- Transport[Transport Plugin<br/>• publish()<br/>• subscribe()<br/>• Memory/Redis/STDOUT]
29
- Serializer[Serializer Plugin<br/>• encode()<br/>• decode()<br/>• JSON/Custom]
30
- Logger[Logger Plugin<br/>• Structured logging<br/>• Multiple outputs<br/>• Colorization]
31
- end
32
-
33
- subgraph "Message Processing"
34
- Dispatcher[Dispatcher<br/>• Route messages<br/>• Thread pool<br/>• Subscriptions<br/>• DDQ management]
35
- DDQ[Deduplication Queue<br/>• Handler-scoped<br/>• Memory/Redis storage<br/>• O(1) performance<br/>• Circular buffer]
36
- Handlers[Message Handlers<br/>• Default handler<br/>• Block handlers<br/>• Proc handlers<br/>• Method handlers]
37
- end
38
-
39
- subgraph "Reliability Layer"
40
- CircuitBreaker[Circuit Breaker<br/>• Failure thresholds<br/>• Automatic fallback<br/>• Recovery detection]
41
- DLQ[Dead Letter Queue<br/>• Failed messages<br/>• Replay mechanism<br/>• JSON Lines format]
42
- end
43
-
44
- subgraph "Monitoring"
45
- Stats[Statistics<br/>• Message counts<br/>• Processing metrics<br/>• Thread pool status]
46
- Filters[Message Filtering<br/>• Entity-aware routing<br/>• Regex patterns<br/>• Broadcast handling]
47
- end
48
-
49
- Base --> Header
50
- Base --> Props
51
- Base --> Transport
52
- Base --> Serializer
53
- Base --> Logger
54
-
55
- Transport --> Dispatcher
56
- Dispatcher --> DDQ
57
- Dispatcher --> Handlers
58
- Dispatcher --> Stats
59
- Dispatcher --> Filters
60
-
61
- Transport --> CircuitBreaker
62
- CircuitBreaker --> DLQ
63
-
64
- DDQ -.-> Stats
65
- Handlers -.-> Stats
66
- ```
19
+ ![SmartMessage Architecture Overview](../assets/images/smartmessage_architecture_overview.svg)
67
20
 
68
21
  ## Core Components
69
22
 
@@ -77,7 +30,7 @@ The foundation class that all messages inherit from, built on `Hashie::Dash`.
77
30
  - Message lifecycle management
78
31
  - Header generation and management
79
32
 
80
- **Location:** `lib/smart_message/base.rb:11-278`
33
+ **Location:** `lib/smart_message/base.rb:17-199`
81
34
 
82
35
  ```ruby
83
36
  class MyMessage < SmartMessage::Base
@@ -107,12 +60,13 @@ Handles message delivery and routing between systems.
107
60
  ```ruby
108
61
  # Transport interface
109
62
  class CustomTransport < SmartMessage::Transport::Base
110
- def publish(message_header, message_payload)
63
+ def do_publish(message_class, serialized_message)
111
64
  # Send message via your transport
112
65
  end
113
66
 
114
- def subscribe(message_class, process_method)
115
- # Set up subscription
67
+ def subscribe(message_class, process_method, filter_options = {})
68
+ # Set up subscription via dispatcher
69
+ @dispatcher.add(message_class, process_method, filter_options)
116
70
  end
117
71
  end
118
72
  ```
@@ -130,12 +84,14 @@ Handles encoding and decoding of message content.
130
84
 
131
85
  ```ruby
132
86
  class CustomSerializer < SmartMessage::Serializer::Base
133
- def encode(message_instance)
134
- # Convert to wire format
87
+ def do_encode(message_instance)
88
+ # Convert message instance to wire format
89
+ # Default implementation uses message_instance.to_h
135
90
  end
136
91
 
137
- def decode(payload)
138
- # Convert from wire format
92
+ def do_decode(payload)
93
+ # Convert from wire format back to hash
94
+ # Must return hash compatible with message initialization
139
95
  end
140
96
  end
141
97
  ```
@@ -156,7 +112,7 @@ Routes incoming messages to appropriate handlers using concurrent processing wit
156
112
  ```ruby
157
113
  dispatcher = SmartMessage::Dispatcher.new
158
114
  dispatcher.add("MyMessage", "MyMessage.process")
159
- dispatcher.route(header, payload)
115
+ dispatcher.route(decoded_message)
160
116
 
161
117
  # DDQ integration is automatic when enabled
162
118
  MyMessage.enable_deduplication!
@@ -173,33 +129,18 @@ Handler-scoped message deduplication system preventing duplicate processing.
173
129
  - O(1) performance with hybrid Array + Set data structure
174
130
 
175
131
  **Architecture:**
176
- ```mermaid
177
- graph LR
178
- subgraph "Handler A DDQ"
179
- A1[Circular Array]
180
- A2[Lookup Set]
181
- A3[Mutex Lock]
182
- end
183
-
184
- subgraph "Handler B DDQ"
185
- B1[Circular Array]
186
- B2[Lookup Set]
187
- B3[Mutex Lock]
188
- end
189
-
190
- Message[Incoming Message<br/>UUID: abc-123] --> Dispatcher
191
- Dispatcher --> |Check Handler A| A2
192
- Dispatcher --> |Check Handler B| B2
193
-
194
- A2 --> |Not Found| ProcessA[Process with Handler A]
195
- B2 --> |Found| SkipB[Skip Handler B - Duplicate]
196
-
197
- ProcessA --> |Add UUID| A1
198
- ProcessA --> |Add UUID| A2
199
- ```
132
+
133
+ ![DDQ Architecture](../assets/images/ddq_architecture.svg)
200
134
 
201
135
  **Location:** `lib/smart_message/deduplication.rb`, `lib/smart_message/ddq/`
202
136
 
137
+ **Key Features:**
138
+ - Handler-scoped deduplication (each handler gets its own DDQ)
139
+ - UUID-based duplicate detection
140
+ - Multiple storage backends (Memory, Redis)
141
+ - O(1) performance with hybrid Array + Set data structure
142
+ - Thread-safe operations with mutex locks
143
+
203
144
  ### 6. Message Headers
204
145
 
205
146
  Standard metadata attached to every message with entity addressing support.
@@ -217,9 +158,12 @@ header = message._sm_header
217
158
  puts header.uuid # "550e8400-e29b-41d4-a716-446655440000"
218
159
  puts header.message_class # "MyMessage"
219
160
  puts header.published_at # 2025-08-17 10:30:00 UTC
161
+ puts header.publisher_pid # 12345
220
162
  puts header.from # "payment-service"
221
163
  puts header.to # "order-service"
164
+ puts header.reply_to # "payment-service" (defaults to from)
222
165
  puts header.version # 1
166
+ puts header.serializer # "SmartMessage::Serializer::JSON"
223
167
  ```
224
168
 
225
169
  ## Message Lifecycle
@@ -232,7 +176,7 @@ class OrderMessage < SmartMessage::Base
232
176
 
233
177
  config do
234
178
  transport SmartMessage::Transport.create(:memory)
235
- serializer SmartMessage::Serializer::JSON.new
179
+ serializer SmartMessage::Serializer::Json.new
236
180
  end
237
181
  end
238
182
  ```
@@ -264,13 +208,14 @@ order.publish
264
208
 
265
209
  ### 4. Receiving Phase
266
210
  ```ruby
267
- # Transport receives message
268
- transport.receive(header, payload)
269
- # 1. Routes to dispatcher
270
- # 2. Dispatcher checks DDQ for duplicates per handler
271
- # 3. Applies message filters (from/to/broadcast)
272
- # 4. Spawns thread for processing matching handlers
273
- # 5. Marks UUID as processed in handler's DDQ
211
+ # Transport receives serialized message
212
+ transport.receive(message_class, serialized_message)
213
+ # 1. Decodes message using class's configured serializer
214
+ # 2. Routes decoded message to dispatcher
215
+ # 3. Dispatcher checks DDQ for duplicates per handler
216
+ # 4. Applies message filters (from/to/broadcast)
217
+ # 5. Spawns thread for processing matching handlers
218
+ # 6. Marks UUID as processed in handler's DDQ after successful processing
274
219
  ```
275
220
 
276
221
  ### 5. Message Handler Processing
@@ -278,43 +223,42 @@ transport.receive(header, payload)
278
223
  SmartMessage supports multiple handler types, routed through the dispatcher:
279
224
 
280
225
  ```ruby
281
- # Default handler (self.process method)
282
- def self.process(message_header, message_payload)
283
- data = JSON.parse(message_payload)
284
- order = new(data)
226
+ # Default handler (self.process method) - receives decoded message instance
227
+ def self.process(decoded_message)
228
+ order = decoded_message # Already a fully decoded OrderMessage instance
285
229
  fulfill_order(order)
286
230
  end
287
231
 
288
- # Block handler (inline processing)
289
- OrderMessage.subscribe do |header, payload|
290
- data = JSON.parse(payload)
291
- quick_processing(data)
232
+ # Block handler (inline processing) - receives decoded message instance
233
+ OrderMessage.subscribe do |decoded_message|
234
+ quick_processing(decoded_message)
292
235
  end
293
236
 
294
- # Proc handler (reusable across message types)
295
- audit_proc = proc do |header, payload|
296
- AuditService.log_message(header.message_class, payload)
237
+ # Proc handler (reusable across message types) - receives decoded message instance
238
+ audit_proc = proc do |decoded_message|
239
+ AuditService.log_message(decoded_message.class.name, decoded_message)
297
240
  end
298
241
  OrderMessage.subscribe(audit_proc)
299
242
 
300
- # Method handler (service class processing)
243
+ # Method handler (service class processing) - receives decoded message instance
301
244
  class OrderService
302
- def self.process_order(header, payload)
303
- data = JSON.parse(payload)
304
- complex_business_logic(data)
245
+ def self.process_order(decoded_message)
246
+ complex_business_logic(decoded_message)
305
247
  end
306
248
  end
307
249
  OrderMessage.subscribe("OrderService.process_order")
308
250
  ```
309
251
 
310
252
  **Handler Routing Process:**
311
- 1. Dispatcher receives message and header
253
+ 1. Dispatcher receives decoded message instance
312
254
  2. Looks up all registered handlers for message class
313
- 3. For each handler:
255
+ 3. For each handler that matches filters:
256
+ - Checks DDQ for duplicates (handler-scoped)
314
257
  - **String handlers**: Resolves to class method via constantize
315
258
  - **Proc handlers**: Calls proc directly from registry
316
- 4. Executes handlers in parallel threads
317
- 5. Collects statistics and handles errors
259
+ 4. Executes handlers in parallel threads with circuit breaker protection
260
+ 5. Marks UUID as processed in handler's DDQ after successful completion
261
+ 6. Collects statistics and handles errors
318
262
 
319
263
  ## Plugin System Architecture
320
264
 
@@ -364,10 +308,15 @@ end
364
308
  The dispatcher uses `Concurrent::CachedThreadPool` for processing:
365
309
 
366
310
  ```ruby
367
- # Each message processing happens in its own thread
311
+ # Each message processing happens in its own thread with circuit breaker protection
368
312
  @router_pool.post do
369
- # Message processing happens here
370
- target_class.constantize.process(header, payload)
313
+ circuit_result = circuit(:message_processor).wrap do
314
+ if proc_handler?(message_processor)
315
+ SmartMessage::Base.call_proc_handler(message_processor, decoded_message)
316
+ else
317
+ target_class.constantize.method(class_method).call(decoded_message)
318
+ end
319
+ end
371
320
  end
372
321
  ```
373
322
 
@@ -396,11 +345,13 @@ puts "Completed tasks: #{status[:completed_task_count]}"
396
345
  Processing exceptions are isolated to prevent cascade failures:
397
346
 
398
347
  ```ruby
399
- begin
400
- target_class.constantize.process(header, payload)
401
- rescue Exception => e
402
- # Log error but don't crash the dispatcher
403
- # TODO: Add proper exception logging
348
+ circuit_result = circuit(:message_processor).wrap do
349
+ # Handler execution with circuit breaker protection
350
+ end
351
+
352
+ # Handle circuit breaker fallback responses
353
+ if circuit_result.is_a?(Hash) && circuit_result[:circuit_breaker]
354
+ handle_circuit_breaker_fallback(circuit_result, decoded_message, message_processor)
404
355
  end
405
356
  ```
406
357
 
@@ -415,6 +366,7 @@ module SmartMessage::Errors
415
366
  class NotImplemented < RuntimeError; end
416
367
  class ReceivedMessageNotSubscribed < RuntimeError; end
417
368
  class UnknownMessageClass < RuntimeError; end
369
+ class ValidationError < RuntimeError; end
418
370
  end
419
371
  ```
420
372
 
@@ -425,13 +377,12 @@ end
425
377
  SmartMessage integrates BreakerMachines for production-grade reliability:
426
378
 
427
379
  ```ruby
428
- # Transport operations protected by circuit breakers
380
+ # Circuit breakers are automatically configured for all transports
429
381
  class MyTransport < SmartMessage::Transport::Base
430
- circuit :transport_publish do
431
- threshold failures: 5, within: 30.seconds
432
- reset_after 15.seconds
433
- fallback SmartMessage::CircuitBreaker::Fallbacks.dead_letter_queue
434
- end
382
+ # Inherits circuit breaker configuration:
383
+ # - :transport_publish for publishing operations
384
+ # - :transport_subscribe for subscription operations
385
+ # - Automatic DLQ fallback for failed publishes
435
386
  end
436
387
  ```
437
388
 
@@ -450,25 +401,12 @@ message.publish # If transport fails, goes to DLQ
450
401
 
451
402
  # Manual capture for business logic failures
452
403
  dlq = SmartMessage::DeadLetterQueue.default
453
- dlq.enqueue(header, payload, error: "Validation failed")
404
+ dlq.enqueue(decoded_message, error: "Validation failed", transport: "manual")
454
405
  ```
455
406
 
456
407
  **DLQ Architecture:**
457
408
 
458
- ```mermaid
459
- graph TB
460
- Publish[Message Publishing]
461
- CB[Circuit Breaker<br/>Monitoring]
462
- Transport[Transport<br/>Success]
463
- DLQ[Dead Letter Queue<br/>Failure Storage]
464
- Replay[Replay Mechanism<br/>Manual/Automated]
465
-
466
- Publish --> CB
467
- CB --> |Success| Transport
468
- CB --> |Failure| DLQ
469
- DLQ --> Replay
470
- Replay --> |Retry| Publish
471
- ```
409
+ ![Dead Letter Queue Architecture](../assets/images/dlq_architecture.svg)
472
410
 
473
411
  **DLQ Features:**
474
412
  - JSON Lines format for efficient append operations
@@ -536,7 +474,9 @@ When a message needs a plugin:
536
474
 
537
475
  ```ruby
538
476
  def transport
539
- @transport || @@transport || raise(Errors::TransportNotConfigured)
477
+ @transport || self.class.class_variable_get(:@@transport) || raise(Errors::TransportNotConfigured)
478
+ rescue NameError
479
+ raise(Errors::TransportNotConfigured)
540
480
  end
541
481
  ```
542
482
 
@@ -187,10 +187,10 @@ puts dispatcher.subscribers["MyMessage"]
187
187
  When a transport receives a message, it calls the dispatcher:
188
188
 
189
189
  ```ruby
190
- # Transport receives message and routes it
191
- transport.receive(message_header, message_payload)
192
- # This internally calls:
193
- dispatcher.route(message_header, message_payload)
190
+ # Transport receives serialized message and routes it
191
+ transport.receive(message_class, serialized_message)
192
+ # This internally decodes the message and calls:
193
+ dispatcher.route(decoded_message)
194
194
  ```
195
195
 
196
196
  ### 2. Subscription Lookup
@@ -198,12 +198,12 @@ dispatcher.route(message_header, message_payload)
198
198
  The dispatcher finds all registered handlers:
199
199
 
200
200
  ```ruby
201
- def route(message_header, message_payload)
202
- message_klass = message_header.message_class
201
+ def route(decoded_message)
202
+ message_klass = decoded_message._sm_header.message_class
203
203
  return nil if @subscribers[message_klass].empty?
204
204
 
205
- @subscribers[message_klass].each do |message_processor|
206
- # Process each handler
205
+ @subscribers[message_klass].each do |subscription|
206
+ # Process each handler with filters
207
207
  end
208
208
  end
209
209
  ```
@@ -213,16 +213,17 @@ end
213
213
  Each handler is processed in its own thread, with support for both method and proc handlers:
214
214
 
215
215
  ```ruby
216
- @subscribers[message_klass].each do |message_processor|
216
+ @subscribers[message_klass].each do |subscription|
217
+ message_processor = subscription[:process_method]
217
218
  SS.add(message_klass, message_processor, 'routed')
218
219
 
219
220
  @router_pool.post do
220
- # This runs in a separate thread
221
- begin
221
+ # This runs in a separate thread with circuit breaker protection
222
+ circuit_result = circuit(:message_processor).wrap do
222
223
  # Check if this is a proc handler or a regular method call
223
224
  if proc_handler?(message_processor)
224
225
  # Call the proc handler via SmartMessage::Base
225
- SmartMessage::Base.call_proc_handler(message_processor, message_header, message_payload)
226
+ SmartMessage::Base.call_proc_handler(message_processor, decoded_message)
226
227
  else
227
228
  # Original method call logic
228
229
  parts = message_processor.split('.')
@@ -231,11 +232,13 @@ Each handler is processed in its own thread, with support for both method and pr
231
232
 
232
233
  target_klass.constantize
233
234
  .method(class_method)
234
- .call(message_header, message_payload)
235
+ .call(decoded_message)
235
236
  end
236
- rescue Exception => e
237
- # Error handling - doesn't crash the dispatcher
238
- puts "Error processing message: #{e.message}" if $DEBUG
237
+ end
238
+
239
+ # Handle circuit breaker fallback if triggered
240
+ if circuit_result.is_a?(Hash) && circuit_result[:circuit_breaker]
241
+ handle_circuit_breaker_fallback(circuit_result, decoded_message, message_processor)
239
242
  end
240
243
  end
241
244
  end
@@ -691,7 +694,7 @@ RSpec.describe "Message Processing" do
691
694
  before do
692
695
  TestMessage.config do
693
696
  transport transport
694
- serializer SmartMessage::Serializer::JSON.new
697
+ serializer SmartMessage::Serializer::Json.new
695
698
  end
696
699
 
697
700
  TestMessage.subscribe
@@ -710,7 +713,4 @@ end
710
713
 
711
714
  ## Next Steps
712
715
 
713
- - [Thread Safety](thread-safety.md) - Understanding concurrent processing
714
- - [Statistics & Monitoring](monitoring.md) - Detailed monitoring guide
715
- - [Custom Transports](custom-transports.md) - How transports interact with the dispatcher
716
- - [Troubleshooting](troubleshooting.md) - Common dispatcher issues
716
+ - [Troubleshooting](../development/troubleshooting.md) - Common dispatcher issues
@@ -320,7 +320,7 @@ class FilterTest < Minitest::Test
320
320
  @transport = SmartMessage::Transport.create(:memory, auto_process: true)
321
321
  TestMessage.config do
322
322
  transport @transport
323
- serializer SmartMessage::Serializer::JSON.new
323
+ serializer SmartMessage::Serializer::Json.new
324
324
  end
325
325
  TestMessage.unsubscribe!
326
326
  end
@@ -447,5 +447,4 @@ end
447
447
 
448
448
  - [Dispatcher Documentation](dispatcher.md) - How filtering integrates with message routing
449
449
  - [Entity Addressing](addressing.md) - Understanding `from`, `to`, and `reply_to` fields
450
- - [Examples](examples.md) - Complete working examples with filtering
451
- - [Testing Guide](testing.md) - Best practices for testing filtered subscriptions
450
+ - [Examples](../getting-started/examples.md) - Complete working examples with filtering
@@ -36,9 +36,9 @@ SmartMessage supports multiple ways to handle incoming messages:
36
36
  ### 1. Default Handler Pattern (using `self.process`)
37
37
  ```ruby
38
38
  class SensorDataMessage < SmartMessage::Base
39
- def self.process(message_header, message_payload)
39
+ def self.process(decoded_message)
40
40
  # This gets called when a SensorDataMessage is received
41
- data = JSON.parse(message_payload)
41
+ # decoded_message is already a message instance
42
42
  puts "Sensor reading: #{data['value']}"
43
43
  end
44
44
  end
@@ -51,7 +51,7 @@ SensorDataMessage.subscribe # Uses "SensorDataMessage.process"
51
51
  class ThermostatService
52
52
  def self.handle_sensor_data(message_header, message_payload)
53
53
  # Custom processing logic
54
- data = JSON.parse(message_payload)
54
+ # decoded_message is already a message instance
55
55
  adjust_temperature(data)
56
56
  end
57
57
  end
@@ -99,8 +99,8 @@ Looking at the smart home IoT example:
99
99
 
100
100
  ```ruby
101
101
  class SensorDataMessage < SmartMessage::Base
102
- def self.process(message_header, message_payload)
103
- sensor_data = JSON.parse(message_payload)
102
+ def self.process(decoded_message)
103
+ sensor_# decoded_message is already a message instance
104
104
  icon = case sensor_data['device_type']
105
105
  when 'thermostat' then '🌡️'
106
106
  when 'security_camera' then '📹'
@@ -145,8 +145,8 @@ A single message type can have multiple subscribers with different handlers usin
145
145
  ```ruby
146
146
  # Default handler for logging
147
147
  class SensorDataMessage < SmartMessage::Base
148
- def self.process(message_header, message_payload)
149
- data = JSON.parse(message_payload)
148
+ def self.process(decoded_message)
149
+ # decoded_message is already a message instance
150
150
  puts "📊 Sensor data logged: #{data['device_id']}"
151
151
  end
152
152
  end
@@ -154,7 +154,7 @@ end
154
154
  # Custom method handler for specific services
155
155
  class ThermostatService
156
156
  def self.handle_sensor_data(message_header, message_payload)
157
- data = JSON.parse(message_payload)
157
+ # decoded_message is already a message instance
158
158
  return unless data['device_type'] == 'thermostat'
159
159
  adjust_temperature(data['value'])
160
160
  end
@@ -197,10 +197,10 @@ SensorDataMessage.subscribe(database_logger) # Proc handler
197
197
 
198
198
  ```ruby
199
199
  class SensorDataMessage < SmartMessage::Base
200
- def self.process(message_header, message_payload)
200
+ def self.process(decoded_message)
201
201
  # This runs in its own thread
202
202
  # Be careful with shared state
203
- data = JSON.parse(message_payload)
203
+ # decoded_message is already a message instance
204
204
 
205
205
  # Thread-safe operations
206
206
  update_local_cache(data)
@@ -216,9 +216,9 @@ Handlers should include proper error handling:
216
216
 
217
217
  ```ruby
218
218
  class SensorDataMessage < SmartMessage::Base
219
- def self.process(message_header, message_payload)
219
+ def self.process(decoded_message)
220
220
  begin
221
- data = JSON.parse(message_payload)
221
+ # decoded_message is already a message instance
222
222
 
223
223
  # Validate required fields
224
224
  raise "Missing device_id" unless data['device_id']
@@ -310,9 +310,9 @@ PaymentEventMessage.subscribe(audit_logger)
310
310
 
311
311
  ### 1. Keep Handlers Fast
312
312
  ```ruby
313
- def self.process(message_header, message_payload)
313
+ def self.process(decoded_message)
314
314
  # Quick validation
315
- data = JSON.parse(message_payload)
315
+ # decoded_message is already a message instance
316
316
  return unless valid_message?(data)
317
317
 
318
318
  # Delegate heavy work to background jobs
@@ -348,7 +348,7 @@ SensorDataMessage.subscribe do |h, p|; process_stuff(p); end
348
348
  ### 3. Filter Messages Early
349
349
  ```ruby
350
350
  def self.handle_thermostat_data(message_header, message_payload)
351
- data = JSON.parse(message_payload)
351
+ # decoded_message is already a message instance
352
352
 
353
353
  # Filter early to avoid unnecessary processing
354
354
  return unless data['device_type'] == 'thermostat'
@@ -361,11 +361,11 @@ end
361
361
 
362
362
  ### 4. Include Logging and Monitoring
363
363
  ```ruby
364
- def self.process(message_header, message_payload)
364
+ def self.process(decoded_message)
365
365
  start_time = Time.now
366
366
 
367
367
  begin
368
- data = JSON.parse(message_payload)
368
+ # decoded_message is already a message instance
369
369
  logger.info "Processing sensor data from #{data['device_id']}"
370
370
 
371
371
  # Business logic here
@@ -39,7 +39,7 @@ class MyMessage < SmartMessage::Base
39
39
 
40
40
  config do
41
41
  transport SmartMessage::Transport.create(:stdout) # No loopback
42
- serializer SmartMessage::Serializer::JSON.new
42
+ serializer SmartMessage::Serializer::Json.new
43
43
  end
44
44
  end
45
45
 
@@ -47,7 +47,7 @@ end
47
47
  class MyMessage < SmartMessage::Base
48
48
  config do
49
49
  transport SmartMessage::Transport.create(:stdout, loopback: true)
50
- serializer SmartMessage::Serializer::JSON.new
50
+ serializer SmartMessage::Serializer::Json.new
51
51
  end
52
52
  end
53
53
  ```
@@ -102,7 +102,7 @@ MyMessage.new(data: "test").publish
102
102
  class MyMessage < SmartMessage::Base
103
103
  config do
104
104
  transport SmartMessage::Transport.create(:memory)
105
- serializer SmartMessage::Serializer::JSON.new
105
+ serializer SmartMessage::Serializer::Json.new
106
106
  end
107
107
  end
108
108
  ```
@@ -114,7 +114,7 @@ class ProblematicMessage < SmartMessage::Base
114
114
  property :data
115
115
 
116
116
  config do
117
- serializer SmartMessage::Serializer::JSON.new
117
+ serializer SmartMessage::Serializer::Json.new
118
118
  end
119
119
  end
120
120
 
@@ -154,7 +154,7 @@ class MyMessage < SmartMessage::Base
154
154
  property :data
155
155
 
156
156
  config do
157
- serializer SmartMessage::Serializer::JSON.new
157
+ serializer SmartMessage::Serializer::Json.new
158
158
  # Missing transport!
159
159
  end
160
160
  end
@@ -165,7 +165,7 @@ MyMessage.new(data: "test").publish # Throws TransportNotConfigured
165
165
  class MyMessage < SmartMessage::Base
166
166
  config do
167
167
  transport SmartMessage::Transport.create(:memory)
168
- serializer SmartMessage::Serializer::JSON.new
168
+ serializer SmartMessage::Serializer::Json.new
169
169
  end
170
170
  end
171
171
  ```
@@ -471,7 +471,7 @@ class MyMessage < SmartMessage::Base
471
471
  # Reconfigure
472
472
  config do
473
473
  transport SmartMessage::Transport.create(:memory)
474
- serializer SmartMessage::Serializer::JSON.new
474
+ serializer SmartMessage::Serializer::Json.new
475
475
  end
476
476
  end
477
477
  ```