smart_message 0.0.2 → 0.0.4
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 +31 -1
- data/Gemfile.lock +6 -1
- data/README.md +92 -14
- data/docs/README.md +1 -0
- data/docs/architecture.md +41 -8
- data/docs/dispatcher.md +52 -16
- data/docs/getting-started.md +64 -2
- data/docs/logging.md +452 -0
- data/docs/message_processing.md +423 -0
- data/docs/proc_handlers_summary.md +247 -0
- data/docs/transports.md +202 -8
- data/examples/.gitignore +2 -0
- data/examples/04_redis_smart_home_iot.rb +649 -0
- data/examples/05_proc_handlers.rb +181 -0
- data/examples/06_custom_logger_example.rb +620 -0
- data/examples/README.md +118 -3
- data/examples/smart_home_iot_dataflow.md +257 -0
- data/lib/smart_message/base.rb +94 -4
- data/lib/smart_message/dispatcher.rb +22 -6
- data/lib/smart_message/logger/default.rb +217 -0
- data/lib/smart_message/logger.rb +9 -1
- data/lib/smart_message/transport/redis_transport.rb +190 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +1 -0
- data/lib/smart_message/version.rb +1 -1
- data/smart_message.gemspec +1 -0
- metadata +25 -1
data/docs/transports.md
CHANGED
@@ -125,6 +125,194 @@ messages.each do |msg|
|
|
125
125
|
end
|
126
126
|
```
|
127
127
|
|
128
|
+
### Redis Transport
|
129
|
+
|
130
|
+
Production-ready Redis pub/sub transport for distributed messaging.
|
131
|
+
|
132
|
+
**Features:**
|
133
|
+
- Redis pub/sub messaging
|
134
|
+
- Automatic channel management using message class names
|
135
|
+
- Thread-safe subscriber management
|
136
|
+
- Connection resilience with automatic reconnection
|
137
|
+
- Configurable connection parameters
|
138
|
+
- Background message subscription threads
|
139
|
+
|
140
|
+
**Usage:**
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# Basic Redis configuration
|
144
|
+
transport = SmartMessage::Transport.create(:redis,
|
145
|
+
url: 'redis://localhost:6379',
|
146
|
+
db: 0
|
147
|
+
)
|
148
|
+
|
149
|
+
# Production configuration with custom options
|
150
|
+
transport = SmartMessage::Transport.create(:redis,
|
151
|
+
url: 'redis://prod-redis:6379',
|
152
|
+
db: 1,
|
153
|
+
auto_subscribe: true,
|
154
|
+
reconnect_attempts: 5,
|
155
|
+
reconnect_delay: 2
|
156
|
+
)
|
157
|
+
|
158
|
+
# Configure in message class
|
159
|
+
class OrderMessage < SmartMessage::Base
|
160
|
+
property :order_id
|
161
|
+
property :customer_id
|
162
|
+
property :amount
|
163
|
+
|
164
|
+
config do
|
165
|
+
transport SmartMessage::Transport.create(:redis,
|
166
|
+
url: 'redis://localhost:6379',
|
167
|
+
db: 1
|
168
|
+
)
|
169
|
+
serializer SmartMessage::Serializer::JSON.new
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.process(message_header, message_payload)
|
173
|
+
order_data = JSON.parse(message_payload)
|
174
|
+
order = new(order_data)
|
175
|
+
puts "Processing order #{order.order_id} for $#{order.amount}"
|
176
|
+
# Your business logic here
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Subscribe to messages (creates Redis subscription to "OrderMessage" channel)
|
181
|
+
OrderMessage.subscribe
|
182
|
+
|
183
|
+
# Publish messages (publishes to "OrderMessage" Redis channel)
|
184
|
+
order = OrderMessage.new(
|
185
|
+
order_id: "ORD-123",
|
186
|
+
customer_id: "CUST-456",
|
187
|
+
amount: 99.99
|
188
|
+
)
|
189
|
+
order.publish
|
190
|
+
```
|
191
|
+
|
192
|
+
**Options:**
|
193
|
+
- `url` (String): Redis connection URL (default: 'redis://localhost:6379')
|
194
|
+
- `db` (Integer): Redis database number (default: 0)
|
195
|
+
- `auto_subscribe` (Boolean): Automatically start subscriber thread (default: true)
|
196
|
+
- `reconnect_attempts` (Integer): Number of reconnection attempts (default: 5)
|
197
|
+
- `reconnect_delay` (Integer): Delay between reconnection attempts in seconds (default: 1)
|
198
|
+
- `debug` (Boolean): Enable debug output (default: false)
|
199
|
+
|
200
|
+
**Channel Naming:**
|
201
|
+
|
202
|
+
The Redis transport uses the message class name as the Redis channel name. This provides automatic routing:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
class UserMessage < SmartMessage::Base
|
206
|
+
# Messages published to/from Redis channel "UserMessage"
|
207
|
+
end
|
208
|
+
|
209
|
+
class AdminMessage < SmartMessage::Base
|
210
|
+
# Messages published to/from Redis channel "AdminMessage"
|
211
|
+
end
|
212
|
+
|
213
|
+
class OrderProcessing::PaymentMessage < SmartMessage::Base
|
214
|
+
# Messages published to/from Redis channel "OrderProcessing::PaymentMessage"
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
**Connection Management:**
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
transport = SmartMessage::Transport.create(:redis, url: 'redis://localhost:6379')
|
222
|
+
|
223
|
+
# Check connection status
|
224
|
+
puts transport.connected? # => true/false
|
225
|
+
|
226
|
+
# Manual connection management
|
227
|
+
transport.connect
|
228
|
+
transport.disconnect
|
229
|
+
|
230
|
+
# The transport automatically reconnects on connection failures
|
231
|
+
```
|
232
|
+
|
233
|
+
**Multi-Message Type Support:**
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
# Different message types can share the same Redis transport
|
237
|
+
redis_transport = SmartMessage::Transport.create(:redis,
|
238
|
+
url: 'redis://localhost:6379',
|
239
|
+
auto_subscribe: true
|
240
|
+
)
|
241
|
+
|
242
|
+
# Configure multiple message classes to use the same transport
|
243
|
+
[OrderMessage, PaymentMessage, ShippingMessage].each do |msg_class|
|
244
|
+
msg_class.config do
|
245
|
+
transport redis_transport
|
246
|
+
serializer SmartMessage::Serializer::JSON.new
|
247
|
+
end
|
248
|
+
|
249
|
+
# Subscribe to each message type (creates separate Redis subscriptions)
|
250
|
+
msg_class.subscribe
|
251
|
+
end
|
252
|
+
|
253
|
+
# Publishing to any message type routes to its specific Redis channel
|
254
|
+
OrderMessage.new(order_id: "123").publish # -> "OrderMessage" channel
|
255
|
+
PaymentMessage.new(amount: 50.0).publish # -> "PaymentMessage" channel
|
256
|
+
ShippingMessage.new(tracking: "ABC").publish # -> "ShippingMessage" channel
|
257
|
+
```
|
258
|
+
|
259
|
+
**Error Handling and Resilience:**
|
260
|
+
|
261
|
+
The Redis transport includes built-in error handling:
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
# Automatic reconnection on connection failures
|
265
|
+
transport = SmartMessage::Transport.create(:redis,
|
266
|
+
url: 'redis://localhost:6379',
|
267
|
+
reconnect_attempts: 5, # Try 5 times to reconnect
|
268
|
+
reconnect_delay: 2 # Wait 2 seconds between attempts
|
269
|
+
)
|
270
|
+
|
271
|
+
# Connection failures during publishing will trigger automatic retry
|
272
|
+
# If all reconnection attempts fail, the original error is raised
|
273
|
+
```
|
274
|
+
|
275
|
+
**Production Deployment:**
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
# Production Redis configuration
|
279
|
+
class ProductionMessage < SmartMessage::Base
|
280
|
+
config do
|
281
|
+
transport SmartMessage::Transport.create(:redis,
|
282
|
+
url: ENV['REDIS_URL'] || 'redis://localhost:6379',
|
283
|
+
db: ENV['REDIS_DB']&.to_i || 0,
|
284
|
+
auto_subscribe: true,
|
285
|
+
reconnect_attempts: 10,
|
286
|
+
reconnect_delay: 5
|
287
|
+
)
|
288
|
+
serializer SmartMessage::Serializer::JSON.new
|
289
|
+
logger Logger.new(STDOUT)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
```
|
293
|
+
|
294
|
+
**Testing with Redis:**
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
# Test configuration (using separate Redis database)
|
298
|
+
class TestMessage < SmartMessage::Base
|
299
|
+
config do
|
300
|
+
transport SmartMessage::Transport.create(:redis,
|
301
|
+
url: 'redis://localhost:6379',
|
302
|
+
db: 15, # Use separate database for tests
|
303
|
+
auto_subscribe: true
|
304
|
+
)
|
305
|
+
serializer SmartMessage::Serializer::JSON.new
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# In your test setup
|
310
|
+
def setup
|
311
|
+
# Clear test database
|
312
|
+
Redis.new(url: 'redis://localhost:6379', db: 15).flushdb
|
313
|
+
end
|
314
|
+
```
|
315
|
+
|
128
316
|
## Transport Interface
|
129
317
|
|
130
318
|
All transports must implement the `SmartMessage::Transport::Base` interface:
|
@@ -188,19 +376,24 @@ transport.receive(message_header, message_payload) # protected method
|
|
188
376
|
Register custom transports for easy creation:
|
189
377
|
|
190
378
|
```ruby
|
191
|
-
# Register
|
192
|
-
SmartMessage::Transport.register(:redis, RedisTransport)
|
379
|
+
# Register custom transport classes
|
193
380
|
SmartMessage::Transport.register(:kafka, KafkaTransport)
|
381
|
+
SmartMessage::Transport.register(:webhook, WebhookTransport)
|
194
382
|
|
195
|
-
# List all registered transports
|
383
|
+
# List all registered transports (includes built-ins)
|
196
384
|
puts SmartMessage::Transport.available
|
197
|
-
# => [:stdout, :memory, :redis, :kafka]
|
385
|
+
# => [:stdout, :memory, :redis, :kafka, :webhook]
|
198
386
|
|
199
|
-
# Create instances
|
387
|
+
# Create instances of built-in transports
|
200
388
|
redis_transport = SmartMessage::Transport.create(:redis,
|
201
389
|
url: "redis://localhost:6379"
|
202
390
|
)
|
203
391
|
|
392
|
+
memory_transport = SmartMessage::Transport.create(:memory,
|
393
|
+
auto_process: true
|
394
|
+
)
|
395
|
+
|
396
|
+
# Create instances of custom transports
|
204
397
|
kafka_transport = SmartMessage::Transport.create(:kafka,
|
205
398
|
servers: ["localhost:9092"]
|
206
399
|
)
|
@@ -311,12 +504,13 @@ SmartMessage::Transport.create(:memory,
|
|
311
504
|
max_messages: 1000
|
312
505
|
)
|
313
506
|
|
314
|
-
#
|
507
|
+
# Redis specific
|
315
508
|
SmartMessage::Transport.create(:redis,
|
316
509
|
url: "redis://localhost:6379",
|
317
510
|
db: 1,
|
318
|
-
|
319
|
-
|
511
|
+
auto_subscribe: true,
|
512
|
+
reconnect_attempts: 5,
|
513
|
+
reconnect_delay: 2
|
320
514
|
)
|
321
515
|
```
|
322
516
|
|
data/examples/.gitignore
ADDED