smart_message 0.0.10 → 0.0.13

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 (149) 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 +64 -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} +80 -145
  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} +103 -89
  32. data/docs/{getting-started.md → getting-started/quick-start.md} +47 -23
  33. data/docs/index.md +64 -0
  34. data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
  35. data/docs/{logging.md → reference/logging.md} +1 -1
  36. data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
  37. data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
  38. data/docs/reference/serializers.md +245 -0
  39. data/docs/{transports.md → reference/transports.md} +9 -11
  40. data/docs/transports/memory-transport.md +374 -0
  41. data/docs/transports/redis-transport-comparison.md +361 -0
  42. data/docs/transports/redis-transport.md +490 -0
  43. data/examples/README.md +104 -14
  44. data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
  45. data/examples/city_scenario/README.md +515 -0
  46. data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
  47. data/examples/city_scenario/citizen.rb +195 -0
  48. data/examples/city_scenario/city_diagram.svg +125 -0
  49. data/examples/city_scenario/common/health_monitor.rb +80 -0
  50. data/examples/city_scenario/common/logger.rb +30 -0
  51. data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
  52. data/examples/city_scenario/fire_department.rb +446 -0
  53. data/examples/city_scenario/fire_emergency_flow.svg +95 -0
  54. data/examples/city_scenario/health_department.rb +100 -0
  55. data/examples/city_scenario/health_monitoring_system.svg +130 -0
  56. data/examples/city_scenario/house.rb +244 -0
  57. data/examples/city_scenario/local_bank.rb +217 -0
  58. data/examples/city_scenario/messages/emergency_911_message.rb +80 -0
  59. data/examples/city_scenario/messages/emergency_resolved_message.rb +42 -0
  60. data/examples/city_scenario/messages/fire_dispatch_message.rb +42 -0
  61. data/examples/city_scenario/messages/fire_emergency_message.rb +44 -0
  62. data/examples/city_scenario/messages/health_check_message.rb +21 -0
  63. data/examples/city_scenario/messages/health_status_message.rb +34 -0
  64. data/examples/city_scenario/messages/police_dispatch_message.rb +45 -0
  65. data/examples/city_scenario/messages/silent_alarm_message.rb +37 -0
  66. data/examples/city_scenario/police_department.rb +316 -0
  67. data/examples/city_scenario/redis_monitor.rb +129 -0
  68. data/examples/city_scenario/redis_stats.rb +743 -0
  69. data/examples/city_scenario/room_for_improvement.md +240 -0
  70. data/examples/city_scenario/security_emergency_flow.svg +95 -0
  71. data/examples/city_scenario/service_internal_architecture.svg +154 -0
  72. data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
  73. data/examples/city_scenario/start_demo.sh +236 -0
  74. data/examples/city_scenario/stop_demo.sh +106 -0
  75. data/examples/city_scenario/visitor.rb +631 -0
  76. data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -3
  77. data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +10 -40
  78. data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -3
  79. data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +1 -2
  80. data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +1 -4
  81. data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
  82. data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +1 -2
  83. data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +13 -14
  84. data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +1 -4
  85. data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +2 -8
  86. data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +2 -6
  87. data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +1 -2
  88. data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +1 -6
  89. data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +17 -8
  90. data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -2
  91. data/examples/memory/README.md +163 -0
  92. data/examples/memory/memory_transport_architecture.svg +90 -0
  93. data/examples/memory/point_to_point_pattern.svg +94 -0
  94. data/examples/memory/publish_subscribe_pattern.svg +125 -0
  95. data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +1 -5
  96. data/examples/redis/README.md +228 -0
  97. data/examples/redis/alert_system_flow.svg +127 -0
  98. data/examples/redis/dashboard_status_flow.svg +107 -0
  99. data/examples/redis/device_command_flow.svg +113 -0
  100. data/examples/redis/redis_transport_architecture.svg +115 -0
  101. data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
  102. data/examples/redis/smart_home_system_architecture.svg +133 -0
  103. data/ideas/README.md +41 -0
  104. data/ideas/agents.md +1001 -0
  105. data/ideas/database_transport.md +980 -0
  106. data/ideas/improvement.md +359 -0
  107. data/ideas/meshage.md +1788 -0
  108. data/ideas/message_discovery.md +178 -0
  109. data/ideas/message_schema.md +1381 -0
  110. data/lib/smart_message/.idea/.gitignore +8 -0
  111. data/lib/smart_message/.idea/markdown.xml +6 -0
  112. data/lib/smart_message/.idea/misc.xml +4 -0
  113. data/lib/smart_message/.idea/modules.xml +8 -0
  114. data/lib/smart_message/.idea/smart_message.iml +16 -0
  115. data/lib/smart_message/.idea/vcs.xml +6 -0
  116. data/lib/smart_message/addressing.rb +15 -0
  117. data/lib/smart_message/base.rb +19 -12
  118. data/lib/smart_message/configuration.rb +2 -23
  119. data/lib/smart_message/dead_letter_queue.rb +1 -1
  120. data/lib/smart_message/logger.rb +15 -4
  121. data/lib/smart_message/messaging.rb +3 -62
  122. data/lib/smart_message/plugins.rb +6 -44
  123. data/lib/smart_message/serializer.rb +14 -0
  124. data/lib/smart_message/transport/base.rb +42 -8
  125. data/lib/smart_message/transport/memory_transport.rb +23 -4
  126. data/lib/smart_message/transport/redis_transport.rb +11 -0
  127. data/lib/smart_message/transport/stdout_transport.rb +28 -10
  128. data/lib/smart_message/transport.rb +33 -1
  129. data/lib/smart_message/version.rb +1 -1
  130. data/lib/smart_message.rb +5 -52
  131. data/mkdocs.yml +184 -0
  132. data/p2p_plan.md +326 -0
  133. data/p2p_roadmap.md +287 -0
  134. data/smart_message.gemspec +2 -0
  135. data/smart_message.svg +51 -0
  136. metadata +145 -45
  137. data/docs/README.md +0 -57
  138. data/docs/serializers.md +0 -575
  139. data/examples/dead_letters.jsonl +0 -12
  140. data/examples/temp.txt +0 -94
  141. data/examples/tmux_chat/README.md +0 -283
  142. data/examples/tmux_chat/bot_agent.rb +0 -278
  143. data/examples/tmux_chat/human_agent.rb +0 -199
  144. data/examples/tmux_chat/room_monitor.rb +0 -160
  145. data/examples/tmux_chat/shared_chat_system.rb +0 -328
  146. data/examples/tmux_chat/start_chat_demo.sh +0 -190
  147. data/examples/tmux_chat/stop_chat_demo.sh +0 -22
  148. /data/docs/{properties.md → core-concepts/properties.md} +0 -0
  149. /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -9,61 +9,14 @@ SmartMessage is designed around the principle that **messages should be independ
