smart_message 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/Gemfile.lock +9 -1
- data/examples/01_point_to_point_orders.rb +4 -2
- data/examples/02_publish_subscribe_events.rb +2 -1
- data/examples/03_many_to_many_chat.rb +10 -5
- data/examples/04_redis_smart_home_iot.rb +32 -16
- data/examples/05_proc_handlers.rb +3 -2
- data/examples/06_custom_logger_example.rb +13 -7
- data/examples/07_error_handling_scenarios.rb +26 -15
- data/examples/08_entity_addressing_basic.rb +14 -7
- data/examples/08_entity_addressing_with_filtering.rb +24 -12
- data/examples/tmux_chat/bot_agent.rb +2 -1
- data/examples/tmux_chat/shared_chat_system.rb +4 -2
- data/lib/smart_message/base.rb +6 -3
- data/lib/smart_message/circuit_breaker.rb +227 -0
- data/lib/smart_message/dispatcher.rb +136 -15
- data/lib/smart_message/serializer/base.rb +77 -4
- data/lib/smart_message/serializer/json.rb +2 -2
- data/lib/smart_message/transport/base.rb +138 -2
- data/lib/smart_message/transport/memory_transport.rb +1 -1
- data/lib/smart_message/transport/redis_transport.rb +43 -13
- data/lib/smart_message/transport/stdout_transport.rb +1 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +1 -0
- data/smart_message.gemspec +1 -0
- metadata +16 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 118cf15fea99493c202bd383a547358a44a3873284cfe656665701213b40b51e
|
4
|
+
data.tar.gz: 7e542557b5b88f2963291232a875ba4dab44c682673251d7e976ac913ff41c14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 637591235fcdf1feb8708f244d1f2f6b416d7330bca7f06c597581ea9ffcb3eb55e74610bcc045c7839e50304f978fd24092c870528a7f3277cd0d8be2620ae4
|
7
|
+
data.tar.gz: 35773299f97565981fc778bd76358c2560eef1d6c4885367ddb66d81448136c8a8a9f402d26477d2c7ac29c885f07906a7e3f0396fb827fdf15e5bbe3e5a43bc
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
|
+
## [0.0.7] 2025-08-19
|
10
|
+
### Added
|
11
|
+
- **Production-Grade Circuit Breaker Integration**: Comprehensive reliability patterns using BreakerMachines gem
|
12
|
+
- Circuit breaker protection for message processing operations with configurable failure thresholds
|
13
|
+
- Transport-level circuit breakers for publish/subscribe operations with automatic fallback
|
14
|
+
- Integrated circuit breaker DSL throughout SmartMessage components for production reliability
|
15
|
+
- Memory and Redis storage backends for circuit breaker state persistence
|
16
|
+
- Built-in fallback mechanisms including dead letter queue, retry with exponential backoff, and graceful degradation
|
17
|
+
- Circuit breaker statistics and introspection capabilities for monitoring and debugging
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
- **Critical: Redis Transport Header Preservation**: Fixed message header information loss during Redis pub/sub transport
|
21
|
+
- **Root Cause**: Redis transport was only sending message payload through channels, reconstructing generic headers on receive
|
22
|
+
- **Impact**: Message addressing information (`from`, `to`, `reply_to`) was lost, breaking entity-aware filtering and routing
|
23
|
+
- **Solution**: Modified Redis transport to combine header and payload into JSON structure during publish/subscribe
|
24
|
+
- **Result**: Full header information now preserved across Redis transport with backward compatibility fallback
|
25
|
+
- Redis IoT example now displays all expected sensor data, alerts, and real-time device monitoring output
|
26
|
+
- **Performance: Dispatcher Shutdown Optimization**: Dramatically improved dispatcher shutdown speed
|
27
|
+
- **Previous Issue**: Slow 1-second polling with no timeout mechanism caused delays up to several minutes
|
28
|
+
- **Optimization**: Replaced with native `wait_for_termination(3)` using Concurrent::ThreadPool built-in timeout
|
29
|
+
- **Performance Gain**: Shutdown now completes in milliseconds instead of seconds
|
30
|
+
- Removed unnecessary circuit breaker wrapper - thread pool handles timeout and fallback natively
|
31
|
+
- **Code Quality: Transport Method Signature Consistency**: Ensured all transport `do_publish` methods use consistent parameters
|
32
|
+
- Standardized `do_publish(message_header, message_payload)` signature across all transport implementations
|
33
|
+
- Updated Redis, STDOUT, and Memory transports for consistent interface
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
- **Dispatcher Architecture**: Simplified shutdown mechanism leveraging Concurrent::ThreadPool native capabilities
|
37
|
+
- Removed custom timeout polling logic in favor of built-in `wait_for_termination` method
|
38
|
+
- Eliminated redundant circuit breaker for shutdown operations - thread pool provides timeout and fallback
|
39
|
+
- **BREAKING**: Removed `router_pool_shutdown` circuit breaker configuration (no longer needed)
|
40
|
+
- **Redis Transport Protocol**: Enhanced message format to preserve complete header information
|
41
|
+
- Redis messages now contain both header and payload: `{header: {...}, payload: "..."}`
|
42
|
+
- Automatic fallback to legacy behavior for malformed or old-format messages
|
43
|
+
- Maintains full backward compatibility with existing Redis deployments
|
44
|
+
|
45
|
+
### Enhanced
|
46
|
+
- **Circuit Breaker Integration**: Strategic application of reliability patterns where most beneficial
|
47
|
+
- Message processing operations protected with configurable failure thresholds and fallback behavior
|
48
|
+
- Transport publish/subscribe operations wrapped with circuit breakers for external service protection
|
49
|
+
- Clean separation between internal operations (thread pools) and external dependencies (Redis, etc.)
|
50
|
+
- **Redis Transport Reliability**: Production-ready Redis pub/sub with complete message fidelity
|
51
|
+
- Full message header preservation enables proper entity-aware filtering and routing
|
52
|
+
- IoT examples now demonstrate complete real-time messaging with sensor data, alerts, and device commands
|
53
|
+
- Enhanced error handling with JSON parsing fallbacks for malformed messages
|
54
|
+
- **Developer Experience**: Cleaner, more maintainable codebase with proper separation of concerns
|
55
|
+
- Circuit breakers applied strategically for external dependencies, not internal thread management
|
56
|
+
- Simplified shutdown logic leverages proven Concurrent::ThreadPool patterns
|
57
|
+
- Clear distinction between reliability mechanisms and business logic
|
58
|
+
|
59
|
+
### Documentation
|
60
|
+
- **Circuit Breaker Patterns**: Examples of proper BreakerMachines DSL usage throughout codebase
|
61
|
+
- Strategic application for external service protection vs internal thread management
|
62
|
+
- Configuration examples for different failure scenarios and recovery patterns
|
63
|
+
- Best practices for production-grade messaging reliability
|
9
64
|
|
10
65
|
## [0.0.6] 2025-08-19
|
11
66
|
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
smart_message (0.0.
|
4
|
+
smart_message (0.0.7)
|
5
5
|
activesupport
|
6
|
+
breaker_machines
|
6
7
|
concurrent-ruby
|
7
8
|
hashie
|
8
9
|
redis
|
@@ -27,6 +28,11 @@ GEM
|
|
27
28
|
base64 (0.3.0)
|
28
29
|
benchmark (0.4.1)
|
29
30
|
bigdecimal (3.2.2)
|
31
|
+
breaker_machines (0.4.0)
|
32
|
+
activesupport (>= 7.2)
|
33
|
+
concurrent-ruby (~> 1.3)
|
34
|
+
state_machines (>= 0.50.0)
|
35
|
+
zeitwerk (~> 2.7)
|
30
36
|
concurrent-ruby (1.3.5)
|
31
37
|
connection_pool (2.5.3)
|
32
38
|
debug_me (1.1.1)
|
@@ -53,9 +59,11 @@ GEM
|
|
53
59
|
shoulda-context (2.0.0)
|
54
60
|
shoulda-matchers (4.5.1)
|
55
61
|
activesupport (>= 4.2.0)
|
62
|
+
state_machines (0.100.1)
|
56
63
|
tzinfo (2.0.6)
|
57
64
|
concurrent-ruby (~> 1.0)
|
58
65
|
uri (1.0.3)
|
66
|
+
zeitwerk (2.7.3)
|
59
67
|
|
60
68
|
PLATFORMS
|
61
69
|
arm64-darwin-24
|
@@ -92,7 +92,8 @@ class OrderService
|
|
92
92
|
customer_id: customer_id,
|
93
93
|
amount: amount,
|
94
94
|
payment_method: payment_method,
|
95
|
-
items: items
|
95
|
+
items: items,
|
96
|
+
from: 'OrderService'
|
96
97
|
)
|
97
98
|
|
98
99
|
puts "🏪 OrderService: Sending order to payment processing..."
|
@@ -132,7 +133,8 @@ class PaymentService
|
|
132
133
|
payment_id: payment_id,
|
133
134
|
status: success ? 'success' : 'failed',
|
134
135
|
message: success ? 'Payment processed successfully' : 'Insufficient funds',
|
135
|
-
processed_at: Time.now.iso8601
|
136
|
+
processed_at: Time.now.iso8601,
|
137
|
+
from: 'PaymentService'
|
136
138
|
)
|
137
139
|
|
138
140
|
puts "💳 PaymentService: Sending payment response..."
|
@@ -164,7 +164,8 @@ class HumanChatAgent
|
|
164
164
|
message_type: 'user',
|
165
165
|
timestamp: Time.now.iso8601,
|
166
166
|
mentions: mentions,
|
167
|
-
metadata: { client: 'human_agent' }
|
167
|
+
metadata: { client: 'human_agent' },
|
168
|
+
from: @user_id
|
168
169
|
)
|
169
170
|
|
170
171
|
message.publish
|
@@ -228,7 +229,8 @@ class HumanChatAgent
|
|
228
229
|
user_id: @user_id,
|
229
230
|
command: command,
|
230
231
|
parameters: parameters,
|
231
|
-
timestamp: Time.now.iso8601
|
232
|
+
timestamp: Time.now.iso8601,
|
233
|
+
from: @user_id
|
232
234
|
)
|
233
235
|
|
234
236
|
bot_command.publish
|
@@ -259,7 +261,8 @@ class HumanChatAgent
|
|
259
261
|
notification_type: notification_type,
|
260
262
|
content: content,
|
261
263
|
timestamp: Time.now.iso8601,
|
262
|
-
metadata: { triggered_by: @user_id }
|
264
|
+
metadata: { triggered_by: @user_id },
|
265
|
+
from: @user_id
|
263
266
|
)
|
264
267
|
|
265
268
|
notification.publish
|
@@ -444,7 +447,8 @@ class BotAgent
|
|
444
447
|
message_type: 'bot',
|
445
448
|
timestamp: Time.now.iso8601,
|
446
449
|
mentions: [],
|
447
|
-
metadata: { bot_type: 'service_bot' }
|
450
|
+
metadata: { bot_type: 'service_bot' },
|
451
|
+
from: @bot_id
|
448
452
|
)
|
449
453
|
|
450
454
|
message.publish
|
@@ -480,7 +484,8 @@ class RoomManager
|
|
480
484
|
notification_type: 'room_created',
|
481
485
|
content: "Room '#{name}' was created",
|
482
486
|
timestamp: Time.now.iso8601,
|
483
|
-
metadata: { description: description }
|
487
|
+
metadata: { description: description },
|
488
|
+
from: 'RoomManager'
|
484
489
|
)
|
485
490
|
|
486
491
|
notification.publish
|
@@ -234,7 +234,7 @@ class SmartThermostat
|
|
234
234
|
@battery_level -= 0.1 if rand < 0.1 # Occasional battery drain
|
235
235
|
|
236
236
|
# Publish sensor data
|
237
|
-
SensorDataMessage.new(
|
237
|
+
sensor_msg = SensorDataMessage.new(
|
238
238
|
device_id: @device_id,
|
239
239
|
device_type: 'thermostat',
|
240
240
|
location: @location,
|
@@ -242,8 +242,11 @@ class SmartThermostat
|
|
242
242
|
value: @current_temp.round(1),
|
243
243
|
unit: 'celsius',
|
244
244
|
timestamp: Time.now.iso8601,
|
245
|
-
battery_level: @battery_level.round(1)
|
246
|
-
|
245
|
+
battery_level: @battery_level.round(1),
|
246
|
+
from: @device_id
|
247
|
+
)
|
248
|
+
puts "🌡️ #{@device_id}: Publishing temperature #{@current_temp.round(1)}°C"
|
249
|
+
sensor_msg.publish
|
247
250
|
|
248
251
|
# Check for alerts
|
249
252
|
if @current_temp > 28.0
|
@@ -255,7 +258,8 @@ class SmartThermostat
|
|
255
258
|
location: @location,
|
256
259
|
message: "Temperature too high: #{@current_temp.round(1)}°C",
|
257
260
|
timestamp: Time.now.iso8601,
|
258
|
-
requires_action: true
|
261
|
+
requires_action: true,
|
262
|
+
from: @device_id
|
259
263
|
).publish
|
260
264
|
end
|
261
265
|
|
@@ -325,7 +329,8 @@ class SecurityCamera
|
|
325
329
|
value: true,
|
326
330
|
unit: 'boolean',
|
327
331
|
timestamp: Time.now.iso8601,
|
328
|
-
battery_level: @battery_level.round(1)
|
332
|
+
battery_level: @battery_level.round(1),
|
333
|
+
from: @device_id
|
329
334
|
).publish
|
330
335
|
|
331
336
|
# Send security alert
|
@@ -337,7 +342,8 @@ class SecurityCamera
|
|
337
342
|
location: @location,
|
338
343
|
message: "Motion detected in #{@location}",
|
339
344
|
timestamp: Time.now.iso8601,
|
340
|
-
requires_action: false
|
345
|
+
requires_action: false,
|
346
|
+
from: @device_id
|
341
347
|
).publish
|
342
348
|
|
343
349
|
# Auto-start recording
|
@@ -346,7 +352,8 @@ class SecurityCamera
|
|
346
352
|
command: 'start_recording',
|
347
353
|
parameters: { duration: 30 },
|
348
354
|
requested_by: 'motion_detector',
|
349
|
-
timestamp: Time.now.iso8601
|
355
|
+
timestamp: Time.now.iso8601,
|
356
|
+
from: @device_id
|
350
357
|
).publish
|
351
358
|
|
352
359
|
elsif !motion_detected && @motion_detected
|
@@ -361,7 +368,8 @@ class SecurityCamera
|
|
361
368
|
value: false,
|
362
369
|
unit: 'boolean',
|
363
370
|
timestamp: Time.now.iso8601,
|
364
|
-
battery_level: @battery_level.round(1)
|
371
|
+
battery_level: @battery_level.round(1),
|
372
|
+
from: @device_id
|
365
373
|
).publish
|
366
374
|
end
|
367
375
|
|
@@ -424,7 +432,8 @@ class SmartDoorLock
|
|
424
432
|
value: @locked ? 'locked' : 'unlocked',
|
425
433
|
unit: 'string',
|
426
434
|
timestamp: Time.now.iso8601,
|
427
|
-
battery_level: @battery_level.round(1)
|
435
|
+
battery_level: @battery_level.round(1),
|
436
|
+
from: @device_id
|
428
437
|
).publish
|
429
438
|
|
430
439
|
# Check for low battery
|
@@ -437,7 +446,8 @@ class SmartDoorLock
|
|
437
446
|
location: @location,
|
438
447
|
message: "Door lock battery low: #{@battery_level.round(1)}%",
|
439
448
|
timestamp: Time.now.iso8601,
|
440
|
-
requires_action: true
|
449
|
+
requires_action: true,
|
450
|
+
from: @device_id
|
441
451
|
).publish
|
442
452
|
end
|
443
453
|
|
@@ -528,7 +538,8 @@ class IoTDashboard
|
|
528
538
|
summary_stats: {
|
529
539
|
total_devices: @@devices.size,
|
530
540
|
avg_battery: @@devices.values.map { |d| d[:battery_level] || 100 }.sum / [@@devices.size, 1].max
|
531
|
-
}
|
541
|
+
},
|
542
|
+
from: 'IoTDashboard'
|
532
543
|
).publish
|
533
544
|
end
|
534
545
|
end
|
@@ -592,13 +603,16 @@ class SmartHomeDemo
|
|
592
603
|
puts "\n🎛️ Sending some manual commands..."
|
593
604
|
sleep(2)
|
594
605
|
|
595
|
-
DeviceCommandMessage.new(
|
606
|
+
cmd_msg = DeviceCommandMessage.new(
|
596
607
|
device_id: "THERM-001",
|
597
608
|
command: "set_temperature",
|
598
609
|
parameters: { target: 24.0 },
|
599
610
|
requested_by: "mobile_app",
|
600
|
-
timestamp: Time.now.iso8601
|
601
|
-
|
611
|
+
timestamp: Time.now.iso8601,
|
612
|
+
from: 'SmartHomeDemo'
|
613
|
+
)
|
614
|
+
puts "🎛️ Publishing command: set_temperature for THERM-001"
|
615
|
+
cmd_msg.publish
|
602
616
|
|
603
617
|
sleep(3)
|
604
618
|
|
@@ -607,7 +621,8 @@ class SmartHomeDemo
|
|
607
621
|
command: "start_recording",
|
608
622
|
parameters: { duration: 60 },
|
609
623
|
requested_by: "security_schedule",
|
610
|
-
timestamp: Time.now.iso8601
|
624
|
+
timestamp: Time.now.iso8601,
|
625
|
+
from: 'SmartHomeDemo'
|
611
626
|
).publish
|
612
627
|
|
613
628
|
sleep(2)
|
@@ -617,7 +632,8 @@ class SmartHomeDemo
|
|
617
632
|
command: "get_status",
|
618
633
|
parameters: {},
|
619
634
|
requested_by: "mobile_app",
|
620
|
-
timestamp: Time.now.iso8601
|
635
|
+
timestamp: Time.now.iso8601,
|
636
|
+
from: 'SmartHomeDemo'
|
621
637
|
).publish
|
622
638
|
|
623
639
|
puts "\n⏳ Monitoring for 30 seconds (watch the Redis channels!)..."
|
@@ -137,7 +137,7 @@ notifications = [
|
|
137
137
|
notifications.each_with_index do |notification_data, index|
|
138
138
|
puts "\n📤 Publishing notification #{index + 1}: #{notification_data[:title]}"
|
139
139
|
|
140
|
-
notification = NotificationMessage.new(**notification_data)
|
140
|
+
notification = NotificationMessage.new(**notification_data, from: 'ProcHandlerDemo')
|
141
141
|
notification.publish
|
142
142
|
|
143
143
|
# Give time for all handlers to process
|
@@ -161,7 +161,8 @@ error_notification = NotificationMessage.new(
|
|
161
161
|
title: 'Another Error',
|
162
162
|
message: 'This error won\'t trigger the block handler',
|
163
163
|
user_id: 'test_user',
|
164
|
-
timestamp: Time.now.iso8601
|
164
|
+
timestamp: Time.now.iso8601,
|
165
|
+
from: 'ProcHandlerDemo'
|
165
166
|
)
|
166
167
|
|
167
168
|
error_notification.publish
|
@@ -470,7 +470,7 @@ class PriorityOrderService
|
|
470
470
|
|
471
471
|
def process_priority_order(order_data)
|
472
472
|
# Create message with instance-level logger override
|
473
|
-
message = OrderProcessingMessage.new(**order_data)
|
473
|
+
message = OrderProcessingMessage.new(**order_data, from: 'PriorityOrderService')
|
474
474
|
|
475
475
|
# Override the logger at instance level
|
476
476
|
message.logger(@priority_logger)
|
@@ -506,7 +506,8 @@ class LoggerDemo
|
|
506
506
|
DefaultLoggerMessage.subscribe
|
507
507
|
default_msg = DefaultLoggerMessage.new(
|
508
508
|
message: "Testing the built-in default logger",
|
509
|
-
level: "info"
|
509
|
+
level: "info",
|
510
|
+
from: 'LoggerDemo'
|
510
511
|
)
|
511
512
|
default_msg.publish
|
512
513
|
sleep(0.5)
|
@@ -518,7 +519,8 @@ class LoggerDemo
|
|
518
519
|
customer_id: "CUST-123",
|
519
520
|
amount: 99.99,
|
520
521
|
status: "pending",
|
521
|
-
items: ["Widget A", "Widget B"]
|
522
|
+
items: ["Widget A", "Widget B"],
|
523
|
+
from: 'LoggerDemo'
|
522
524
|
)
|
523
525
|
order1.publish
|
524
526
|
sleep(0.5)
|
@@ -529,7 +531,8 @@ class LoggerDemo
|
|
529
531
|
recipient: "customer@example.com",
|
530
532
|
subject: "Order Confirmation",
|
531
533
|
body: "Your order has been received",
|
532
|
-
priority: "normal"
|
534
|
+
priority: "normal",
|
535
|
+
from: 'LoggerDemo'
|
533
536
|
)
|
534
537
|
notification.publish
|
535
538
|
sleep(0.5)
|
@@ -542,7 +545,8 @@ class LoggerDemo
|
|
542
545
|
customer_id: "VIP-456",
|
543
546
|
amount: 299.99,
|
544
547
|
status: "urgent",
|
545
|
-
items: ["Premium Widget", "Express Shipping"]
|
548
|
+
items: ["Premium Widget", "Express Shipping"],
|
549
|
+
from: 'PriorityOrderService'
|
546
550
|
)
|
547
551
|
sleep(0.5)
|
548
552
|
|
@@ -555,7 +559,8 @@ class LoggerDemo
|
|
555
559
|
# Create and send a message - watch for the Ruby logger output
|
556
560
|
msg = StandardLoggerMessage.new(
|
557
561
|
content: "Testing with Ruby's standard logger",
|
558
|
-
level: "info"
|
562
|
+
level: "info",
|
563
|
+
from: 'LoggerDemo'
|
559
564
|
)
|
560
565
|
|
561
566
|
# The logger will output to STDOUT using Ruby's standard format
|
@@ -573,7 +578,8 @@ class LoggerDemo
|
|
573
578
|
order_id: nil, # This might cause issues
|
574
579
|
customer_id: "ERROR-TEST",
|
575
580
|
amount: "invalid_amount",
|
576
|
-
status: "error_demo"
|
581
|
+
status: "error_demo",
|
582
|
+
from: 'LoggerDemo'
|
577
583
|
)
|
578
584
|
|
579
585
|
# Simulate an error during processing
|
@@ -167,7 +167,8 @@ class ErrorDemonstrator
|
|
167
167
|
missing_user_id = UserRegistrationMessage.new(
|
168
168
|
email: "john@example.com",
|
169
169
|
age: 25,
|
170
|
-
username: "johndoe"
|
170
|
+
username: "johndoe",
|
171
|
+
from: 'ErrorDemonstrator'
|
171
172
|
)
|
172
173
|
puts "❌ ERROR: Should have failed but didn't!"
|
173
174
|
rescue => e
|
@@ -181,7 +182,8 @@ class ErrorDemonstrator
|
|
181
182
|
begin
|
182
183
|
missing_multiple = UserRegistrationMessage.new(
|
183
184
|
user_id: "USER-123",
|
184
|
-
username: "johndoe"
|
185
|
+
username: "johndoe",
|
186
|
+
from: 'ErrorDemonstrator'
|
185
187
|
)
|
186
188
|
puts "❌ ERROR: Should have failed but didn't!"
|
187
189
|
rescue => e
|
@@ -197,7 +199,8 @@ class ErrorDemonstrator
|
|
197
199
|
user_id: "USER-123",
|
198
200
|
email: "john@example.com",
|
199
201
|
age: 25,
|
200
|
-
username: "johndoe"
|
202
|
+
username: "johndoe",
|
203
|
+
from: 'ErrorDemonstrator'
|
201
204
|
)
|
202
205
|
puts "✅ Message created successfully"
|
203
206
|
puts " User: #{valid_user.username} (#{valid_user.email})"
|
@@ -227,7 +230,8 @@ class ErrorDemonstrator
|
|
227
230
|
user_id: "USER-124",
|
228
231
|
email: "not-an-email", # Invalid format
|
229
232
|
age: 25,
|
230
|
-
username: "janedoe"
|
233
|
+
username: "janedoe",
|
234
|
+
from: 'ErrorDemonstrator'
|
231
235
|
)
|
232
236
|
puts " Message created, now validating..."
|
233
237
|
invalid_email.validate! # Explicitly trigger validation
|
@@ -245,7 +249,8 @@ class ErrorDemonstrator
|
|
245
249
|
user_id: "USER-125",
|
246
250
|
email: "kid@example.com",
|
247
251
|
age: 10, # Too young (< 13)
|
248
|
-
username: "kiduser"
|
252
|
+
username: "kiduser",
|
253
|
+
from: 'ErrorDemonstrator'
|
249
254
|
)
|
250
255
|
puts " Message created, now validating..."
|
251
256
|
invalid_age.validate! # Explicitly trigger validation
|
@@ -263,7 +268,8 @@ class ErrorDemonstrator
|
|
263
268
|
user_id: "USER-126",
|
264
269
|
email: "user@example.com",
|
265
270
|
age: 30,
|
266
|
-
username: "user@123!" # Contains invalid characters
|
271
|
+
username: "user@123!", # Contains invalid characters
|
272
|
+
from: 'ErrorDemonstrator'
|
267
273
|
)
|
268
274
|
puts " Message created, now validating..."
|
269
275
|
invalid_username.validate! # Explicitly trigger validation
|
@@ -282,7 +288,8 @@ class ErrorDemonstrator
|
|
282
288
|
email: "user@example.com",
|
283
289
|
age: 30,
|
284
290
|
username: "validuser",
|
285
|
-
subscription_type: "platinum" # Not in allowed list
|
291
|
+
subscription_type: "platinum", # Not in allowed list
|
292
|
+
from: 'ErrorDemonstrator'
|
286
293
|
)
|
287
294
|
puts " Message created, now validating..."
|
288
295
|
invalid_subscription.validate! # Explicitly trigger validation
|
@@ -301,7 +308,8 @@ class ErrorDemonstrator
|
|
301
308
|
email: "valid@example.com",
|
302
309
|
age: 25,
|
303
310
|
username: "validuser123",
|
304
|
-
subscription_type: "premium"
|
311
|
+
subscription_type: "premium",
|
312
|
+
from: 'ErrorDemonstrator'
|
305
313
|
)
|
306
314
|
puts " Message created, now validating..."
|
307
315
|
valid_user.validate! # Explicitly trigger validation
|
@@ -339,7 +347,8 @@ class ErrorDemonstrator
|
|
339
347
|
user_id: "USER-V1-001",
|
340
348
|
email: "v1user@example.com",
|
341
349
|
age: 28,
|
342
|
-
username: "v1user"
|
350
|
+
username: "v1user",
|
351
|
+
from: 'ErrorDemonstrator'
|
343
352
|
)
|
344
353
|
puts "✅ V1 Message created (version #{v1_message._sm_header.version})"
|
345
354
|
v1_message.publish
|
@@ -356,7 +365,8 @@ class ErrorDemonstrator
|
|
356
365
|
email: "v2user@example.com",
|
357
366
|
age: 32,
|
358
367
|
username: "v2user",
|
359
|
-
phone_number: "+1234567890"
|
368
|
+
phone_number: "+1234567890",
|
369
|
+
from: 'ErrorDemonstrator'
|
360
370
|
)
|
361
371
|
puts "✅ V2 Message created (version #{v2_message._sm_header.version})"
|
362
372
|
v2_message.publish
|
@@ -374,7 +384,8 @@ class ErrorDemonstrator
|
|
374
384
|
user_id: "USER-MISMATCH-001",
|
375
385
|
email: "mismatch@example.com",
|
376
386
|
age: 35,
|
377
|
-
username: "mismatchuser"
|
387
|
+
username: "mismatchuser",
|
388
|
+
from: 'ErrorDemonstrator'
|
378
389
|
)
|
379
390
|
|
380
391
|
puts "✅ Original message created with version #{version_mismatch_message._sm_header.version}"
|
@@ -396,8 +407,8 @@ class ErrorDemonstrator
|
|
396
407
|
|
397
408
|
# Test Case 4: Show version information
|
398
409
|
puts "📊 Test Case 3D: Version information display"
|
399
|
-
v1_msg = UserRegistrationMessage.new(user_id: "INFO-V1", email: "info@example.com", age: 25, username: "infouser")
|
400
|
-
v2_msg = UserRegistrationMessageV2.new(user_id: "INFO-V2", email: "info@example.com", age: 25, username: "infouser", phone_number: "+1234567890")
|
410
|
+
v1_msg = UserRegistrationMessage.new(user_id: "INFO-V1", email: "info@example.com", age: 25, username: "infouser", from: 'ErrorDemonstrator')
|
411
|
+
v2_msg = UserRegistrationMessageV2.new(user_id: "INFO-V2", email: "info@example.com", age: 25, username: "infouser", phone_number: "+1234567890", from: 'ErrorDemonstrator')
|
401
412
|
|
402
413
|
puts "📋 Version Information:"
|
403
414
|
puts " UserRegistrationMessage (V1):"
|
@@ -424,7 +435,7 @@ class ErrorDemonstrator
|
|
424
435
|
puts "Expected: Ideally should report all missing fields"
|
425
436
|
puts "Actual Hashie::Dash behavior:"
|
426
437
|
begin
|
427
|
-
message = MultiRequiredMessage.new(optional_field: "present")
|
438
|
+
message = MultiRequiredMessage.new(optional_field: "present", from: 'ErrorDemonstrator')
|
428
439
|
puts "❌ ERROR: Should have failed but didn't!"
|
429
440
|
rescue => e
|
430
441
|
puts "✅ Error caught: #{e.class.name}"
|
@@ -443,7 +454,7 @@ class ErrorDemonstrator
|
|
443
454
|
test_data.each_with_index do |test_case, index|
|
444
455
|
puts " #{index + 1}. #{test_case[:name]}:"
|
445
456
|
begin
|
446
|
-
message = MultiRequiredMessage.new(test_case[:data])
|
457
|
+
message = MultiRequiredMessage.new(test_case[:data].merge(from: 'ErrorDemonstrator'))
|
447
458
|
puts " ✅ Success: Message created"
|
448
459
|
rescue => e
|
449
460
|
field_name = e.message.match(/property '([^']+)'/)[1] rescue 'unknown'
|
@@ -62,7 +62,8 @@ order = OrderMessage.new(
|
|
62
62
|
order_id: "ORD-2024-001",
|
63
63
|
customer_id: "CUST-12345",
|
64
64
|
items: ["Widget A", "Widget B", "Gadget C"],
|
65
|
-
total_amount: 299.99
|
65
|
+
total_amount: 299.99,
|
66
|
+
from: 'order-service'
|
66
67
|
)
|
67
68
|
|
68
69
|
puts "\n📤 Publishing point-to-point order message..."
|
@@ -111,7 +112,8 @@ SystemAnnouncementMessage.subscribe
|
|
111
112
|
announcement = SystemAnnouncementMessage.new(
|
112
113
|
message: "System maintenance scheduled for tonight at 2:00 AM EST",
|
113
114
|
priority: 'high',
|
114
|
-
effective_time: '2024-12-20 02:00:00 EST'
|
115
|
+
effective_time: '2024-12-20 02:00:00 EST',
|
116
|
+
from: 'admin-service'
|
115
117
|
)
|
116
118
|
|
117
119
|
puts "\n📤 Publishing broadcast announcement..."
|
@@ -192,7 +194,8 @@ UserLookupResponse.subscribe
|
|
192
194
|
request = UserLookupRequest.new(
|
193
195
|
user_id: "USER-789",
|
194
196
|
request_id: SecureRandom.uuid,
|
195
|
-
requested_fields: ['name', 'email', 'last_login']
|
197
|
+
requested_fields: ['name', 'email', 'last_login'],
|
198
|
+
from: 'web-service'
|
196
199
|
)
|
197
200
|
|
198
201
|
puts "\n📤 Publishing user lookup request..."
|
@@ -208,7 +211,8 @@ response = UserLookupResponse.new(
|
|
208
211
|
email: "alice@example.com",
|
209
212
|
last_login: "2024-12-19 14:30:00"
|
210
213
|
},
|
211
|
-
success: true
|
214
|
+
success: true,
|
215
|
+
from: 'user-service'
|
212
216
|
)
|
213
217
|
response.to('web-service') # Set reply destination
|
214
218
|
|
@@ -261,7 +265,8 @@ normal_payment = PaymentMessage.new(
|
|
261
265
|
payment_id: "PAY-001",
|
262
266
|
amount: 150.00,
|
263
267
|
account_id: "ACCT-12345",
|
264
|
-
payment_method: 'credit_card'
|
268
|
+
payment_method: 'credit_card',
|
269
|
+
from: 'payment-service'
|
265
270
|
)
|
266
271
|
|
267
272
|
puts "\n📤 Publishing normal payment (using class defaults)..."
|
@@ -275,7 +280,8 @@ backup_payment = PaymentMessage.new(
|
|
275
280
|
payment_id: "PAY-002",
|
276
281
|
amount: 75.50,
|
277
282
|
account_id: "ACCT-67890",
|
278
|
-
payment_method: 'debit_card'
|
283
|
+
payment_method: 'debit_card',
|
284
|
+
from: 'payment-service'
|
279
285
|
)
|
280
286
|
|
281
287
|
# Override instance addressing
|
@@ -338,7 +344,8 @@ external_message = ExternalAPIMessage.new(
|
|
338
344
|
api_call: "PUT /api/v1/users/USER-123",
|
339
345
|
payload_data: internal_data,
|
340
346
|
authentication_token: "Bearer abc123xyz789",
|
341
|
-
partner_id: "PARTNER-ALPHA"
|
347
|
+
partner_id: "PARTNER-ALPHA",
|
348
|
+
from: 'api-gateway'
|
342
349
|
)
|
343
350
|
|
344
351
|
# Gateway can override destination based on routing rules
|