smart_message 0.0.1

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +3 -0
  3. data/.gitignore +8 -0
  4. data/.travis.yml +7 -0
  5. data/CHANGELOG.md +100 -0
  6. data/COMMITS.md +196 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +71 -0
  9. data/README.md +303 -0
  10. data/Rakefile +10 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/docs/README.md +52 -0
  14. data/docs/architecture.md +370 -0
  15. data/docs/dispatcher.md +593 -0
  16. data/docs/examples.md +808 -0
  17. data/docs/getting-started.md +235 -0
  18. data/docs/ideas_to_think_about.md +329 -0
  19. data/docs/serializers.md +575 -0
  20. data/docs/transports.md +501 -0
  21. data/docs/troubleshooting.md +582 -0
  22. data/examples/01_point_to_point_orders.rb +200 -0
  23. data/examples/02_publish_subscribe_events.rb +364 -0
  24. data/examples/03_many_to_many_chat.rb +608 -0
  25. data/examples/README.md +335 -0
  26. data/examples/tmux_chat/README.md +283 -0
  27. data/examples/tmux_chat/bot_agent.rb +272 -0
  28. data/examples/tmux_chat/human_agent.rb +197 -0
  29. data/examples/tmux_chat/room_monitor.rb +158 -0
  30. data/examples/tmux_chat/shared_chat_system.rb +295 -0
  31. data/examples/tmux_chat/start_chat_demo.sh +190 -0
  32. data/examples/tmux_chat/stop_chat_demo.sh +22 -0
  33. data/lib/simple_stats.rb +57 -0
  34. data/lib/smart_message/base.rb +284 -0
  35. data/lib/smart_message/dispatcher/.keep +0 -0
  36. data/lib/smart_message/dispatcher.rb +146 -0
  37. data/lib/smart_message/errors.rb +29 -0
  38. data/lib/smart_message/header.rb +20 -0
  39. data/lib/smart_message/logger/base.rb +8 -0
  40. data/lib/smart_message/logger.rb +7 -0
  41. data/lib/smart_message/serializer/base.rb +23 -0
  42. data/lib/smart_message/serializer/json.rb +22 -0
  43. data/lib/smart_message/serializer.rb +10 -0
  44. data/lib/smart_message/transport/base.rb +85 -0
  45. data/lib/smart_message/transport/memory_transport.rb +69 -0
  46. data/lib/smart_message/transport/registry.rb +59 -0
  47. data/lib/smart_message/transport/stdout_transport.rb +62 -0
  48. data/lib/smart_message/transport.rb +41 -0
  49. data/lib/smart_message/version.rb +7 -0
  50. data/lib/smart_message/wrapper.rb +43 -0
  51. data/lib/smart_message.rb +54 -0
  52. data/smart_message.gemspec +53 -0
  53. metadata +252 -0