9
9
  ### Core Principles
10
10
 
11
11
  1. **Separation of Concerns**: Message content, transport, and serialization are independent
12
- 2. **Plugin Architecture**: Pluggable transports and serializers
12
+ 2. **Plugin Architecture**: Pluggable transports
13
13
  3. **Dual Configuration**: Both class-level and instance-level configuration
14
14
  4. **Thread Safety**: Concurrent message processing with thread pools
15
- 5. **Gateway Support**: Messages can flow between different transports/serializers
15
+ 5. **Gateway Support**: Messages can flow between different transports
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
 
@@ -73,11 +26,11 @@ The foundation class that all messages inherit from, built on `Hashie::Dash`.
73
26
 
74
27
  **Key Responsibilities:**
75
28
  - Property management and validation
76
- - Plugin configuration (transport, serializer, logger)
29
+ - Plugin configuration (transport, logger)
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
@@ -87,7 +40,6 @@ class MyMessage < SmartMessage::Base
87
40
 
88
41
  config do
89
42
  transport MyTransport.new
90
- serializer MySerializer.new
91
43
  end
92
44
  end
93
45
  ```
@@ -107,12 +59,13 @@ Handles message delivery and routing between systems.
107
59
  ```ruby
108
60
  # Transport interface
109
61
  class CustomTransport < SmartMessage::Transport::Base
