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
data/README.md
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
# SmartMessage
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/smart_message)
|
4
|
+
[](https://www.ruby-lang.org/en/)
|
5
|
+
|
6
|
+
SmartMessage is a message abstraction framework that decouples business logic from message transports and serialization formats. Much like ActiveRecord abstracts models from database implementations, SmartMessage abstracts messages from their backend transport and serialization mechanisms.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- **Transport Abstraction**: Plugin architecture supporting multiple message transports (RabbitMQ, Kafka, etc.)
|
11
|
+
- **Serialization Flexibility**: Pluggable serialization formats (JSON, MessagePack, etc.)
|
12
|
+
- **Dual-Level Configuration**: Class and instance-level plugin overrides for gateway patterns
|
13
|
+
- **Concurrent Processing**: Thread-safe message routing using `Concurrent::CachedThreadPool`
|
14
|
+
- **Built-in Statistics**: Message processing metrics and monitoring
|
15
|
+
- **Development Tools**: STDOUT and in-memory transports for testing
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'smart_message'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle install
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install smart_message
|
32
|
+
|
33
|
+
## Quick Start
|
34
|
+
|
35
|
+
### 1. Define a Message Class
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class OrderMessage < SmartMessage::Base
|
39
|
+
property :order_id
|
40
|
+
property :customer_id
|
41
|
+
property :amount
|
42
|
+
property :items
|
43
|
+
|
44
|
+
# Configure transport and serializer at class level
|
45
|
+
config do
|
46
|
+
transport SmartMessage::Transport.create(:stdout, loopback: true)
|
47
|
+
serializer SmartMessage::Serializer::JSON.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# Business logic for processing received messages
|
51
|
+
def self.process(message_header, message_payload)
|
52
|
+
# Decode the message
|
53
|
+
order_data = JSON.parse(message_payload)
|
54
|
+
order = new(order_data)
|
55
|
+
|
56
|
+
# Process the order
|
57
|
+
puts "Processing order #{order.order_id} for customer #{order.customer_id}"
|
58
|
+
puts "Amount: $#{order.amount}"
|
59
|
+
|
60
|
+
# Your business logic here
|
61
|
+
process_order(order)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def self.process_order(order)
|
67
|
+
# Implementation specific to your domain
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### 2. Publish Messages
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# Create and publish a message
|
76
|
+
order = OrderMessage.new(
|
77
|
+
order_id: "ORD-123",
|
78
|
+
customer_id: "CUST-456",
|
79
|
+
amount: 99.99,
|
80
|
+
items: ["Widget A", "Widget B"]
|
81
|
+
)
|
82
|
+
|
83
|
+
order.publish
|
84
|
+
```
|
85
|
+
|
86
|
+
### 3. Subscribe to Messages
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# Subscribe to process incoming OrderMessage instances
|
90
|
+
OrderMessage.subscribe
|
91
|
+
|
92
|
+
# Or specify a custom processing method
|
93
|
+
OrderMessage.subscribe("OrderMessage.custom_processor")
|
94
|
+
```
|
95
|
+
|
96
|
+
## Architecture
|
97
|
+
|
98
|
+
### Core Components
|
99
|
+
|
100
|
+
#### SmartMessage::Base
|
101
|
+
The foundation class that all messages inherit from. Built on `Hashie::Dash` with extensions for:
|
102
|
+
- Property management and coercion
|
103
|
+
- Multi-level plugin configuration
|
104
|
+
- Message lifecycle management
|
105
|
+
- Automatic header generation (UUID, timestamps, process tracking)
|
106
|
+
|
107
|
+
#### Transport Layer
|
108
|
+
Pluggable message delivery system with built-in implementations:
|
109
|
+
|
110
|
+
- **StdoutTransport**: Development and testing transport
|
111
|
+
- **MemoryTransport**: In-memory queuing for testing
|
112
|
+
- **Custom Transports**: Implement `SmartMessage::Transport::Base`
|
113
|
+
|
114
|
+
#### Serializer System
|
115
|
+
Pluggable message encoding/decoding:
|
116
|
+
|
117
|
+
- **JSON Serializer**: Built-in JSON support
|
118
|
+
- **Custom Serializers**: Implement `SmartMessage::Serializer::Base`
|
119
|
+
|
120
|
+
#### Dispatcher
|
121
|
+
Concurrent message routing engine that:
|
122
|
+
- Uses thread pools for async processing
|
123
|
+
- Routes messages to subscribed handlers
|
124
|
+
- Provides processing statistics
|
125
|
+
- Handles graceful shutdown
|
126
|
+
|
127
|
+
### Plugin Architecture
|
128
|
+
|
129
|
+
SmartMessage supports two levels of plugin configuration:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
# Class-level configuration (default for all instances)
|
133
|
+
class MyMessage < SmartMessage::Base
|
134
|
+
config do
|
135
|
+
transport MyTransport.new
|
136
|
+
serializer MySerializer.new
|
137
|
+
logger MyLogger.new
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Instance-level configuration (overrides class defaults)
|
142
|
+
message = MyMessage.new
|
143
|
+
message.config do
|
144
|
+
transport DifferentTransport.new # Override for this instance
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
This enables gateway patterns where messages can be received from one transport/serializer and republished to another.
|
149
|
+
|
150
|
+
## Transport Implementations
|
151
|
+
|
152
|
+
### STDOUT Transport (Development)
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# Basic STDOUT output
|
156
|
+
transport = SmartMessage::Transport.create(:stdout)
|
157
|
+
|
158
|
+
# With loopback for testing subscriptions
|
159
|
+
transport = SmartMessage::Transport.create(:stdout, loopback: true)
|
160
|
+
|
161
|
+
# Output to file
|
162
|
+
transport = SmartMessage::Transport.create(:stdout, output: "messages.log")
|
163
|
+
```
|
164
|
+
|
165
|
+
### Memory Transport (Testing)
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# Auto-process messages as they're published
|
169
|
+
transport = SmartMessage::Transport.create(:memory, auto_process: true)
|
170
|
+
|
171
|
+
# Store messages without processing
|
172
|
+
transport = SmartMessage::Transport.create(:memory, auto_process: false)
|
173
|
+
|
174
|
+
# Check stored messages
|
175
|
+
puts transport.message_count
|
176
|
+
puts transport.all_messages
|
177
|
+
transport.process_all # Process all pending messages
|
178
|
+
```
|
179
|
+
|
180
|
+
### Custom Transport
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
class RedisTransport < SmartMessage::Transport::Base
|
184
|
+
def default_options
|
185
|
+
{ redis_url: "redis://localhost:6379" }
|
186
|
+
end
|
187
|
+
|
188
|
+
def configure
|
189
|
+
@redis = Redis.new(url: @options[:redis_url])
|
190
|
+
end
|
191
|
+
|
192
|
+
def publish(message_header, message_payload)
|
193
|
+
channel = message_header.message_class
|
194
|
+
@redis.publish(channel, message_payload)
|
195
|
+
end
|
196
|
+
|
197
|
+
def subscribe(message_class, process_method)
|
198
|
+
super
|
199
|
+
# Set up Redis subscription for message_class
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Register the transport
|
204
|
+
SmartMessage::Transport.register(:redis, RedisTransport)
|
205
|
+
|
206
|
+
# Use the transport
|
207
|
+
MyMessage.config do
|
208
|
+
transport SmartMessage::Transport.create(:redis, redis_url: "redis://prod:6379")
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
## Message Lifecycle
|
213
|
+
|
214
|
+
1. **Definition**: Create message class inheriting from `SmartMessage::Base`
|
215
|
+
2. **Configuration**: Set transport, serializer, and logger plugins
|
216
|
+
3. **Publishing**: Message instance is encoded and sent through transport
|
217
|
+
4. **Subscription**: Message classes register with dispatcher for processing
|
218
|
+
5. **Processing**: Received messages are decoded and `process` method is called
|
219
|
+
|
220
|
+
## Advanced Usage
|
221
|
+
|
222
|
+
### Statistics and Monitoring
|
223
|
+
|
224
|
+
SmartMessage includes built-in statistics collection:
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
# Access global statistics
|
228
|
+
puts SS.stat # Shows all collected statistics
|
229
|
+
|
230
|
+
# Get specific counts
|
231
|
+
publish_count = SS.get("MyMessage", "publish")
|
232
|
+
process_count = SS.get("MyMessage", "MyMessage.process", "routed")
|
233
|
+
|
234
|
+
# Reset statistics
|
235
|
+
SS.reset # Clear all stats
|
236
|
+
SS.reset("MyMessage", "publish") # Reset specific stat
|
237
|
+
```
|
238
|
+
|
239
|
+
### Dispatcher Status
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
dispatcher = SmartMessage::Dispatcher.new
|
243
|
+
|
244
|
+
# Check thread pool status
|
245
|
+
status = dispatcher.status
|
246
|
+
puts "Running: #{status[:running]}"
|
247
|
+
puts "Queue length: #{status[:queue_length]}"
|
248
|
+
puts "Completed tasks: #{status[:completed_task_count]}"
|
249
|
+
|
250
|
+
# Check subscriptions
|
251
|
+
puts dispatcher.subscribers
|
252
|
+
```
|
253
|
+
|
254
|
+
### Message Properties and Headers
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
class MyMessage < SmartMessage::Base
|
258
|
+
property :user_id
|
259
|
+
property :action
|
260
|
+
property :timestamp, default: -> { Time.now }
|
261
|
+
end
|
262
|
+
|
263
|
+
message = MyMessage.new(user_id: 123, action: "login")
|
264
|
+
|
265
|
+
# Access message properties
|
266
|
+
puts message.user_id
|
267
|
+
puts message.fields # Returns Set of property names (excluding internal _sm_ properties)
|
268
|
+
|
269
|
+
# Access message header
|
270
|
+
puts message._sm_header.uuid
|
271
|
+
puts message._sm_header.message_class
|
272
|
+
puts message._sm_header.published_at
|
273
|
+
puts message._sm_header.publisher_pid
|
274
|
+
```
|
275
|
+
|
276
|
+
## Development
|
277
|
+
|
278
|
+
After checking out the repo, run:
|
279
|
+
|
280
|
+
```bash
|
281
|
+
bin/setup # Install dependencies
|
282
|
+
bin/console # Start interactive console
|
283
|
+
rake test # Run test suite
|
284
|
+
```
|
285
|
+
|
286
|
+
### Testing
|
287
|
+
|
288
|
+
SmartMessage uses Minitest with Shoulda for testing:
|
289
|
+
|
290
|
+
```bash
|
291
|
+
rake test # Run all tests
|
292
|
+
ruby -Ilib:test test/base_test.rb # Run specific test file
|
293
|
+
```
|
294
|
+
|
295
|
+
Test output and debug information is logged to `test.log`.
|
296
|
+
|
297
|
+
## Contributing
|
298
|
+
|
299
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/MadBomber/smart_message.
|
300
|
+
|
301
|
+
## License
|
302
|
+
|
303
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "smart_message"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/docs/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# SmartMessage Documentation
|
2
|
+
|
3
|
+
Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abstracts messages from backend transport systems.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
### Getting Started
|
8
|
+
- [Installation & Quick Start](getting-started.md)
|
9
|
+
- [Basic Usage Examples](examples.md)
|
10
|
+
|
11
|
+
### Core Concepts
|
12
|
+
- [Architecture Overview](architecture.md)
|
13
|
+
- [Message Lifecycle](message-lifecycle.md)
|
14
|
+
- [Plugin System](plugin-system.md)
|
15
|
+
|
16
|
+
### Components
|
17
|
+
- [SmartMessage::Base](base.md)
|
18
|
+
- [Transport Layer](transports.md)
|
19
|
+
- [Serializers](serializers.md)
|
20
|
+
- [Dispatcher & Routing](dispatcher.md)
|
21
|
+
- [Message Headers](headers.md)
|
22
|
+
|
23
|
+
### Advanced Topics
|
24
|
+
- [Custom Transports](custom-transports.md)
|
25
|
+
- [Custom Serializers](custom-serializers.md)
|
26
|
+
- [Gateway Patterns](gateway-patterns.md)
|
27
|
+
- [Statistics & Monitoring](monitoring.md)
|
28
|
+
- [Thread Safety](thread-safety.md)
|
29
|
+
|
30
|
+
### Development
|
31
|
+
- [Testing Guide](testing.md)
|
32
|
+
- [Contributing](contributing.md)
|
33
|
+
- [Troubleshooting](troubleshooting.md)
|
34
|
+
|
35
|
+
### Reference
|
36
|
+
- [API Reference](api-reference.md)
|
37
|
+
- [Configuration Options](configuration.md)
|
38
|
+
- [Error Handling](error-handling.md)
|
39
|
+
|
40
|
+
## Quick Navigation
|
41
|
+
|
42
|
+
- **New to SmartMessage?** Start with [Getting Started](getting-started.md)
|
43
|
+
- **Need examples?** Check out [Examples](examples.md)
|
44
|
+
- **Understanding the architecture?** Read [Architecture Overview](architecture.md)
|
45
|
+
- **Building custom components?** See [Custom Transports](custom-transports.md) and [Custom Serializers](custom-serializers.md)
|
46
|
+
- **Having issues?** Visit [Troubleshooting](troubleshooting.md)
|
47
|
+
|
48
|
+
## Version
|
49
|
+
|
50
|
+
This documentation is for SmartMessage v0.0.1.
|
51
|
+
|
52
|
+
For older versions, please check the git tags and corresponding documentation.
|