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.
- checksums.yaml +7 -0
- data/.envrc +3 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +100 -0
- data/COMMITS.md +196 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +71 -0
- data/README.md +303 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docs/README.md +52 -0
- data/docs/architecture.md +370 -0
- data/docs/dispatcher.md +593 -0
- data/docs/examples.md +808 -0
- data/docs/getting-started.md +235 -0
- data/docs/ideas_to_think_about.md +329 -0
- data/docs/serializers.md +575 -0
- data/docs/transports.md +501 -0
- data/docs/troubleshooting.md +582 -0
- data/examples/01_point_to_point_orders.rb +200 -0
- data/examples/02_publish_subscribe_events.rb +364 -0
- data/examples/03_many_to_many_chat.rb +608 -0
- data/examples/README.md +335 -0
- data/examples/tmux_chat/README.md +283 -0
- data/examples/tmux_chat/bot_agent.rb +272 -0
- data/examples/tmux_chat/human_agent.rb +197 -0
- data/examples/tmux_chat/room_monitor.rb +158 -0
- data/examples/tmux_chat/shared_chat_system.rb +295 -0
- data/examples/tmux_chat/start_chat_demo.sh +190 -0
- data/examples/tmux_chat/stop_chat_demo.sh +22 -0
- data/lib/simple_stats.rb +57 -0
- data/lib/smart_message/base.rb +284 -0
- data/lib/smart_message/dispatcher/.keep +0 -0
- data/lib/smart_message/dispatcher.rb +146 -0
- data/lib/smart_message/errors.rb +29 -0
- data/lib/smart_message/header.rb +20 -0
- data/lib/smart_message/logger/base.rb +8 -0
- data/lib/smart_message/logger.rb +7 -0
- data/lib/smart_message/serializer/base.rb +23 -0
- data/lib/smart_message/serializer/json.rb +22 -0
- data/lib/smart_message/serializer.rb +10 -0
- data/lib/smart_message/transport/base.rb +85 -0
- data/lib/smart_message/transport/memory_transport.rb +69 -0
- data/lib/smart_message/transport/registry.rb +59 -0
- data/lib/smart_message/transport/stdout_transport.rb +62 -0
- data/lib/smart_message/transport.rb +41 -0
- data/lib/smart_message/version.rb +7 -0
- data/lib/smart_message/wrapper.rb +43 -0
- data/lib/smart_message.rb +54 -0
- data/smart_message.gemspec +53 -0
- metadata +252 -0
@@ -0,0 +1,370 @@
|
|
1
|
+
# SmartMessage Architecture
|
2
|
+
|
3
|
+
SmartMessage follows a plugin-based architecture that cleanly separates message concerns from transport and serialization mechanisms.
|
4
|
+
|
5
|
+
## Design Philosophy
|
6
|
+
|
7
|
+
SmartMessage is designed around the principle that **messages should be independent of their delivery mechanism**. Just as ActiveRecord abstracts database operations from business logic, SmartMessage abstracts message delivery from message content.
|
8
|
+
|
9
|
+
### Core Principles
|
10
|
+
|
11
|
+
1. **Separation of Concerns**: Message content, transport, and serialization are independent
|
12
|
+
2. **Plugin Architecture**: Pluggable transports and serializers
|
13
|
+
3. **Dual Configuration**: Both class-level and instance-level configuration
|
14
|
+
4. **Thread Safety**: Concurrent message processing with thread pools
|
15
|
+
5. **Gateway Support**: Messages can flow between different transports/serializers
|
16
|
+
|
17
|
+
## Architecture Overview
|
18
|
+
|
19
|
+
```
|
20
|
+
┌─────────────────────────────────────────────────────────────┐
|
21
|
+
│ SmartMessage::Base │
|
22
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│
|
23
|
+
│ │ Message │ │ Transport │ │ Serializer ││
|
24
|
+
│ │ Properties │ │ Plugin │ │ Plugin ││
|
25
|
+
│ │ │ │ │ │ ││
|
26
|
+
│ │ • user_id │ │ • publish() │ │ • encode() ││
|
27
|
+
│ │ • action │ │ • subscribe() │ │ • decode() ││
|
28
|
+
│ │ • timestamp │ │ • receive() │ │ ││
|
29
|
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘│
|
30
|
+
└─────────────────────────────────────────────────────────────┘
|
31
|
+
│
|
32
|
+
▼
|
33
|
+
┌─────────────────────┐
|
34
|
+
│ Dispatcher │
|
35
|
+
│ │
|
36
|
+
│ • Route messages │
|
37
|
+
│ • Thread pool │
|
38
|
+
│ • Subscriptions │
|
39
|
+
└─────────────────────┘
|
40
|
+
│
|
41
|
+
▼
|
42
|
+
┌─────────────────────┐
|
43
|
+
│ Business Logic │
|
44
|
+
│ │
|
45
|
+
│ • process() method │
|
46
|
+
│ • Domain logic │
|
47
|
+
└─────────────────────┘
|
48
|
+
```
|
49
|
+
|
50
|
+
## Core Components
|
51
|
+
|
52
|
+
### 1. SmartMessage::Base
|
53
|
+
|
54
|
+
The foundation class that all messages inherit from, built on `Hashie::Dash`.
|
55
|
+
|
56
|
+
**Key Responsibilities:**
|
57
|
+
- Property management and validation
|
58
|
+
- Plugin configuration (transport, serializer, logger)
|
59
|
+
- Message lifecycle management
|
60
|
+
- Header generation and management
|
61
|
+
|
62
|
+
**Location:** `lib/smart_message/base.rb:11-278`
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class MyMessage < SmartMessage::Base
|
66
|
+
property :data
|
67
|
+
|
68
|
+
config do
|
69
|
+
transport MyTransport.new
|
70
|
+
serializer MySerializer.new
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### 2. Transport Layer
|
76
|
+
|
77
|
+
Handles message delivery and routing between systems.
|
78
|
+
|
79
|
+
**Key Responsibilities:**
|
80
|
+
- Message publishing and receiving
|
81
|
+
- Subscription management
|
82
|
+
- Connection handling
|
83
|
+
- Transport-specific configuration
|
84
|
+
|
85
|
+
**Location:** `lib/smart_message/transport/`
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# Transport interface
|
89
|
+
class CustomTransport < SmartMessage::Transport::Base
|
90
|
+
def publish(message_header, message_payload)
|
91
|
+
# Send message via your transport
|
92
|
+
end
|
93
|
+
|
94
|
+
def subscribe(message_class, process_method)
|
95
|
+
# Set up subscription
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
### 3. Serializer System
|
101
|
+
|
102
|
+
Handles encoding and decoding of message content.
|
103
|
+
|
104
|
+
**Key Responsibilities:**
|
105
|
+
- Message encoding (Ruby object → wire format)
|
106
|
+
- Message decoding (wire format → Ruby object)
|
107
|
+
- Format-specific handling
|
108
|
+
|
109
|
+
**Location:** `lib/smart_message/serializer/`
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
class CustomSerializer < SmartMessage::Serializer::Base
|
113
|
+
def encode(message_instance)
|
114
|
+
# Convert to wire format
|
115
|
+
end
|
116
|
+
|
117
|
+
def decode(payload)
|
118
|
+
# Convert from wire format
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
### 4. Dispatcher
|
124
|
+
|
125
|
+
Routes incoming messages to appropriate handlers using concurrent processing.
|
126
|
+
|
127
|
+
**Key Responsibilities:**
|
128
|
+
- Message routing based on class
|
129
|
+
- Thread pool management
|
130
|
+
- Subscription catalog management
|
131
|
+
- Statistics collection
|
132
|
+
|
133
|
+
**Location:** `lib/smart_message/dispatcher.rb:11-147`
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
dispatcher = SmartMessage::Dispatcher.new
|
137
|
+
dispatcher.add("MyMessage", "MyMessage.process")
|
138
|
+
dispatcher.route(header, payload)
|
139
|
+
```
|
140
|
+
|
141
|
+
### 5. Message Headers
|
142
|
+
|
143
|
+
Standard metadata attached to every message.
|
144
|
+
|
145
|
+
**Key Responsibilities:**
|
146
|
+
- Message identification (UUID)
|
147
|
+
- Routing information (message class)
|
148
|
+
- Tracking data (timestamps, process IDs)
|
149
|
+
|
150
|
+
**Location:** `lib/smart_message/header.rb:9-20`
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
header = message._sm_header
|
154
|
+
puts header.uuid # "550e8400-e29b-41d4-a716-446655440000"
|
155
|
+
puts header.message_class # "MyMessage"
|
156
|
+
puts header.published_at # 2025-08-17 10:30:00 UTC
|
157
|
+
```
|
158
|
+
|
159
|
+
## Message Lifecycle
|
160
|
+
|
161
|
+
### 1. Definition Phase
|
162
|
+
```ruby
|
163
|
+
class OrderMessage < SmartMessage::Base
|
164
|
+
property :order_id
|
165
|
+
property :amount
|
166
|
+
|
167
|
+
config do
|
168
|
+
transport SmartMessage::Transport.create(:memory)
|
169
|
+
serializer SmartMessage::Serializer::JSON.new
|
170
|
+
end
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
### 2. Subscription Phase
|
175
|
+
```ruby
|
176
|
+
OrderMessage.subscribe
|
177
|
+
# Registers "OrderMessage.process" with dispatcher
|
178
|
+
```
|
179
|
+
|
180
|
+
### 3. Publishing Phase
|
181
|
+
```ruby
|
182
|
+
order = OrderMessage.new(order_id: "123", amount: 99.99)
|
183
|
+
order.publish
|
184
|
+
# 1. Creates header with UUID, timestamp, etc.
|
185
|
+
# 2. Encodes message via serializer
|
186
|
+
# 3. Sends via transport
|
187
|
+
```
|
188
|
+
|
189
|
+
### 4. Receiving Phase
|
190
|
+
```ruby
|
191
|
+
# Transport receives message
|
192
|
+
transport.receive(header, payload)
|
193
|
+
# 1. Routes to dispatcher
|
194
|
+
# 2. Dispatcher finds subscribers
|
195
|
+
# 3. Spawns thread for processing
|
196
|
+
# 4. Calls OrderMessage.process(header, payload)
|
197
|
+
```
|
198
|
+
|
199
|
+
### 5. Processing Phase
|
200
|
+
```ruby
|
201
|
+
def self.process(message_header, message_payload)
|
202
|
+
# 1. Decode payload
|
203
|
+
data = JSON.parse(message_payload)
|
204
|
+
order = new(data)
|
205
|
+
|
206
|
+
# 2. Execute business logic
|
207
|
+
fulfill_order(order)
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
## Plugin System Architecture
|
212
|
+
|
213
|
+
### Dual-Level Configuration
|
214
|
+
|
215
|
+
SmartMessage supports configuration at both class and instance levels:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
# Class-level (default for all instances)
|
219
|
+
class PaymentMessage < SmartMessage::Base
|
220
|
+
config do
|
221
|
+
transport ProductionTransport.new
|
222
|
+
serializer SecureSerializer.new
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Instance-level (overrides class configuration)
|
227
|
+
test_payment = PaymentMessage.new(amount: 1.00)
|
228
|
+
test_payment.config do
|
229
|
+
transport TestTransport.new # Override for this instance
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
This enables sophisticated gateway patterns where messages can be:
|
234
|
+
- Received from one transport (e.g., RabbitMQ)
|
235
|
+
- Processed with business logic
|
236
|
+
- Republished to another transport (e.g., Kafka)
|
237
|
+
|
238
|
+
### Plugin Registration
|
239
|
+
|
240
|
+
Transports are registered in a central registry:
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
# Register custom transport
|
244
|
+
SmartMessage::Transport.register(:redis, RedisTransport)
|
245
|
+
|
246
|
+
# Use registered transport
|
247
|
+
MyMessage.config do
|
248
|
+
transport SmartMessage::Transport.create(:redis, url: "redis://localhost")
|
249
|
+
end
|
250
|
+
```
|
251
|
+
|
252
|
+
## Thread Safety & Concurrency
|
253
|
+
|
254
|
+
### Thread Pool Management
|
255
|
+
|
256
|
+
The dispatcher uses `Concurrent::CachedThreadPool` for processing:
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
# Each message processing happens in its own thread
|
260
|
+
@router_pool.post do
|
261
|
+
# Message processing happens here
|
262
|
+
target_class.constantize.process(header, payload)
|
263
|
+
end
|
264
|
+
```
|
265
|
+
|
266
|
+
### Thread Safety Considerations
|
267
|
+
|
268
|
+
1. **Message Instances**: Each message is processed in isolation
|
269
|
+
2. **Shared State**: Avoid shared mutable state in message classes
|
270
|
+
3. **Statistics**: Thread-safe statistics collection via `SimpleStats`
|
271
|
+
4. **Graceful Shutdown**: Automatic cleanup on process exit
|
272
|
+
|
273
|
+
### Monitoring Thread Pools
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
dispatcher = SmartMessage::Dispatcher.new
|
277
|
+
status = dispatcher.status
|
278
|
+
|
279
|
+
puts "Running: #{status[:running]}"
|
280
|
+
puts "Queue length: #{status[:queue_length]}"
|
281
|
+
puts "Completed tasks: #{status[:completed_task_count]}"
|
282
|
+
```
|
283
|
+
|
284
|
+
## Error Handling Architecture
|
285
|
+
|
286
|
+
### Exception Isolation
|
287
|
+
|
288
|
+
Processing exceptions are isolated to prevent cascade failures:
|
289
|
+
|
290
|
+
```ruby
|
291
|
+
begin
|
292
|
+
target_class.constantize.process(header, payload)
|
293
|
+
rescue Exception => e
|
294
|
+
# Log error but don't crash the dispatcher
|
295
|
+
# TODO: Add proper exception logging
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
### Custom Error Types
|
300
|
+
|
301
|
+
SmartMessage defines specific error types for different failure modes:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
module SmartMessage::Errors
|
305
|
+
class TransportNotConfigured < RuntimeError; end
|
306
|
+
class SerializerNotConfigured < RuntimeError; end
|
307
|
+
class NotImplemented < RuntimeError; end
|
308
|
+
class ReceivedMessageNotSubscribed < RuntimeError; end
|
309
|
+
class UnknownMessageClass < RuntimeError; end
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
313
|
+
## Statistics & Monitoring
|
314
|
+
|
315
|
+
### Built-in Statistics
|
316
|
+
|
317
|
+
SmartMessage automatically collects processing statistics:
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
# Statistics are collected for:
|
321
|
+
SS.add(message_class, 'publish')
|
322
|
+
SS.add(message_class, process_method, 'routed')
|
323
|
+
|
324
|
+
# Access statistics
|
325
|
+
puts SS.stat
|
326
|
+
puts SS.get("MyMessage", "publish")
|
327
|
+
```
|
328
|
+
|
329
|
+
### Monitoring Points
|
330
|
+
|
331
|
+
1. **Message Publishing**: Count of published messages per class
|
332
|
+
2. **Message Routing**: Count of routed messages per processor
|
333
|
+
3. **Thread Pool**: Queue length, completed tasks, running status
|
334
|
+
4. **Transport Status**: Connection status, message counts
|
335
|
+
|
336
|
+
## Configuration Architecture
|
337
|
+
|
338
|
+
### Configuration Hierarchy
|
339
|
+
|
340
|
+
1. **Class-level defaults**: Set via `MyMessage.config`
|
341
|
+
2. **Instance-level overrides**: Set via `message.config`
|
342
|
+
3. **Runtime configuration**: Dynamic plugin switching
|
343
|
+
|
344
|
+
### Configuration Objects
|
345
|
+
|
346
|
+
Configuration uses method-based DSL:
|
347
|
+
|
348
|
+
```ruby
|
349
|
+
config do
|
350
|
+
transport MyTransport.new(option1: value1)
|
351
|
+
serializer MySerializer.new(option2: value2)
|
352
|
+
logger MyLogger.new(level: :debug)
|
353
|
+
end
|
354
|
+
```
|
355
|
+
|
356
|
+
### Plugin Resolution
|
357
|
+
|
358
|
+
When a message needs a plugin:
|
359
|
+
|
360
|
+
1. Check instance-level configuration
|
361
|
+
2. Fall back to class-level configuration
|
362
|
+
3. Raise error if not configured
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
def transport
|
366
|
+
@transport || @@transport || raise(Errors::TransportNotConfigured)
|
367
|
+
end
|
368
|
+
```
|
369
|
+
|
370
|
+
This architecture provides flexibility while maintaining clear fallback behavior.
|