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
@@ -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.