data/docs/examples.md ADDED
@@ -0,0 +1,808 @@
1
+ # Examples & Use Cases
2
+
3
+ This document provides practical examples of using SmartMessage in real-world scenarios.
4
+
5
+ ## Basic Messaging Patterns
6
+
7
+ ### Simple Notification System
8
+
9
+ ```ruby
10
+ require 'smart_message'
11
+
12
+ class NotificationMessage < SmartMessage::Base
13
+ property :recipient
14
+ property :subject
15
+ property :body
16
+ property :priority, default: 'normal'
17
+ property :channel, default: 'email'
18
+
19
+ config do
20
+ transport SmartMessage::Transport.create(:stdout, loopback: true)
21
+ serializer SmartMessage::Serializer::JSON.new
22
+ end
23
+
24
+ def self.process(message_header, message_payload)
25
+ data = JSON.parse(message_payload)
26
+ notification = new(data)
27
+
28
+ case notification.channel
29
+ when 'email'
30
+ send_email(notification)
31
+ when 'sms'
32
+ send_sms(notification)
33
+ when 'push'
34
+ send_push_notification(notification)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def self.send_email(notification)
41
+ puts "📧 Sending email to #{notification.recipient}"
42
+ puts "Subject: #{notification.subject}"
43
+ puts "Priority: #{notification.priority}"
44
+ end
45
+
46
+ def self.send_sms(notification)
47
+ puts "📱 Sending SMS to #{notification.recipient}"
48
+ puts "Message: #{notification.body}"
49
+ end
50
+
51
+ def self.send_push_notification(notification)
52
+ puts "🔔 Sending push notification to #{notification.recipient}"
53
+ puts "Title: #{notification.subject}"
54
+ end
55
+ end
56
+
57
+ # Setup
58
+ NotificationMessage.subscribe
59
+
60
+ # Send notifications
61
+ NotificationMessage.new(
62
+ recipient: "user@example.com",
63
+ subject: "Welcome!",
64
+ body: "Thanks for signing up!",
65
+ priority: "high"
66
+ ).publish
67
+
68
+ NotificationMessage.new(
69
+ recipient: "+1234567890",
70
+ subject: "Alert",
71
+ body: "Your order has shipped!",
72
+ channel: "sms"
73
+ ).publish
74
+ ```
75
+
76
+ ### Event-Driven Architecture
77
+
78
+ ```ruby
79
+ # User registration event
80
+ class UserRegisteredEvent < SmartMessage::Base
81
+ property :user_id
82
+ property :email
83
+ property :name
84
+ property :registration_source
85
+ property :timestamp, default: -> { Time.now.iso8601 }
86
+
87
+ config do
88
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
89
+ serializer SmartMessage::Serializer::JSON.new
90
+ end
91
+
92
+ def self.process(message_header, message_payload)
93
+ data = JSON.parse(message_payload)
94
+ event = new(data)
95
+
96
+ # Fan out to multiple handlers
97
+ send_welcome_email(event)
98
+ create_user_profile(event)
99
+ track_analytics(event)
100
+ setup_default_preferences(event)
101
+ end
102
+
103
+ private
104
+
105
+ def self.send_welcome_email(event)
106
+ WelcomeEmailMessage.new(
107
+ user_id: event.user_id,
108
+ email: event.email,
109
+ name: event.name
110
+ ).publish
111
+ end
112
+
113
+ def self.create_user_profile(event)
114
+ CreateProfileMessage.new(
115
+ user_id: event.user_id,
116
+ source: event.registration_source
117
+ ).publish
118
+ end
119
+
120
+ def self.track_analytics(event)
121
+ AnalyticsMessage.new(
122
+ event_type: 'user_registration',
123
+ user_id: event.user_id,
124
+ properties: {
125
+ source: event.registration_source,
126
+ timestamp: event.timestamp
127
+ }
128
+ ).publish
129
+ end
130
+
131
+ def self.setup_default_preferences(event)
132
+ PreferencesMessage.new(
133
+ user_id: event.user_id,
134
+ preferences: default_preferences
135
+ ).publish
136
+ end
137
+
138
+ def self.default_preferences
139
+ {
140
+ email_notifications: true,
141
+ marketing_emails: false,
142
+ theme: 'light'
143
+ }
144
+ end
145
+ end
146
+
147
+ # Supporting message classes
148
+ class WelcomeEmailMessage < SmartMessage::Base
149
+ property :user_id
150
+ property :email
151
+ property :name
152
+
153
+ config do
154
+ transport SmartMessage::Transport.create(:stdout)
155
+ serializer SmartMessage::Serializer::JSON.new
156
+ end
157
+
158
+ def self.process(message_header, message_payload)
159
+ data = JSON.parse(message_payload)
160
+ message = new(data)
161
+
162
+ puts "📧 Sending welcome email to #{message.email} (#{message.name})"
163
+ # Email sending logic here
164
+ end
165
+ end
166
+
167
+ class AnalyticsMessage < SmartMessage::Base
168
+ property :event_type
169
+ property :user_id
170
+ property :properties
171
+
172
+ config do
173
+ transport SmartMessage::Transport.create(:stdout)
174
+ serializer SmartMessage::Serializer::JSON.new
175
+ end
176
+
177
+ def self.process(message_header, message_payload)
178
+ data = JSON.parse(message_payload)
179
+ event = new(data)
180
+
181
+ puts "📊 Tracking event: #{event.event_type} for user #{event.user_id}"
182
+ # Analytics tracking logic here
183
+ end
184
+ end
185
+
186
+ # Setup and trigger
187
+ [UserRegisteredEvent, WelcomeEmailMessage, AnalyticsMessage].each(&:subscribe)
188
+
189
+ # Simulate user registration
190
+ UserRegisteredEvent.new(
191
+ user_id: 12345,
192
+ email: "alice@example.com",
193
+ name: "Alice Johnson",
194
+ registration_source: "web_form"
195
+ ).publish
196
+ ```
197
+
198
+ ## E-commerce Order Processing
199
+
200
+ ```ruby
201
+ # Order lifecycle management
202
+ class OrderCreatedMessage < SmartMessage::Base
203
+ property :order_id
204
+ property :customer_id
205
+ property :items
206
+ property :total_amount
207
+ property :shipping_address
208
+ property :created_at, default: -> { Time.now.iso8601 }
209
+
210
+ config do
211
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
212
+ serializer SmartMessage::Serializer::JSON.new
213
+ end
214
+
215
+ def self.process(message_header, message_payload)
216
+ data = JSON.parse(message_payload)
217
+ order = new(data)
218
+
219
+ # Validate order
220
+ if valid_order?(order)
221
+ # Reserve inventory
222
+ InventoryReservationMessage.new(
223
+ order_id: order.order_id,
224
+ items: order.items
225
+ ).publish
226
+
227
+ # Process payment
228
+ PaymentProcessingMessage.new(
229
+ order_id: order.order_id,
230
+ customer_id: order.customer_id,
231
+ amount: order.total_amount
232
+ ).publish
233
+ else
234
+ # Handle invalid order
235
+ OrderRejectedMessage.new(
236
+ order_id: order.order_id,
237
+ reason: "Invalid order data"
238
+ ).publish
239
+ end
240
+ end
241
+
242
+ private
243
+
244
+ def self.valid_order?(order)
245
+ order.items&.any? && order.total_amount&.positive?
246
+ end
247
+ end
248
+
249
+ class InventoryReservationMessage < SmartMessage::Base
250
+ property :order_id
251
+ property :items
252
+
253
+ config do
254
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
255
+ serializer SmartMessage::Serializer::JSON.new
256
+ end
257
+
258
+ def self.process(message_header, message_payload)
259
+ data = JSON.parse(message_payload)
260
+ reservation = new(data)
261
+
262
+ success = reserve_inventory(reservation.items)
263
+
264
+ if success
265
+ InventoryReservedMessage.new(
266
+ order_id: reservation.order_id
267
+ ).publish
268
+ else
269
+ InventoryFailedMessage.new(
270
+ order_id: reservation.order_id,
271
+ reason: "Insufficient stock"
272
+ ).publish
273
+ end
274
+ end
275
+
276
+ private
277
+
278
+ def self.reserve_inventory(items)
279
+ # Inventory reservation logic
280
+ puts "🏪 Reserving inventory for #{items.length} items"
281
+ true # Simulate success
282
+ end
283
+ end
284
+
285
+ class PaymentProcessingMessage < SmartMessage::Base
286
+ property :order_id
287
+ property :customer_id
288
+ property :amount
289
+
290
+ config do
291
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
292
+ serializer SmartMessage::Serializer::JSON.new
293
+ end
294
+
295
+ def self.process(message_header, message_payload)
296
+ data = JSON.parse(message_payload)
297
+ payment = new(data)
298
+
299
+ success = process_payment(payment)
300
+
301
+ if success
302
+ PaymentSuccessMessage.new(
303
+ order_id: payment.order_id,
304
+ transaction_id: generate_transaction_id
305
+ ).publish
306
+ else
307
+ PaymentFailedMessage.new(
308
+ order_id: payment.order_id,
309
+ reason: "Payment declined"
310
+ ).publish
311
+ end
312
+ end
313
+
314
+ private
315
+
316
+ def self.process_payment(payment)
317
+ puts "💳 Processing payment of $#{payment.amount} for order #{payment.order_id}"
318
+ true # Simulate success
319
+ end
320
+
321
+ def self.generate_transaction_id
322
+ "txn_#{SecureRandom.hex(8)}"
323
+ end
324
+ end
325
+
326
+ # Setup
327
+ [
328
+ OrderCreatedMessage,
329
+ InventoryReservationMessage,
330
+ PaymentProcessingMessage
331
+ ].each(&:subscribe)
332
+
333
+ # Create an order
334
+ OrderCreatedMessage.new(
335
+ order_id: "ORD-001",
336
+ customer_id: "CUST-123",
337
+ items: [
338
+ { sku: "WIDGET-A", quantity: 2, price: 19.99 },
339
+ { sku: "GADGET-B", quantity: 1, price: 49.99 }
340
+ ],
341
+ total_amount: 89.97,
342
+ shipping_address: {
343
+ street: "123 Main St",
344
+ city: "Anytown",
345
+ state: "CA",
346
+ zip: "12345"
347
+ }
348
+ ).publish
349
+ ```
350
+
351
+ ## Logging and Monitoring
352
+
353
+ ```ruby
354
+ # Centralized logging system
355
+ class LogMessage < SmartMessage::Base
356
+ property :level
357
+ property :service
358
+ property :message
359
+ property :context
360
+ property :timestamp, default: -> { Time.now.iso8601 }
361
+ property :correlation_id
362
+
363
+ config do
364
+ transport SmartMessage::Transport.create(:stdout, output: "application.log")
365
+ serializer SmartMessage::Serializer::JSON.new
366
+ end
367
+
368
+ def self.process(message_header, message_payload)
369
+ data = JSON.parse(message_payload)
370
+ log_entry = new(data)
371
+
372
+ formatted_message = format_log_entry(log_entry)
373
+
374
+ case log_entry.level
375
+ when 'ERROR', 'FATAL'
376
+ send_alert(log_entry)
377
+ when 'WARN'
378
+ track_warning(log_entry)
379
+ end
380
+
381
+ puts formatted_message
382
+ end
383
+
384
+ private
385
+
386
+ def self.format_log_entry(log_entry)
387
+ "[#{log_entry.timestamp}] #{log_entry.level} #{log_entry.service}: #{log_entry.message}" +
388
+ (log_entry.correlation_id ? " (#{log_entry.correlation_id})" : "") +
389
+ (log_entry.context ? " | #{log_entry.context.to_json}" : "")
390
+ end
391
+
392
+ def self.send_alert(log_entry)
393
+ if log_entry.level == 'FATAL'
394
+ puts "🚨 FATAL ERROR ALERT: #{log_entry.message}"
395
+ else
396
+ puts "⚠️ ERROR ALERT: #{log_entry.message}"
397
+ end
398
+ end
399
+
400
+ def self.track_warning(log_entry)
401
+ puts "📝 Warning tracked: #{log_entry.message}"
402
+ end
403
+ end
404
+
405
+ # Application performance monitoring
406
+ class MetricMessage < SmartMessage::Base
407
+ property :metric_name
408
+ property :value
409
+ property :unit
410
+ property :tags
411
+ property :timestamp, default: -> { Time.now.to_f }
412
+
413
+ config do
414
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
415
+ serializer SmartMessage::Serializer::JSON.new
416
+ end
417
+
418
+ def self.process(message_header, message_payload)
419
+ data = JSON.parse(message_payload)
420
+ metric = new(data)
421
+
422
+ # Store metric (would typically go to monitoring system)
423
+ store_metric(metric)
424
+
425
+ # Check for alerts
426
+ check_thresholds(metric)
427
+ end
428
+
429
+ private
430
+
431
+ def self.store_metric(metric)
432
+ puts "📊 Metric: #{metric.metric_name} = #{metric.value} #{metric.unit} #{metric.tags}"
433
+ end
434
+
435
+ def self.check_thresholds(metric)
436
+ case metric.metric_name
437
+ when 'response_time'
438
+ if metric.value > 1000 # More than 1 second
439
+ puts "⚠️ High response time alert: #{metric.value}ms"
440
+ end
441
+ when 'error_rate'
442
+ if metric.value > 0.05 # More than 5% error rate
443
+ puts "🚨 High error rate alert: #{(metric.value * 100).round(2)}%"
444
+ end
445
+ end
446
+ end
447
+ end
448
+
449
+ # Setup
450
+ LogMessage.subscribe
451
+ MetricMessage.subscribe
452
+
453
+ # Log some events
454
+ LogMessage.new(
455
+ level: "INFO",
456
+ service: "user-service",
457
+ message: "User login successful",
458
+ context: { user_id: 123, ip: "192.168.1.1" },
459
+ correlation_id: "req-abc123"
460
+ ).publish
461
+
462
+ LogMessage.new(
463
+ level: "ERROR",
464
+ service: "payment-service",
465
+ message: "Payment gateway timeout",
466
+ context: { order_id: "ORD-001", gateway: "stripe" },
467
+ correlation_id: "req-def456"
468
+ ).publish
469
+
470
+ # Send some metrics
471
+ MetricMessage.new(
472
+ metric_name: "response_time",
473
+ value: 1250,
474
+ unit: "ms",
475
+ tags: { service: "api", endpoint: "/users" }
476
+ ).publish
477
+
478
+ MetricMessage.new(
479
+ metric_name: "error_rate",
480
+ value: 0.08,
481
+ unit: "percentage",
482
+ tags: { service: "payment-service" }
483
+ ).publish
484
+ ```
485
+
486
+ ## Gateway Pattern
487
+
488
+ ```ruby
489
+ # Bridge between different message systems
490
+ class MessageGateway < SmartMessage::Base
491
+ property :source_system
492
+ property :destination_system
493
+ property :message_type
494
+ property :payload
495
+
496
+ # Receive from one transport
497
+ config do
498
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
499
+ serializer SmartMessage::Serializer::JSON.new
500
+ end
501
+
502
+ def self.process(message_header, message_payload)
503
+ data = JSON.parse(message_payload)
504
+ gateway_message = new(data)
505
+
506
+ # Transform and forward to destination system
507
+ case gateway_message.destination_system
508
+ when 'email_system'
509
+ forward_to_email_system(gateway_message)
510
+ when 'sms_system'
511
+ forward_to_sms_system(gateway_message)
512
+ when 'audit_system'
513
+ forward_to_audit_system(gateway_message)
514
+ end
515
+ end
516
+
517
+ private
518
+
519
+ def self.forward_to_email_system(gateway_message)
520
+ # Create a new message instance with different transport
521
+ email_message = EmailSystemMessage.new(
522
+ original_payload: gateway_message.payload,
523
+ source: gateway_message.source_system
524
+ )
525
+
526
+ # Override transport for this instance
527
+ email_message.config do
528
+ transport SmartMessage::Transport.create(:stdout, output: "email_system.log")
529
+ end
530
+
531
+ email_message.publish
532
+ end
533
+
534
+ def self.forward_to_sms_system(gateway_message)
535
+ sms_message = SMSSystemMessage.new(
536
+ original_payload: gateway_message.payload,
537
+ source: gateway_message.source_system
538
+ )
539
+
540
+ sms_message.config do
541
+ transport SmartMessage::Transport.create(:stdout, output: "sms_system.log")
542
+ end
543
+
544
+ sms_message.publish
545
+ end
546
+
547
+ def self.forward_to_audit_system(gateway_message)
548
+ audit_message = AuditSystemMessage.new(
549
+ event_type: gateway_message.message_type,
550
+ data: gateway_message.payload,
551
+ source_system: gateway_message.source_system,
552
+ processed_at: Time.now.iso8601
553
+ )
554
+
555
+ audit_message.config do
556
+ transport SmartMessage::Transport.create(:stdout, output: "audit_system.log")
557
+ end
558
+
559
+ audit_message.publish
560
+ end
561
+ end
562
+
563
+ # Destination system message classes
564
+ class EmailSystemMessage < SmartMessage::Base
565
+ property :original_payload
566
+ property :source
567
+
568
+ def self.process(message_header, message_payload)
569
+ puts "📧 Email system processed message from #{JSON.parse(message_payload)['source']}"
570
+ end
571
+ end
572
+
573
+ class SMSSystemMessage < SmartMessage::Base
574
+ property :original_payload
575
+ property :source
576
+
577
+ def self.process(message_header, message_payload)
578
+ puts "📱 SMS system processed message from #{JSON.parse(message_payload)['source']}"
579
+ end
580
+ end
581
+
582
+ class AuditSystemMessage < SmartMessage::Base
583
+ property :event_type
584
+ property :data
585
+ property :source_system
586
+ property :processed_at
587
+
588
+ def self.process(message_header, message_payload)
589
+ puts "📋 Audit system logged event from #{JSON.parse(message_payload)['source_system']}"
590
+ end
591
+ end
592
+
593
+ # Setup
594
+ [MessageGateway, EmailSystemMessage, SMSSystemMessage, AuditSystemMessage].each(&:subscribe)
595
+
596
+ # Route messages through gateway
597
+ MessageGateway.new(
598
+ source_system: "web_app",
599
+ destination_system: "email_system",
600
+ message_type: "notification",
601
+ payload: { recipient: "user@example.com", subject: "Hello!" }
602
+ ).publish
603
+
604
+ MessageGateway.new(
605
+ source_system: "mobile_app",
606
+ destination_system: "audit_system",
607
+ message_type: "user_action",
608
+ payload: { action: "login", user_id: 123 }
609
+ ).publish
610
+ ```
611
+
612
+ ## Error Handling and Retry Patterns
613
+
614
+ ```ruby
615
+ # Resilient message processing with retries
616
+ class ResilientMessage < SmartMessage::Base
617
+ property :data
618
+ property :retry_count, default: 0
619
+ property :max_retries, default: 3
620
+ property :original_error
621
+
622
+ config do
623
+ transport SmartMessage::Transport.create(:memory, auto_process: true)
624
+ serializer SmartMessage::Serializer::JSON.new
625
+ end
626
+
627
+ def self.process(message_header, message_payload)
628
+ data = JSON.parse(message_payload)
629
+ message = new(data)
630
+
631
+ begin
632
+ # Simulate potentially failing operation
633
+ if should_fail?(message)
634
+ raise StandardError, "Simulated failure"
635
+ end
636
+
637
+ puts "✅ Successfully processed message: #{message.data}"
638
+
639
+ rescue => e
640
+ handle_error(message, e)
641
+ end
642
+ end
643
+
644
+ private
645
+
646
+ def self.should_fail?(message)
647
+ # Simulate 30% failure rate
648
+ rand < 0.3
649
+ end
650
+
651
+ def self.handle_error(message, error)
652
+ puts "❌ Error processing message: #{error.message}"
653
+
654
+ if message.retry_count < message.max_retries
655
+ # Retry with exponential backoff
656
+ delay = 2 ** message.retry_count
657
+ puts "🔄 Retrying in #{delay} seconds (attempt #{message.retry_count + 1})"
658
+
659
+ # In a real system, you'd use a delayed job or similar
660
+ Thread.new do
661
+ sleep(delay)
662
+
663
+ retry_message = new(
664
+ data: message.data,
665
+ retry_count: message.retry_count + 1,
666
+ max_retries: message.max_retries,
667
+ original_error: error.message
668
+ )
669
+
670
+ retry_message.publish
671
+ end
672
+ else
673
+ # Max retries exceeded, send to dead letter queue
674
+ DeadLetterMessage.new(
675
+ original_message: message.to_h,
676
+ final_error: error.message,
677
+ retry_attempts: message.retry_count,
678
+ failed_at: Time.now.iso8601
679
+ ).publish
680
+ end
681
+ end
682
+ end
683
+
684
+ class DeadLetterMessage < SmartMessage::Base
685
+ property :original_message
686
+ property :final_error
687
+ property :retry_attempts
688
+ property :failed_at
689
+
690
+ config do
691
+ transport SmartMessage::Transport.create(:stdout, output: "dead_letter_queue.log")
692
+ serializer SmartMessage::Serializer::JSON.new
693
+ end
694
+
695
+ def self.process(message_header, message_payload)
696
+ data = JSON.parse(message_payload)
697
+ dead_letter = new(data)
698
+
699
+ puts "💀 Message sent to dead letter queue:"
700
+ puts " Original: #{dead_letter.original_message}"
701
+ puts " Error: #{dead_letter.final_error}"
702
+ puts " Attempts: #{dead_letter.retry_attempts}"
703
+ puts " Failed at: #{dead_letter.failed_at}"
704
+
705
+ # Could trigger alerts, save to database, etc.
706
+ end
707
+ end
708
+
709
+ # Setup
710
+ ResilientMessage.subscribe
711
+ DeadLetterMessage.subscribe
712
+
713
+ # Send messages that might fail
714
+ 5.times do |i|
715
+ ResilientMessage.new(
716
+ data: "Test message #{i + 1}"
717
+ ).publish
718
+
719
+ sleep(0.1) # Small delay between messages
720
+ end
721
+ ```
722
+
723
+ ## Testing Helpers
724
+
725
+ ```ruby
726
+ # Test utilities for SmartMessage
727
+ module SmartMessageTestHelpers
728
+ def self.with_test_transport
729
+ original_transports = {}
730
+
731
+ # Store original transports
732
+ SmartMessage::Base.descendants.each do |klass|
733
+ original_transports[klass] = klass.transport
734
+ end
735
+
736
+ # Set up test transport
737
+ test_transport = SmartMessage::Transport.create(:memory, auto_process: true)
738
+
739
+ SmartMessage::Base.descendants.each do |klass|
740
+ klass.config do
741
+ transport test_transport
742
+ end
743
+ end
744
+
745
+ yield test_transport
746
+
747
+ ensure
748
+ # Restore original transports
749
+ original_transports.each do |klass, transport|
750
+ klass.config do
751
+ transport transport
752
+ end
753
+ end
754
+ end
755
+
756
+ def self.clear_statistics
757
+ SS.reset
758
+ end
759
+
760
+ def self.wait_for_processing(timeout: 1.0)
761
+ start_time = Time.now
762
+
763
+ while Time.now - start_time < timeout
764
+ # Check if any messages are still being processed
765
+ # This is a simplified check
766
+ sleep(0.01)
767
+ end
768
+ end
769
+ end
770
+
771
+ # Example test usage
772
+ def test_message_processing
773
+ SmartMessageTestHelpers.with_test_transport do |transport|
774
+ # Clear any existing messages
775
+ transport.clear_messages
776
+ SmartMessageTestHelpers.clear_statistics
777
+
778
+ # Set up subscriptions
779
+ TestMessage.subscribe
780
+
781
+ # Send test message
782
+ TestMessage.new(data: "test").publish
783
+
784
+ # Wait for processing
785
+ SmartMessageTestHelpers.wait_for_processing
786
+
787
+ # Check results
788
+ puts "Messages in transport: #{transport.message_count}"
789
+ puts "Published count: #{SS.get('TestMessage', 'publish')}"
790
+ puts "Processed count: #{SS.get('TestMessage', 'TestMessage.process', 'routed')}"
791
+ end
792
+ end
793
+
794
+ class TestMessage < SmartMessage::Base
795
+ property :data
796
+
797
+ def self.process(message_header, message_payload)
798
+ data = JSON.parse(message_payload)
799
+ message = new(data)
800
+ puts "Processed test message: #{message.data}"
801
+ end
802
+ end
803
+
804
+ # Run the test
805
+ test_message_processing
806
+ ```
807
+
808
+ These examples demonstrate the flexibility and power of SmartMessage for building robust, scalable messaging systems. Each pattern can be adapted to your specific needs and combined with other patterns for more complex workflows.