110
- def publish(message_header, message_payload)
62
+ def do_publish(message_class, serialized_message)
111
63
  # Send message via your transport
112
64
  end
113
65
 
114
- def subscribe(message_class, process_method)
115
- # Set up subscription
66
+ def subscribe(message_class, process_method, filter_options = {})
67
+ # Set up subscription via dispatcher
68
+ @dispatcher.add(message_class, process_method, filter_options)
116
69
  end
117
70
  end
118
71
  ```
@@ -130,12 +83,14 @@ Handles encoding and decoding of message content.
130
83
 
131
84
  ```ruby
132
85
  class CustomSerializer < SmartMessage::Serializer::Base
133
- def encode(message_instance)
134
- # Convert to wire format
86
+ def do_encode(message_instance)
87
+ # Convert message instance to wire format
88
+ # Default implementation uses message_instance.to_h
135
89
  end
136
90
 
137
- def decode(payload)
138
- # Convert from wire format
91
+ def do_decode(payload)
92
+ # Convert from wire format back to hash
93
+ # Must return hash compatible with message initialization
139
94
  end
140
95
  end
141
96
  ```
@@ -156,7 +111,7 @@ Routes incoming messages to appropriate handlers using concurrent processing wit
156
111
  ```ruby
157
112
  dispatcher = SmartMessage::Dispatcher.new
158
113
  dispatcher.add("MyMessage", "MyMessage.process")
159
- dispatcher.route(header, payload)
114
+ dispatcher.route(decoded_message)
160
115
 
161
116
  # DDQ integration is automatic when enabled
162
117
  MyMessage.enable_deduplication!
@@ -173,33 +128,18 @@ Handler-scoped message deduplication system preventing duplicate processing.
173
128
  - O(1) performance with hybrid Array + Set data structure
174
129
 
175
130
  **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
- ```
131
+
132
+ ![DDQ Architecture](../assets/images/ddq_architecture.svg)
200
133
 
201
134
  **Location:** `lib/smart_message/deduplication.rb`, `lib/smart_message/ddq/`
202
135
 
136
+ **Key Features:**
137
+ - Handler-scoped deduplication (each handler gets its own DDQ)
138
+ - UUID-based duplicate detection
139
+ - Multiple storage backends (Memory, Redis)
140
+ - O(1) performance with hybrid Array + Set data structure
141
+ - Thread-safe operations with mutex locks
142
+
203
143
  ### 6. Message Headers
204
144
 
205
145
  Standard metadata attached to every message with entity addressing support.
@@ -217,8 +157,10 @@ header = message._sm_header
217
157
  puts header.uuid # "550e8400-e29b-41d4-a716-446655440000"
218
158
  puts header.message_class # "MyMessage"
219
159
  puts header.published_at # 2025-08-17 10:30:00 UTC
160
+ puts header.publisher_pid # 12345
220
161
  puts header.from # "payment-service"
221
162
  puts header.to # "order-service"
163
+ puts header.reply_to # "payment-service" (defaults to from)
222
164
  puts header.version # 1
223
165
  ```
224
166
 
@@ -232,7 +174,6 @@ class OrderMessage < SmartMessage::Base
232
174
 
233
175
  config do
234
176
  transport SmartMessage::Transport.create(:memory)
235
- serializer SmartMessage::Serializer::JSON.new
236
177
  end
237
178
  end
