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.
- checksums.yaml +4 -4
- data/.github/workflows/deploy-github-pages.yml +38 -0
- data/.gitignore +5 -0
- data/CHANGELOG.md +64 -0
- data/Gemfile.lock +35 -4
- data/README.md +169 -71
- data/Rakefile +29 -4
- data/docs/assets/images/ddq_architecture.svg +130 -0
- data/docs/assets/images/dlq_architecture.svg +115 -0
- data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
- data/docs/assets/images/enhanced-fluent-api.svg +149 -0
- data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
- data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
- data/docs/assets/images/fluent-api-demo.svg +59 -0
- data/docs/assets/images/performance-comparison.svg +161 -0
- data/docs/assets/images/redis-basic-architecture.svg +53 -0
- data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
- data/docs/assets/images/redis-queue-architecture.svg +101 -0
- data/docs/assets/images/smart_message.jpg +0 -0
- data/docs/assets/images/smart_message_walking.jpg +0 -0
- data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
- data/docs/assets/images/transport-comparison-matrix.svg +171 -0
- data/docs/assets/javascripts/mathjax.js +17 -0
- data/docs/assets/stylesheets/extra.css +51 -0
- data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
- data/docs/{architecture.md → core-concepts/architecture.md} +80 -145
- data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
- data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
- data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
- data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
- data/docs/{examples.md → getting-started/examples.md} +103 -89
- data/docs/{getting-started.md → getting-started/quick-start.md} +47 -23
- data/docs/index.md +64 -0
- data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
- data/docs/{logging.md → reference/logging.md} +1 -1
- data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
- data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
- data/docs/reference/serializers.md +245 -0
- data/docs/{transports.md → reference/transports.md} +9 -11
- data/docs/transports/memory-transport.md +374 -0
- data/docs/transports/redis-transport-comparison.md +361 -0
- data/docs/transports/redis-transport.md +490 -0
- data/examples/README.md +104 -14
- data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
- data/examples/city_scenario/README.md +515 -0
- data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
- data/examples/city_scenario/citizen.rb +195 -0
- data/examples/city_scenario/city_diagram.svg +125 -0
- data/examples/city_scenario/common/health_monitor.rb +80 -0
- data/examples/city_scenario/common/logger.rb +30 -0
- data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
- data/examples/city_scenario/fire_department.rb +446 -0
- data/examples/city_scenario/fire_emergency_flow.svg +95 -0
- data/examples/city_scenario/health_department.rb +100 -0
- data/examples/city_scenario/health_monitoring_system.svg +130 -0
- data/examples/city_scenario/house.rb +244 -0
- data/examples/city_scenario/local_bank.rb +217 -0
- data/examples/city_scenario/messages/emergency_911_message.rb +80 -0
- data/examples/city_scenario/messages/emergency_resolved_message.rb +42 -0
- data/examples/city_scenario/messages/fire_dispatch_message.rb +42 -0
- data/examples/city_scenario/messages/fire_emergency_message.rb +44 -0
- data/examples/city_scenario/messages/health_check_message.rb +21 -0
- data/examples/city_scenario/messages/health_status_message.rb +34 -0
- data/examples/city_scenario/messages/police_dispatch_message.rb +45 -0
- data/examples/city_scenario/messages/silent_alarm_message.rb +37 -0
- data/examples/city_scenario/police_department.rb +316 -0
- data/examples/city_scenario/redis_monitor.rb +129 -0
- data/examples/city_scenario/redis_stats.rb +743 -0
- data/examples/city_scenario/room_for_improvement.md +240 -0
- data/examples/city_scenario/security_emergency_flow.svg +95 -0
- data/examples/city_scenario/service_internal_architecture.svg +154 -0
- data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
- data/examples/city_scenario/start_demo.sh +236 -0
- data/examples/city_scenario/stop_demo.sh +106 -0
- data/examples/city_scenario/visitor.rb +631 -0
- data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -3
- data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +10 -40
- data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -3
- data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +1 -2
- data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +1 -4
- data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
- data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +1 -2
- data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +13 -14
- data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +1 -4
- data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +2 -8
- data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +2 -6
- data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +1 -2
- data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +1 -6
- data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +17 -8
- data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -2
- data/examples/memory/README.md +163 -0
- data/examples/memory/memory_transport_architecture.svg +90 -0
- data/examples/memory/point_to_point_pattern.svg +94 -0
- data/examples/memory/publish_subscribe_pattern.svg +125 -0
- data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +1 -5
- data/examples/redis/README.md +228 -0
- data/examples/redis/alert_system_flow.svg +127 -0
- data/examples/redis/dashboard_status_flow.svg +107 -0
- data/examples/redis/device_command_flow.svg +113 -0
- data/examples/redis/redis_transport_architecture.svg +115 -0
- data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
- data/examples/redis/smart_home_system_architecture.svg +133 -0
- data/ideas/README.md +41 -0
- data/ideas/agents.md +1001 -0
- data/ideas/database_transport.md +980 -0
- data/ideas/improvement.md +359 -0
- data/ideas/meshage.md +1788 -0
- data/ideas/message_discovery.md +178 -0
- data/ideas/message_schema.md +1381 -0
- data/lib/smart_message/.idea/.gitignore +8 -0
- data/lib/smart_message/.idea/markdown.xml +6 -0
- data/lib/smart_message/.idea/misc.xml +4 -0
- data/lib/smart_message/.idea/modules.xml +8 -0
- data/lib/smart_message/.idea/smart_message.iml +16 -0
- data/lib/smart_message/.idea/vcs.xml +6 -0
- data/lib/smart_message/addressing.rb +15 -0
- data/lib/smart_message/base.rb +19 -12
- data/lib/smart_message/configuration.rb +2 -23
- data/lib/smart_message/dead_letter_queue.rb +1 -1
- data/lib/smart_message/logger.rb +15 -4
- data/lib/smart_message/messaging.rb +3 -62
- data/lib/smart_message/plugins.rb +6 -44
- data/lib/smart_message/serializer.rb +14 -0
- data/lib/smart_message/transport/base.rb +42 -8
- data/lib/smart_message/transport/memory_transport.rb +23 -4
- data/lib/smart_message/transport/redis_transport.rb +11 -0
- data/lib/smart_message/transport/stdout_transport.rb +28 -10
- data/lib/smart_message/transport.rb +33 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +5 -52
- data/mkdocs.yml +184 -0
- data/p2p_plan.md +326 -0
- data/p2p_roadmap.md +287 -0
- data/smart_message.gemspec +2 -0
- data/smart_message.svg +51 -0
- metadata +145 -45
- data/docs/README.md +0 -57
- data/docs/serializers.md +0 -575
- data/examples/dead_letters.jsonl +0 -12
- data/examples/temp.txt +0 -94
- data/examples/tmux_chat/README.md +0 -283
- data/examples/tmux_chat/bot_agent.rb +0 -278
- data/examples/tmux_chat/human_agent.rb +0 -199
- data/examples/tmux_chat/room_monitor.rb +0 -160
- data/examples/tmux_chat/shared_chat_system.rb +0 -328
- data/examples/tmux_chat/start_chat_demo.sh +0 -190
- data/examples/tmux_chat/stop_chat_demo.sh +0 -22
- /data/docs/{properties.md → core-concepts/properties.md} +0 -0
- /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -0,0 +1,245 @@
|
|
1
|
+
# Transport-Based Serialization
|
2
|
+
|
3
|
+
In SmartMessage's architecture, serialization is handled at the transport level rather than being configured for individual messages. Each transport manages its own optimal serialization format, eliminating the need for separate serializer configuration.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
Transport-based serialization provides:
|
8
|
+
- **Automatic Format Selection**: Each transport chooses its optimal serialization format
|
9
|
+
- **Simplified Configuration**: No need to configure serializers separately
|
10
|
+
- **Format Optimization**: Transports can choose the best format for their medium
|
11
|
+
- **Consistent Behavior**: All messages using a transport share the same serialization format
|
12
|
+
|
13
|
+
## Transport Serialization Formats
|
14
|
+
|
15
|
+
### Memory Transport
|
16
|
+
- **Format**: No serialization (objects passed directly)
|
17
|
+
- **Use case**: Testing and development where no network transmission occurs
|
18
|
+
- **Performance**: Fastest possible (no encoding/decoding overhead)
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
# Memory transport - no serialization needed
|
22
|
+
transport = SmartMessage::Transport::MemoryTransport.new
|
23
|
+
```
|
24
|
+
|
25
|
+
### STDOUT Transport
|
26
|
+
- **Format**: JSON (human-readable)
|
27
|
+
- **Use case**: Debugging, development logging, message inspection
|
28
|
+
- **Features**: Pretty-printed output for easy reading
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
# STDOUT transport - uses JSON for readability
|
32
|
+
transport = SmartMessage::Transport::StdoutTransport.new(
|
33
|
+
format: :pretty # or :json for compact format
|
34
|
+
)
|
35
|
+
```
|
36
|
+
|
37
|
+
### Redis Transport
|
38
|
+
- **Format**: MessagePack (primary), JSON (fallback)
|
39
|
+
- **Use case**: Production messaging where efficiency matters
|
40
|
+
- **Benefits**: Compact binary format reduces network overhead
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
# Redis transport - automatically uses MessagePack if available
|
44
|
+
transport = SmartMessage::Transport::RedisTransport.new(
|
45
|
+
url: 'redis://localhost:6379'
|
46
|
+
)
|
47
|
+
```
|
48
|
+
|
49
|
+
## How It Works
|
50
|
+
|
51
|
+
### Transport Serialization Process
|
52
|
+
|
53
|
+
1. **Message Publishing**:
|
54
|
+
```ruby
|
55
|
+
message = OrderMessage.new(order_id: "123", amount: 99.99)
|
56
|
+
message.publish # Transport handles serialization automatically
|
57
|
+
```
|
58
|
+
|
59
|
+
2. **Automatic Encoding**: Transport calls its serializer internally
|
60
|
+
```ruby
|
61
|
+
# Inside transport.publish(message):
|
62
|
+
serialized = transport.serializer.encode(message.to_hash)
|
63
|
+
```
|
64
|
+
|
65
|
+
3. **Message Receiving**: Transport deserializes automatically
|
66
|
+
```ruby
|
67
|
+
# Inside transport.receive(serialized_data):
|
68
|
+
data = transport.serializer.decode(serialized_data)
|
69
|
+
message = MessageClass.new(data)
|
70
|
+
```
|
71
|
+
|
72
|
+
### Message Structure
|
73
|
+
|
74
|
+
All messages are serialized as flat hashes with the `_sm_header` property containing routing metadata:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
{
|
78
|
+
_sm_header: {
|
79
|
+
uuid: "...",
|
80
|
+
message_class: "OrderMessage",
|
81
|
+
published_at: "2025-01-09T...",
|
82
|
+
from: "order-service",
|
83
|
+
to: "fulfillment-service",
|
84
|
+
serializer: "SmartMessage::Serializer::Json"
|
85
|
+
},
|
86
|
+
order_id: "123",
|
87
|
+
amount: 99.99,
|
88
|
+
items: ["Widget A", "Widget B"]
|
89
|
+
}
|
90
|
+
```
|
91
|
+
|
92
|
+
## Custom Transport Serializers
|
93
|
+
|
94
|
+
You can specify a custom serializer when creating a transport:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
# Custom serializer for a transport
|
98
|
+
class MyCustomSerializer
|
99
|
+
def encode(data_hash)
|
100
|
+
# Your encoding logic here
|
101
|
+
# Must return a string
|
102
|
+
end
|
103
|
+
|
104
|
+
def decode(serialized_string)
|
105
|
+
# Your decoding logic here
|
106
|
+
# Must return a hash
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Use custom serializer with transport
|
111
|
+
transport = SmartMessage::Transport::RedisTransport.new(
|
112
|
+
serializer: MyCustomSerializer.new,
|
113
|
+
url: 'redis://localhost:6379'
|
114
|
+
)
|
115
|
+
```
|
116
|
+
|
117
|
+
## Built-in Serializer Classes
|
118
|
+
|
119
|
+
SmartMessage includes these serializer implementations that transports use internally:
|
120
|
+
|
121
|
+
### JSON Serializer
|
122
|
+
```ruby
|
123
|
+
SmartMessage::Serializer::Json.new
|
124
|
+
```
|
125
|
+
- Human-readable format
|
126
|
+
- Wide compatibility
|
127
|
+
- Used by STDOUT transport and as fallback
|
128
|
+
|
129
|
+
### MessagePack Serializer
|
130
|
+
```ruby
|
131
|
+
SmartMessage::Serializer::MessagePack.new
|
132
|
+
```
|
133
|
+
- Binary format for efficiency
|
134
|
+
- Smaller payload size
|
135
|
+
- Used by Redis transport when available
|
136
|
+
|
137
|
+
## Migration from Message-Level Serializers
|
138
|
+
|
139
|
+
If you were previously configuring serializers at the message level, here's how to migrate:
|
140
|
+
|
141
|
+
### Before (Message-Level Configuration)
|
142
|
+
```ruby
|
143
|
+
class OrderMessage < SmartMessage::Base
|
144
|
+
property :order_id
|
145
|
+
property :amount
|
146
|
+
|
147
|
+
config do
|
148
|
+
transport SmartMessage::Transport::RedisTransport.new
|
149
|
+
serializer SmartMessage::Serializer::Json.new # ❌ No longer needed
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
### After (Transport-Level Serialization)
|
155
|
+
```ruby
|
156
|
+
class OrderMessage < SmartMessage::Base
|
157
|
+
property :order_id
|
158
|
+
property :amount
|
159
|
+
|
160
|
+
config do
|
161
|
+
# Transport automatically handles serialization
|
162
|
+
transport SmartMessage::Transport::RedisTransport.new
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Or specify custom serializer for transport
|
167
|
+
class OrderMessage < SmartMessage::Base
|
168
|
+
property :order_id
|
169
|
+
property :amount
|
170
|
+
|
171
|
+
config do
|
172
|
+
transport SmartMessage::Transport::RedisTransport.new(
|
173
|
+
serializer: MyCustomSerializer.new
|
174
|
+
)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
## Serialization Best Practices
|
180
|
+
|
181
|
+
### 1. Let Transports Choose
|
182
|
+
Let each transport use its optimal format:
|
183
|
+
- Memory: No serialization
|
184
|
+
- STDOUT: JSON for readability
|
185
|
+
- Redis: MessagePack for efficiency
|
186
|
+
|
187
|
+
### 2. Custom Serializers
|
188
|
+
Only use custom serializers when you have specific requirements:
|
189
|
+
- Special data formats (XML, Protocol Buffers)
|
190
|
+
- Encryption/compression needs
|
191
|
+
- Legacy system compatibility
|
192
|
+
|
193
|
+
### 3. Testing
|
194
|
+
Test with actual transports to ensure serialization works correctly:
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
RSpec.describe OrderMessage do
|
198
|
+
it "serializes correctly with Redis transport" do
|
199
|
+
transport = SmartMessage::Transport::RedisTransport.new
|
200
|
+
message = OrderMessage.new(order_id: "123", amount: 99.99)
|
201
|
+
|
202
|
+
# Test roundtrip serialization
|
203
|
+
serialized = transport.encode_message(message)
|
204
|
+
deserialized = transport.decode_message(serialized)
|
205
|
+
|
206
|
+
expect(deserialized[:order_id]).to eq("123")
|
207
|
+
expect(deserialized[:amount]).to eq(99.99)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
### 4. Error Handling
|
213
|
+
Transports handle serialization errors internally, but you can still catch them:
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
begin
|
217
|
+
message.publish
|
218
|
+
rescue SmartMessage::Errors::SerializationError => e
|
219
|
+
logger.error "Failed to serialize message: #{e.message}"
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
223
|
+
## Performance Considerations
|
224
|
+
|
225
|
+
### Format Efficiency
|
226
|
+
- **MessagePack**: 20-30% more compact than JSON
|
227
|
+
- **JSON**: Human-readable but larger payload
|
228
|
+
- **Memory**: No serialization overhead
|
229
|
+
|
230
|
+
### Network Optimization
|
231
|
+
- Redis transport automatically uses MessagePack when available
|
232
|
+
- Falls back to JSON if MessagePack gem is not installed
|
233
|
+
- STDOUT uses JSON for debugging clarity
|
234
|
+
|
235
|
+
### Monitoring
|
236
|
+
Each transport logs its serializer choice:
|
237
|
+
```
|
238
|
+
[SmartMessage::Transport::RedisTransport] Using serializer: SmartMessage::Serializer::MessagePack
|
239
|
+
```
|
240
|
+
|
241
|
+
## Next Steps
|
242
|
+
|
243
|
+
- [Transports](transports.md) - Available transport implementations
|
244
|
+
- [Configuration](../getting-started/quick-start.md) - Setting up transports
|
245
|
+
- [Examples](../getting-started/examples.md) - Real-world usage patterns
|
@@ -137,6 +137,8 @@ Production-ready Redis pub/sub transport for distributed messaging.
|
|
137
137
|
- Configurable connection parameters
|
138
138
|
- Background message subscription threads
|
139
139
|
|
140
|
+
> **💡 Redis Transport:** SmartMessage provides a Redis-based transport for production messaging using pub/sub channels. See the [Redis Transport documentation](../transports/redis-transport.md) for detailed usage guidance.
|
141
|
+
|
140
142
|
**Usage:**
|
141
143
|
|
142
144
|
```ruby
|
@@ -166,12 +168,11 @@ class OrderMessage < SmartMessage::Base
|
|
166
168
|
url: 'redis://localhost:6379',
|
167
169
|
db: 1
|
168
170
|
)
|
169
|
-
serializer SmartMessage::Serializer::JSON.new
|
170
171
|
end
|
171
172
|
|
172
|
-
def self.process(
|
173
|
-
|
174
|
-
order =
|
173
|
+
def self.process(decoded_message)
|
174
|
+
# decoded_message is already a message instance
|
175
|
+
order = decoded_message
|
175
176
|
puts "Processing order #{order.order_id} for $#{order.amount}"
|
176
177
|
# Your business logic here
|
177
178
|
end
|
@@ -243,7 +244,6 @@ redis_transport = SmartMessage::Transport.create(:redis,
|
|
243
244
|
[OrderMessage, PaymentMessage, ShippingMessage].each do |msg_class|
|
244
245
|
msg_class.config do
|
245
246
|
transport redis_transport
|
246
|
-
serializer SmartMessage::Serializer::JSON.new
|
247
247
|
end
|
248
248
|
|
249
249
|
# Subscribe to each message type (creates separate Redis subscriptions)
|
@@ -285,7 +285,6 @@ class ProductionMessage < SmartMessage::Base
|
|
285
285
|
reconnect_attempts: 10,
|
286
286
|
reconnect_delay: 5
|
287
287
|
)
|
288
|
-
serializer SmartMessage::Serializer::JSON.new
|
289
288
|
logger Logger.new(STDOUT)
|
290
289
|
end
|
291
290
|
end
|
@@ -302,7 +301,6 @@ class TestMessage < SmartMessage::Base
|
|
302
301
|
db: 15, # Use separate database for tests
|
303
302
|
auto_subscribe: true
|
304
303
|
)
|
305
|
-
serializer SmartMessage::Serializer::JSON.new
|
306
304
|
end
|
307
305
|
end
|
308
306
|
|
@@ -313,6 +311,7 @@ def setup
|
|
313
311
|
end
|
314
312
|
```
|
315
313
|
|
314
|
+
|
316
315
|
## Transport Interface
|
317
316
|
|
318
317
|
All transports must implement the `SmartMessage::Transport::Base` interface:
|
@@ -411,7 +410,6 @@ class OrderMessage < SmartMessage::Base
|
|
411
410
|
# All instances use this transport by default
|
412
411
|
config do
|
413
412
|
transport SmartMessage::Transport.create(:memory, auto_process: true)
|
414
|
-
serializer SmartMessage::Serializer::JSON.new
|
415
413
|
end
|
416
414
|
end
|
417
415
|
```
|
@@ -689,7 +687,7 @@ end
|
|
689
687
|
|
690
688
|
## Next Steps
|
691
689
|
|
692
|
-
- [
|
690
|
+
- [Redis Transport Comparison](../transports/redis-transport-comparison.md) - Detailed comparison of Redis, Enhanced, and Queue transports
|
693
691
|
- [Serializers](serializers.md) - Understanding message serialization
|
694
|
-
- [Dispatcher](dispatcher.md) - Message routing and processing
|
695
|
-
- [Examples](examples.md) - Real-world transport usage patterns
|
692
|
+
- [Dispatcher](../core-concepts/dispatcher.md) - Message routing and processing
|
693
|
+
- [Examples](../getting-started/examples.md) - Real-world transport usage patterns
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# Memory Transport
|
2
|
+
|
3
|
+
The **Memory Transport** is an in-memory transport implementation designed for testing, development, and rapid prototyping. It stores messages in memory and provides synchronous processing capabilities.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The Memory Transport is perfect for:
|
8
|
+
- **Unit testing** - No external dependencies required
|
9
|
+
- **Local development** - Fast, lightweight message processing
|
10
|
+
- **Rapid prototyping** - Quick setup without infrastructure
|
11
|
+
- **Debug and inspection** - Full visibility into message flow
|
12
|
+
|
13
|
+
## Key Features
|
14
|
+
|
15
|
+
- 🧠 **In-Memory Storage** - Messages stored in process memory
|
16
|
+
- ⚡ **Synchronous Processing** - Immediate message processing
|
17
|
+
- 🔍 **Message Inspection** - View and count stored messages
|
18
|
+
- 🔄 **Auto-Processing** - Optional automatic message processing
|
19
|
+
- 🛡️ **Memory Protection** - Configurable message limits to prevent overflow
|
20
|
+
- 🧵 **Thread-Safe** - Mutex-protected operations
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
|
24
|
+
### Basic Setup
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
# Minimal configuration
|
28
|
+
transport = SmartMessage::Transport::MemoryTransport.new
|
29
|
+
|
30
|
+
# With options
|
31
|
+
transport = SmartMessage::Transport::MemoryTransport.new(
|
32
|
+
auto_process: true, # Process messages immediately (default: true)
|
33
|
+
max_messages: 1000 # Maximum messages to store (default: 1000)
|
34
|
+
)
|
35
|
+
```
|
36
|
+
|
37
|
+
### Using with SmartMessage
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Configure as default transport
|
41
|
+
SmartMessage.configure do |config|
|
42
|
+
config.default_transport = SmartMessage::Transport::MemoryTransport.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Use in message class
|
46
|
+
class TestMessage < SmartMessage::Base
|
47
|
+
property :content, required: true
|
48
|
+
|
49
|
+
transport :memory
|
50
|
+
|
51
|
+
def process
|
52
|
+
puts "Processing: #{content}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
## Configuration Options
|
58
|
+
|
59
|
+
| Option | Type | Default | Description |
|
60
|
+
|--------|------|---------|-------------|
|
61
|
+
| `auto_process` | Boolean | `true` | Automatically process messages when published |
|
62
|
+
| `max_messages` | Integer | `1000` | Maximum messages to store (prevents memory overflow) |
|
63
|
+
|
64
|
+
## Usage Examples
|
65
|
+
|
66
|
+
### Basic Message Processing
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
# Create transport
|
70
|
+
transport = SmartMessage::Transport::MemoryTransport.new
|
71
|
+
|
72
|
+
# Define message
|
73
|
+
class AlertMessage < SmartMessage::Base
|
74
|
+
property :message, required: true
|
75
|
+
property :severity, default: 'info'
|
76
|
+
|
77
|
+
transport transport
|
78
|
+
|
79
|
+
def process
|
80
|
+
puts "[#{severity.upcase}] #{message}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Publish message
|
85
|
+
AlertMessage.new(
|
86
|
+
message: "System startup complete",
|
87
|
+
severity: "info"
|
88
|
+
).publish
|
89
|
+
|
90
|
+
# Output: [INFO] System startup complete
|
91
|
+
```
|
92
|
+
|
93
|
+
### Manual Processing Control
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# Disable auto-processing for batch operations
|
97
|
+
transport = SmartMessage::Transport::MemoryTransport.new(auto_process: false)
|
98
|
+
|
99
|
+
class DataMessage < SmartMessage::Base
|
100
|
+
property :data
|
101
|
+
transport transport
|
102
|
+
|
103
|
+
def process
|
104
|
+
puts "Processing: #{data}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Publish multiple messages
|
109
|
+
DataMessage.new(data: "batch 1").publish
|
110
|
+
DataMessage.new(data: "batch 2").publish
|
111
|
+
DataMessage.new(data: "batch 3").publish
|
112
|
+
|
113
|
+
puts "Messages stored: #{transport.message_count}"
|
114
|
+
# Output: Messages stored: 3
|
115
|
+
|
116
|
+
# Process all at once
|
117
|
+
transport.process_all
|
118
|
+
# Output:
|
119
|
+
# Processing: batch 1
|
120
|
+
# Processing: batch 2
|
121
|
+
# Processing: batch 3
|
122
|
+
```
|
123
|
+
|
124
|
+
### Message Inspection
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
transport = SmartMessage::Transport::MemoryTransport.new(auto_process: false)
|
128
|
+
|
129
|
+
class OrderMessage < SmartMessage::Base
|
130
|
+
property :order_id, required: true
|
131
|
+
property :amount, required: true
|
132
|
+
transport transport
|
133
|
+
end
|
134
|
+
|
135
|
+
# Publish test messages
|
136
|
+
OrderMessage.new(order_id: "ORD-001", amount: 99.99).publish
|
137
|
+
OrderMessage.new(order_id: "ORD-002", amount: 149.50).publish
|
138
|
+
|
139
|
+
# Inspect stored messages
|
140
|
+
puts "Total messages: #{transport.message_count}"
|
141
|
+
transport.all_messages.each_with_index do |msg, index|
|
142
|
+
puts "Message #{index + 1}: #{msg[:message_class]} at #{msg[:published_at]}"
|
143
|
+
end
|
144
|
+
|
145
|
+
# Clear messages when done
|
146
|
+
transport.clear_messages
|
147
|
+
puts "Messages after clear: #{transport.message_count}"
|
148
|
+
```
|
149
|
+
|
150
|
+
## API Reference
|
151
|
+
|
152
|
+
### Instance Methods
|
153
|
+
|
154
|
+
#### `#message_count`
|
155
|
+
Returns the number of messages currently stored.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
count = transport.message_count
|
159
|
+
puts "Stored messages: #{count}"
|
160
|
+
```
|
161
|
+
|
162
|
+
#### `#all_messages`
|
163
|
+
Returns a copy of all stored messages with metadata.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
messages = transport.all_messages
|
167
|
+
messages.each do |msg|
|
168
|
+
puts "Class: #{msg[:message_class]}"
|
169
|
+
puts "Time: #{msg[:published_at]}"
|
170
|
+
puts "Data: #{msg[:serialized_message]}"
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
#### `#clear_messages`
|
175
|
+
Removes all stored messages from memory.
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
transport.clear_messages
|
179
|
+
```
|
180
|
+
|
181
|
+
#### `#process_all`
|
182
|
+
Manually processes all stored messages (useful when `auto_process: false`).
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# Publish messages without auto-processing
|
186
|
+
transport = SmartMessage::Transport::MemoryTransport.new(auto_process: false)
|
187
|
+
# ... publish messages ...
|
188
|
+
|
189
|
+
# Process them all at once
|
190
|
+
transport.process_all
|
191
|
+
```
|
192
|
+
|
193
|
+
#### `#connected?`
|
194
|
+
Always returns `true` since memory transport is always available.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
puts transport.connected? # => true
|
198
|
+
```
|
199
|
+
|
200
|
+
## Use Cases
|
201
|
+
|
202
|
+
### Unit Testing
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
RSpec.describe "Message Processing" do
|
206
|
+
let(:transport) { SmartMessage::Transport::MemoryTransport.new }
|
207
|
+
|
208
|
+
before do
|
209
|
+
MyMessage.transport = transport
|
210
|
+
transport.clear_messages
|
211
|
+
end
|
212
|
+
|
213
|
+
it "processes messages correctly" do
|
214
|
+
MyMessage.new(data: "test").publish
|
215
|
+
expect(transport.message_count).to eq(1)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "respects message limits" do
|
219
|
+
transport = SmartMessage::Transport::MemoryTransport.new(max_messages: 2)
|
220
|
+
|
221
|
+
3.times { |i| MyMessage.new(data: i).publish }
|
222
|
+
expect(transport.message_count).to eq(2) # Oldest message dropped
|
223
|
+
end
|
224
|
+
end
|
225
|
+
```
|
226
|
+
|
227
|
+
### Development Environment
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
# config/environments/development.rb
|
231
|
+
SmartMessage.configure do |config|
|
232
|
+
config.default_transport = SmartMessage::Transport::MemoryTransport.new(
|
233
|
+
auto_process: true,
|
234
|
+
max_messages: 500
|
235
|
+
)
|
236
|
+
config.logger.level = Logger::DEBUG # See all message activity
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
### Batch Processing
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
# Collect messages for batch processing
|
244
|
+
transport = SmartMessage::Transport::MemoryTransport.new(auto_process: false)
|
245
|
+
|
246
|
+
# Publish work items
|
247
|
+
work_items.each do |item|
|
248
|
+
WorkMessage.new(item: item).publish
|
249
|
+
end
|
250
|
+
|
251
|
+
# Process batch when ready
|
252
|
+
puts "Processing #{transport.message_count} work items..."
|
253
|
+
start_time = Time.now
|
254
|
+
transport.process_all
|
255
|
+
puts "Completed in #{Time.now - start_time} seconds"
|
256
|
+
```
|
257
|
+
|
258
|
+
## Performance Characteristics
|
259
|
+
|
260
|
+
- **Latency**: ~0.01ms (memory access)
|
261
|
+
- **Throughput**: 100K+ messages/second
|
262
|
+
- **Memory Usage**: ~1KB per stored message
|
263
|
+
- **Concurrency**: Thread-safe with mutex protection
|
264
|
+
- **Persistence**: None (messages lost when process ends)
|
265
|
+
|
266
|
+
## Best Practices
|
267
|
+
|
268
|
+
### Testing
|
269
|
+
- Use `clear_messages` in test setup/teardown
|
270
|
+
- Set reasonable `max_messages` limits for long-running tests
|
271
|
+
- Disable `auto_process` for message inspection tests
|
272
|
+
|
273
|
+
### Development
|
274
|
+
- Enable debug logging to see message flow
|
275
|
+
- Use message inspection methods for debugging
|
276
|
+
- Consider memory limits in long-running development processes
|
277
|
+
|
278
|
+
### Production
|
279
|
+
⚠️ **Not recommended for production use**
|
280
|
+
- Messages are lost when process restarts
|
281
|
+
- No persistence or durability guarantees
|
282
|
+
- Limited by process memory
|
283
|
+
|
284
|
+
## Thread Safety
|
285
|
+
|
286
|
+
The Memory Transport is fully thread-safe:
|
287
|
+
- All operations use mutex synchronization
|
288
|
+
- Messages can be published from multiple threads
|
289
|
+
- Inspection methods return safe copies
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
# Thread-safe concurrent publishing
|
293
|
+
threads = []
|
294
|
+
10.times do |i|
|
295
|
+
threads << Thread.new do
|
296
|
+
100.times { |j| TestMessage.new(data: "#{i}-#{j}").publish }
|
297
|
+
end
|
298
|
+
end
|
299
|
+
threads.each(&:join)
|
300
|
+
|
301
|
+
puts "Total messages: #{transport.message_count}" # Always accurate
|
302
|
+
```
|
303
|
+
|
304
|
+
## Migration from Memory Transport
|
305
|
+
|
306
|
+
When moving from Memory Transport to production transports:
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
# Development (Memory Transport)
|
310
|
+
SmartMessage.configure do |config|
|
311
|
+
config.default_transport = SmartMessage::Transport::MemoryTransport.new
|
312
|
+
end
|
313
|
+
|
314
|
+
# Production (Redis Transport)
|
315
|
+
SmartMessage.configure do |config|
|
316
|
+
config.default_transport = SmartMessage::Transport::RedisTransport.new(
|
317
|
+
url: ENV['REDIS_URL']
|
318
|
+
)
|
319
|
+
end
|
320
|
+
```
|
321
|
+
|
322
|
+
Messages and processing logic remain identical - only the transport configuration changes.
|
323
|
+
|
324
|
+
## Examples
|
325
|
+
|
326
|
+
The `examples/memory/` directory contains comprehensive, runnable examples demonstrating Memory Transport capabilities:
|
327
|
+
|
328
|
+
### Core Messaging Examples
|
329
|
+
- **[03_point_to_point_orders.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/03_point_to_point_orders.rb)** - Point-to-point order processing with payment integration
|
330
|
+
- **[04_publish_subscribe_events.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/04_publish_subscribe_events.rb)** - Event broadcasting to multiple services (email, SMS, audit)
|
331
|
+
- **[05_many_to_many_chat.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/05_many_to_many_chat.rb)** - Interactive chat system with rooms, bots, and human agents
|
332
|
+
|
333
|
+
### Advanced Features
|
334
|
+
- **[01_message_deduplication_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/01_message_deduplication_demo.rb)** - Message deduplication patterns and strategies
|
335
|
+
- **[02_dead_letter_queue_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/02_dead_letter_queue_demo.rb)** - Complete Dead Letter Queue system with circuit breakers
|
336
|
+
- **[07_proc_handlers_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/07_proc_handlers_demo.rb)** - Flexible message handlers (blocks, procs, lambdas, methods)
|
337
|
+
|
338
|
+
### Configuration & Monitoring
|
339
|
+
- **[08_custom_logger_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/08_custom_logger_demo.rb)** - Advanced logging with SmartMessage::Logger::Default
|
340
|
+
- **[09_error_handling_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/09_error_handling_demo.rb)** - Comprehensive validation, version mismatch, and error handling
|
341
|
+
- **[13_header_block_configuration.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/13_header_block_configuration.rb)** - Header and block configuration examples
|
342
|
+
- **[14_global_configuration_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/14_global_configuration_demo.rb)** - Global configuration management
|
343
|
+
- **[15_logger_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/15_logger_demo.rb)** - Advanced logging demonstrations
|
344
|
+
|
345
|
+
### Entity Addressing & Filtering
|
346
|
+
- **[10_entity_addressing_basic.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/10_entity_addressing_basic.rb)** - Basic FROM/TO/REPLY_TO message addressing
|
347
|
+
- **[11_entity_addressing_with_filtering.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/11_entity_addressing_with_filtering.rb)** - Advanced entity-aware message filtering
|
348
|
+
- **[12_regex_filtering_microservices.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/12_regex_filtering_microservices.rb)** - Advanced regex filtering for microservices
|
349
|
+
|
350
|
+
### Visual Demonstrations
|
351
|
+
- **[06_pretty_print_demo.rb](https://github.com/MadBomber/smart_message/blob/main/examples/memory/06_pretty_print_demo.rb)** - Message inspection and pretty-printing capabilities
|
352
|
+
|
353
|
+
### Running Examples
|
354
|
+
|
355
|
+
```bash
|
356
|
+
# Navigate to the SmartMessage directory
|
357
|
+
cd smart_message
|
358
|
+
|
359
|
+
# Run any Memory Transport example
|
360
|
+
ruby examples/memory/03_point_to_point_orders.rb
|
361
|
+
ruby examples/memory/05_many_to_many_chat.rb
|
362
|
+
ruby examples/memory/02_dead_letter_queue_demo.rb
|
363
|
+
|
364
|
+
# Or explore the entire directory
|
365
|
+
ls examples/memory/
|
366
|
+
```
|
367
|
+
|
368
|
+
Each example is self-contained and demonstrates specific Memory Transport features with clear educational comments and real-world scenarios.
|
369
|
+
|
370
|
+
## Related Documentation
|
371
|
+
|
372
|
+
- [Transport Overview](../reference/transports.md) - All available transports
|
373
|
+
- [Redis Transport](redis-transport.md) - Production-ready Redis transport
|
374
|
+
- [Testing Guide](../development/troubleshooting.md) - Testing strategies with SmartMessage
|