238
179
  ```
@@ -257,20 +198,21 @@ order = OrderMessage.new(order_id: "123", amount: 99.99)
257
198
  order.from("order-service").to("payment-service")
258
199
  order.publish
259
200
  # 1. Creates header with UUID, timestamp, addressing
260
- # 2. Encodes message via serializer
201
+ # 2. Encodes message via transport's serializer
261
202
  # 3. Sends via transport
262
203
  # 4. Circuit breaker monitors for failures
263
204
  ```
264
205
 
265
206
  ### 4. Receiving Phase
266
207
  ```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
208
+ # Transport receives serialized message
209
+ transport.receive(message_class, serialized_message)
210
+ # 1. Decodes message using transport's serializer
211
+ # 2. Routes decoded message to dispatcher
212
+ # 3. Dispatcher checks DDQ for duplicates per handler
213
+ # 4. Applies message filters (from/to/broadcast)
214
+ # 5. Spawns thread for processing matching handlers
215
+ # 6. Marks UUID as processed in handler's DDQ after successful processing
274
216
  ```
275
217
 
276
218
  ### 5. Message Handler Processing
@@ -278,43 +220,42 @@ transport.receive(header, payload)
278
220
  SmartMessage supports multiple handler types, routed through the dispatcher:
279
221
 
280
222
  ```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)
223
+ # Default handler (self.process method) - receives decoded message instance
224
+ def self.process(decoded_message)
225
+ order = decoded_message # Already a fully decoded OrderMessage instance
285
226
  fulfill_order(order)
286
227
  end
287
228
 
288
- # Block handler (inline processing)
289
- OrderMessage.subscribe do |header, payload|
290
- data = JSON.parse(payload)
291
- quick_processing(data)
229
+ # Block handler (inline processing) - receives decoded message instance
230
+ OrderMessage.subscribe do |decoded_message|
231
+ quick_processing(decoded_message)
292
232
  end
293
233
 
294
- # Proc handler (reusable across message types)
295
- audit_proc = proc do |header, payload|
296
- AuditService.log_message(header.message_class, payload)
234
+ # Proc handler (reusable across message types) - receives decoded message instance
235
+ audit_proc = proc do |decoded_message|
236
+ AuditService.log_message(decoded_message.class.name, decoded_message)
297
237
  end
298
238
  OrderMessage.subscribe(audit_proc)
299
239
 
300
- # Method handler (service class processing)
240
+ # Method handler (service class processing) - receives decoded message instance
301
241
  class OrderService
302
- def self.process_order(header, payload)
303
- data = JSON.parse(payload)
304
- complex_business_logic(data)
242
+ def self.process_order(decoded_message)
243
+ complex_business_logic(decoded_message)
305
244
  end
306
245
  end
307
246
  OrderMessage.subscribe("OrderService.process_order")
308
247
  ```
309
248
 
310
249
  **Handler Routing Process:**
311
- 1. Dispatcher receives message and header
250
+ 1. Dispatcher receives decoded message instance
312
251
  2. Looks up all registered handlers for message class
313
- 3. For each handler:
252
+ 3. For each handler that matches filters:
253
+ - Checks DDQ for duplicates (handler-scoped)
314
254
  - **String handlers**: Resolves to class method via constantize
315
255
  - **Proc handlers**: Calls proc directly from registry
316
- 4. Executes handlers in parallel threads
317
- 5. Collects statistics and handles errors
256
+ 4. Executes handlers in parallel threads with circuit breaker protection
257
+ 5. Marks UUID as processed in handler's DDQ after successful completion
258
+ 6. Collects statistics and handles errors
318
259
 
319
260
  ## Plugin System Architecture
320
261
 
@@ -327,7 +268,6 @@ SmartMessage supports configuration at both class and instance levels:
327
268
  class PaymentMessage < SmartMessage::Base
328
269
  config do
329
270
  transport ProductionTransport.new
330
- serializer SecureSerializer.new
331
271
  end
332
272
  end
333
273
 
@@ -364,10 +304,15 @@ end
364
304
  The dispatcher uses `Concurrent::CachedThreadPool` for processing:
365
305
 
366
306
  ```ruby
367
- # Each message processing happens in its own thread
307
+ # Each message processing happens in its own thread with circuit breaker protection
368
308
  @router_pool.post do
369
- # Message processing happens here
370
- target_class.constantize.process(header, payload)
309
+ circuit_result = circuit(:message_processor).wrap do
310
+ if proc_handler?(message_processor)
311
+ SmartMessage::Base.call_proc_handler(message_processor, decoded_message)
312
+ else
313
+ target_class.constantize.method(class_method).call(decoded_message)
314
+ end
315
+ end
371
316
  end
372
317
  ```
373
318
 
@@ -396,11 +341,13 @@ puts "Completed tasks: #{status[:completed_task_count]}"
396
341
  Processing exceptions are isolated to prevent cascade failures:
397
342
 
398
343
  ```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
344
+ circuit_result = circuit(:message_processor).wrap do
345
+ # Handler execution with circuit breaker protection
346
+ end
347
+
348
+ # Handle circuit breaker fallback responses
349
+ if circuit_result.is_a?(Hash) && circuit_result[:circuit_breaker]
350
+ handle_circuit_breaker_fallback(circuit_result, decoded_message, message_processor)
404
351
  end
405
352
  ```
406
353
 
@@ -415,6 +362,7 @@ module SmartMessage::Errors
415
362
  class NotImplemented < RuntimeError; end
416
363
  class ReceivedMessageNotSubscribed < RuntimeError; end
417
364
  class UnknownMessageClass < RuntimeError; end
365
+ class ValidationError < RuntimeError; end
418
366
  end
419
367
  ```
420
368
 
@@ -425,13 +373,12 @@ end
425
373
  SmartMessage integrates BreakerMachines for production-grade reliability:
426
374
 
427
375
  ```ruby
428
- # Transport operations protected by circuit breakers
376
+ # Circuit breakers are automatically configured for all transports
429
377
  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
378
+ # Inherits circuit breaker configuration:
379
+ # - :transport_publish for publishing operations
380
+ # - :transport_subscribe for subscription operations
381
+ # - Automatic DLQ fallback for failed publishes
435
382
  end
436
383
  ```
437
384
 
@@ -450,25 +397,12 @@ message.publish # If transport fails, goes to DLQ
450
397
 
451
398
  # Manual capture for business logic failures
452
399
  dlq = SmartMessage::DeadLetterQueue.default
453
- dlq.enqueue(header, payload, error: "Validation failed")
400
+ dlq.enqueue(decoded_message, error: "Validation failed", transport: "manual")
454
401
  ```
455
402
 
456
403
  **DLQ Architecture:**
457
404
 
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
- ```
405
+ ![Dead Letter Queue Architecture](../assets/images/dlq_architecture.svg)
472
406
 
473
407
  **DLQ Features:**
474
408
  - JSON Lines format for efficient append operations
@@ -521,7 +455,6 @@ Configuration uses method-based DSL:
521
455
  ```ruby
522
456
  config do
523
457
  transport MyTransport.new(option1: value1)
524
- serializer MySerializer.new(option2: value2)
525
458
  logger MyLogger.new(level: :debug)
526
459
  end
527
460
  ```
@@ -536,7 +469,9 @@ When a message needs a plugin:
536
469
 
537
470
  ```ruby
538
471
  def transport
539
- @transport || @@transport || raise(Errors::TransportNotConfigured)
472
+ @transport || self.class.class_variable_get(:@@transport) || raise(Errors::TransportNotConfigured)
473
+ rescue NameError
474
+ raise(Errors::TransportNotConfigured)
540
475
  end
541
476
  ```
542
477
 
@@ -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
  ```