smart_message 0.0.1 → 0.0.3
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/.gitignore +1 -0
- data/CHANGELOG.md +31 -1
- data/Gemfile.lock +6 -1
- data/README.md +103 -21
- data/docs/README.md +5 -4
- data/docs/architecture.md +41 -8
- data/docs/dispatcher.md +52 -16
- data/docs/getting-started.md +64 -2
- data/docs/message_processing.md +423 -0
- data/docs/proc_handlers_summary.md +247 -0
- data/docs/properties.md +471 -0
- data/docs/transports.md +202 -8
- data/examples/04_redis_smart_home_iot.rb +649 -0
- data/examples/05_proc_handlers.rb +181 -0
- data/examples/README.md +118 -3
- data/examples/smart_home_iot_dataflow.md +257 -0
- data/lib/smart_message/base.rb +108 -4
- data/lib/smart_message/dispatcher.rb +22 -6
- data/lib/smart_message/property_descriptions.rb +41 -0
- 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 +23 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a7c214ec62b411d8f3bbf0925d6547fa135233684ac22483e028ffc76cc6cbe
|
4
|
+
data.tar.gz: f80ce67e2ea59172a36a8c7b675864c93573bf1f2b94e294124bdf2bbb28ae73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da70af3b299ef64e94d8cefd87b3bb25b0a50dd3b835d4765a6f26231c65d0fa69b2ccf3d204d8de666aca2e241dcb37e396dcd064aaeae895881885e8c1ea21
|
7
|
+
data.tar.gz: fd2da658b323102bcb93e73f9b5d0ae843bd6afc7a8f031be7ebc206d89231f3304092d83de79524d7fd337f09a0bafad7b7bfc782e9f7a9820d32568c65e252
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -7,7 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
-
## [0.0.
|
10
|
+
## [0.0.2] - 2025-08-17
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- **Property Descriptions**: New property-level description system for message documentation
|
14
|
+
- Add descriptions to individual properties using `property :name, description: "text"`
|
15
|
+
- Access descriptions programmatically via `property_description(:name)` method
|
16
|
+
- Get all property descriptions with `property_descriptions` method
|
17
|
+
- List properties with descriptions using `described_properties` method
|
18
|
+
- Implemented via new `SmartMessage::PropertyDescriptions` module
|
19
|
+
- **Class-Level Descriptions**: New message class documentation system
|
20
|
+
- Set class descriptions using `description "text"` class method
|
21
|
+
- Retrieve descriptions with `MyMessage.description`
|
22
|
+
- Can be set within config blocks or after class definition
|
23
|
+
- Useful for API documentation and code generation tools
|
24
|
+
- **Enhanced Documentation**
|
25
|
+
- Added comprehensive property system documentation (`docs/properties.md`)
|
26
|
+
- Documents all Hashie::Dash options and SmartMessage enhancements
|
27
|
+
- Includes examples of property transformations, coercions, and translations
|
28
|
+
- Added to documentation table of contents
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
- Updated README.md with property and class description examples
|
32
|
+
- Enhanced SmartMessage::Base with property introspection capabilities
|
33
|
+
|
34
|
+
### Tests
|
35
|
+
- Added comprehensive test suite for property descriptions (`test/property_descriptions_test.rb`)
|
36
|
+
- Added test suite for class-level descriptions (`test/description_test.rb`)
|
37
|
+
- Updated base_test.rb with class description test cases
|
38
|
+
- All tests passing with full backward compatibility
|
39
|
+
|
40
|
+
## [0.0.1] - 2025-08-17
|
11
41
|
|
12
42
|
### Added
|
13
43
|
- Comprehensive example applications demonstrating different messaging patterns
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
smart_message (0.0.
|
4
|
+
smart_message (0.0.3)
|
5
5
|
activesupport
|
6
6
|
concurrent-ruby
|
7
7
|
hashie
|
8
|
+
redis
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
@@ -41,6 +42,10 @@ GEM
|
|
41
42
|
mutex_m (0.3.0)
|
42
43
|
power_assert (2.0.5)
|
43
44
|
rake (13.3.0)
|
45
|
+
redis (5.4.1)
|
46
|
+
redis-client (>= 0.22.0)
|
47
|
+
redis-client (0.25.2)
|
48
|
+
connection_pool
|
44
49
|
securerandom (0.4.1)
|
45
50
|
shoulda (4.0.0)
|
46
51
|
shoulda-context (~> 2.0)
|
data/README.md
CHANGED
@@ -7,12 +7,14 @@ SmartMessage is a message abstraction framework that decouples business logic fr
|
|
7
7
|
|
8
8
|
## Features
|
9
9
|
|
10
|
-
- **Transport Abstraction**: Plugin architecture supporting multiple message transports (RabbitMQ, Kafka, etc.)
|
10
|
+
- **Transport Abstraction**: Plugin architecture supporting multiple message transports (Redis, RabbitMQ, Kafka, etc.)
|
11
11
|
- **Serialization Flexibility**: Pluggable serialization formats (JSON, MessagePack, etc.)
|
12
|
+
- **Flexible Message Handlers**: Multiple subscription patterns - default methods, custom methods, blocks, procs, and lambdas
|
12
13
|
- **Dual-Level Configuration**: Class and instance-level plugin overrides for gateway patterns
|
13
14
|
- **Concurrent Processing**: Thread-safe message routing using `Concurrent::CachedThreadPool`
|
14
15
|
- **Built-in Statistics**: Message processing metrics and monitoring
|
15
16
|
- **Development Tools**: STDOUT and in-memory transports for testing
|
17
|
+
- **Production Ready**: Redis transport with automatic reconnection and error handling
|
16
18
|
|
17
19
|
## Installation
|
18
20
|
|
@@ -36,10 +38,10 @@ Or install it yourself as:
|
|
36
38
|
|
37
39
|
```ruby
|
38
40
|
class OrderMessage < SmartMessage::Base
|
39
|
-
property :order_id
|
40
|
-
property :customer_id
|
41
|
-
property :amount
|
42
|
-
property :items
|
41
|
+
property :order_id, description: "Unique order identifier"
|
42
|
+
property :customer_id, description: "Customer's unique ID"
|
43
|
+
property :amount, description: "Total order amount in dollars"
|
44
|
+
property :items, description: "Array of ordered items"
|
43
45
|
|
44
46
|
# Configure transport and serializer at class level
|
45
47
|
config do
|
@@ -85,12 +87,33 @@ order.publish
|
|
85
87
|
|
86
88
|
### 3. Subscribe to Messages
|
87
89
|
|
90
|
+
SmartMessage supports multiple ways to handle incoming messages:
|
91
|
+
|
88
92
|
```ruby
|
89
|
-
#
|
93
|
+
# 1. Default handler (uses self.process method)
|
90
94
|
OrderMessage.subscribe
|
91
95
|
|
92
|
-
#
|
93
|
-
OrderMessage.subscribe("
|
96
|
+
# 2. Custom method handler
|
97
|
+
OrderMessage.subscribe("PaymentService.process_order")
|
98
|
+
|
99
|
+
# 3. Block handler (NEW!)
|
100
|
+
OrderMessage.subscribe do |header, payload|
|
101
|
+
order_data = JSON.parse(payload)
|
102
|
+
puts "Quick processing: Order #{order_data['order_id']}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# 4. Proc handler (NEW!)
|
106
|
+
order_processor = proc do |header, payload|
|
107
|
+
order_data = JSON.parse(payload)
|
108
|
+
EmailService.send_confirmation(order_data['customer_id'])
|
109
|
+
end
|
110
|
+
OrderMessage.subscribe(order_processor)
|
111
|
+
|
112
|
+
# 5. Lambda handler (NEW!)
|
113
|
+
audit_handler = lambda do |header, payload|
|
114
|
+
AuditLog.record("Order processed at #{header.published_at}")
|
115
|
+
end
|
116
|
+
OrderMessage.subscribe(audit_handler)
|
94
117
|
```
|
95
118
|
|
96
119
|
## Architecture
|
@@ -109,6 +132,7 @@ Pluggable message delivery system with built-in implementations:
|
|
109
132
|
|
110
133
|
- **StdoutTransport**: Development and testing transport
|
111
134
|
- **MemoryTransport**: In-memory queuing for testing
|
135
|
+
- **RedisTransport**: Redis pub/sub transport for production messaging
|
112
136
|
- **Custom Transports**: Implement `SmartMessage::Transport::Base`
|
113
137
|
|
114
138
|
#### Serializer System
|
@@ -177,35 +201,85 @@ puts transport.all_messages
|
|
177
201
|
transport.process_all # Process all pending messages
|
178
202
|
```
|
179
203
|
|
204
|
+
### Redis Transport (Production)
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
# Basic Redis configuration
|
208
|
+
transport = SmartMessage::Transport.create(:redis,
|
209
|
+
url: 'redis://localhost:6379',
|
210
|
+
db: 0
|
211
|
+
)
|
212
|
+
|
213
|
+
# Production configuration with custom options
|
214
|
+
transport = SmartMessage::Transport.create(:redis,
|
215
|
+
url: 'redis://prod-redis:6379',
|
216
|
+
db: 1,
|
217
|
+
auto_subscribe: true,
|
218
|
+
reconnect_attempts: 5,
|
219
|
+
reconnect_delay: 2
|
220
|
+
)
|
221
|
+
|
222
|
+
# Configure message class to use Redis
|
223
|
+
MyMessage.config do
|
224
|
+
transport SmartMessage::Transport.create(:redis, url: 'redis://localhost:6379')
|
225
|
+
serializer SmartMessage::Serializer::JSON.new
|
226
|
+
end
|
227
|
+
|
228
|
+
# Subscribe to messages (uses message class name as Redis channel)
|
229
|
+
MyMessage.subscribe
|
230
|
+
|
231
|
+
# Publish messages (automatically publishes to Redis channel named "MyMessage")
|
232
|
+
message = MyMessage.new(data: "Hello Redis!")
|
233
|
+
message.publish
|
234
|
+
```
|
235
|
+
|
236
|
+
The Redis transport uses the message class name as the Redis channel name, enabling automatic routing of messages to their appropriate handlers.
|
237
|
+
|
180
238
|
### Custom Transport
|
181
239
|
|
182
240
|
```ruby
|
183
|
-
class
|
241
|
+
class WebhookTransport < SmartMessage::Transport::Base
|
184
242
|
def default_options
|
185
|
-
{
|
243
|
+
{
|
244
|
+
webhook_url: "https://api.example.com/webhooks",
|
245
|
+
timeout: 30,
|
246
|
+
retries: 3
|
247
|
+
}
|
186
248
|
end
|
187
249
|
|
188
250
|
def configure
|
189
|
-
|
251
|
+
require 'net/http'
|
252
|
+
@uri = URI(@options[:webhook_url])
|
190
253
|
end
|
191
254
|
|
192
255
|
def publish(message_header, message_payload)
|
193
|
-
|
194
|
-
@
|
256
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
257
|
+
http.use_ssl = @uri.scheme == 'https'
|
258
|
+
|
259
|
+
request = Net::HTTP::Post.new(@uri)
|
260
|
+
request['Content-Type'] = 'application/json'
|
261
|
+
request['X-Message-Class'] = message_header.message_class
|
262
|
+
request.body = message_payload
|
263
|
+
|
264
|
+
response = http.request(request)
|
265
|
+
raise "Webhook failed: #{response.code}" unless response.code.to_i < 400
|
195
266
|
end
|
196
267
|
|
197
268
|
def subscribe(message_class, process_method)
|
198
269
|
super
|
199
|
-
#
|
270
|
+
# For webhooks, subscription would typically be configured
|
271
|
+
# externally on the webhook provider's side
|
200
272
|
end
|
201
273
|
end
|
202
274
|
|
203
275
|
# Register the transport
|
204
|
-
SmartMessage::Transport.register(:
|
276
|
+
SmartMessage::Transport.register(:webhook, WebhookTransport)
|
205
277
|
|
206
278
|
# Use the transport
|
207
279
|
MyMessage.config do
|
208
|
-
transport SmartMessage::Transport.create(:
|
280
|
+
transport SmartMessage::Transport.create(:webhook,
|
281
|
+
webhook_url: "https://api.myservice.com/messages"
|
282
|
+
)
|
209
283
|
end
|
210
284
|
```
|
211
285
|
|
@@ -214,8 +288,12 @@ end
|
|
214
288
|
1. **Definition**: Create message class inheriting from `SmartMessage::Base`
|
215
289
|
2. **Configuration**: Set transport, serializer, and logger plugins
|
216
290
|
3. **Publishing**: Message instance is encoded and sent through transport
|
217
|
-
4. **Subscription**: Message classes register with dispatcher for processing
|
218
|
-
|
291
|
+
4. **Subscription**: Message classes register handlers with dispatcher for processing
|
292
|
+
- Default handlers (`self.process` method)
|
293
|
+
- Custom method handlers (`"ClassName.method_name"`)
|
294
|
+
- Block handlers (`subscribe do |h,p|...end`)
|
295
|
+
- Proc/Lambda handlers (`subscribe(proc {...})`)
|
296
|
+
5. **Processing**: Received messages are decoded and routed to registered handlers
|
219
297
|
|
220
298
|
## Advanced Usage
|
221
299
|
|
@@ -255,9 +333,9 @@ puts dispatcher.subscribers
|
|
255
333
|
|
256
334
|
```ruby
|
257
335
|
class MyMessage < SmartMessage::Base
|
258
|
-
property :user_id
|
259
|
-
property :action
|
260
|
-
property :timestamp, default: -> { Time.now }
|
336
|
+
property :user_id, description: "User's unique identifier"
|
337
|
+
property :action, description: "Action performed by the user"
|
338
|
+
property :timestamp, default: -> { Time.now }, description: "When the action occurred"
|
261
339
|
end
|
262
340
|
|
263
341
|
message = MyMessage.new(user_id: 123, action: "login")
|
@@ -266,6 +344,10 @@ message = MyMessage.new(user_id: 123, action: "login")
|
|
266
344
|
puts message.user_id
|
267
345
|
puts message.fields # Returns Set of property names (excluding internal _sm_ properties)
|
268
346
|
|
347
|
+
# Access property descriptions
|
348
|
+
puts MyMessage.property_description(:user_id) # => "User's unique identifier"
|
349
|
+
puts MyMessage.property_descriptions # => Hash of all descriptions
|
350
|
+
|
269
351
|
# Access message header
|
270
352
|
puts message._sm_header.uuid
|
271
353
|
puts message._sm_header.message_class
|
data/docs/README.md
CHANGED
@@ -8,14 +8,15 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
8
8
|
- [Installation & Quick Start](getting-started.md)
|
9
9
|
- [Basic Usage Examples](examples.md)
|
10
10
|
|
11
|
-
### Core Concepts
|
11
|
+
### Core Concepts
|
12
12
|
- [Architecture Overview](architecture.md)
|
13
13
|
- [Message Lifecycle](message-lifecycle.md)
|
14
14
|
- [Plugin System](plugin-system.md)
|
15
15
|
|
16
16
|
### Components
|
17
17
|
- [SmartMessage::Base](base.md)
|
18
|
-
- [
|
18
|
+
- [Property System](properties.md)
|
19
|
+
- [Transport Layer](transports.md)
|
19
20
|
- [Serializers](serializers.md)
|
20
21
|
- [Dispatcher & Routing](dispatcher.md)
|
21
22
|
- [Message Headers](headers.md)
|
@@ -47,6 +48,6 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
47
48
|
|
48
49
|
## Version
|
49
50
|
|
50
|
-
This documentation is for SmartMessage v0.0.
|
51
|
+
This documentation is for SmartMessage v0.0.2.
|
51
52
|
|
52
|
-
For older versions, please check the git tags and corresponding documentation.
|
53
|
+
For older versions, please check the git tags and corresponding documentation.
|
data/docs/architecture.md
CHANGED
@@ -40,10 +40,12 @@ SmartMessage is designed around the principle that **messages should be independ
|
|
40
40
|
│
|
41
41
|
▼
|
42
42
|
┌─────────────────────┐
|
43
|
-
│
|
43
|
+
│ Message Handlers │
|
44
44
|
│ │
|
45
|
-
│ •
|
46
|
-
│ •
|
45
|
+
│ • Default handler │
|
46
|
+
│ • Block handlers │
|
47
|
+
│ • Proc handlers │
|
48
|
+
│ • Method handlers │
|
47
49
|
└─────────────────────┘
|
48
50
|
```
|
49
51
|
|
@@ -193,21 +195,52 @@ transport.receive(header, payload)
|
|
193
195
|
# 1. Routes to dispatcher
|
194
196
|
# 2. Dispatcher finds subscribers
|
195
197
|
# 3. Spawns thread for processing
|
196
|
-
# 4. Calls
|
198
|
+
# 4. Calls registered message handlers
|
197
199
|
```
|
198
200
|
|
199
|
-
### 5. Processing
|
201
|
+
### 5. Message Handler Processing
|
202
|
+
|
203
|
+
SmartMessage supports multiple handler types, routed through the dispatcher:
|
204
|
+
|
200
205
|
```ruby
|
206
|
+
# Default handler (self.process method)
|
201
207
|
def self.process(message_header, message_payload)
|
202
|
-
# 1. Decode payload
|
203
208
|
data = JSON.parse(message_payload)
|
204
209
|
order = new(data)
|
205
|
-
|
206
|
-
# 2. Execute business logic
|
207
210
|
fulfill_order(order)
|
208
211
|
end
|
212
|
+
|
213
|
+
# Block handler (inline processing)
|
214
|
+
OrderMessage.subscribe do |header, payload|
|
215
|
+
data = JSON.parse(payload)
|
216
|
+
quick_processing(data)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Proc handler (reusable across message types)
|
220
|
+
audit_proc = proc do |header, payload|
|
221
|
+
AuditService.log_message(header.message_class, payload)
|
222
|
+
end
|
223
|
+
OrderMessage.subscribe(audit_proc)
|
224
|
+
|
225
|
+
# Method handler (service class processing)
|
226
|
+
class OrderService
|
227
|
+
def self.process_order(header, payload)
|
228
|
+
data = JSON.parse(payload)
|
229
|
+
complex_business_logic(data)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
OrderMessage.subscribe("OrderService.process_order")
|
209
233
|
```
|
210
234
|
|
235
|
+
**Handler Routing Process:**
|
236
|
+
1. Dispatcher receives message and header
|
237
|
+
2. Looks up all registered handlers for message class
|
238
|
+
3. For each handler:
|
239
|
+
- **String handlers**: Resolves to class method via constantize
|
240
|
+
- **Proc handlers**: Calls proc directly from registry
|
241
|
+
4. Executes handlers in parallel threads
|
242
|
+
5. Collects statistics and handles errors
|
243
|
+
|
211
244
|
## Plugin System Architecture
|
212
245
|
|
213
246
|
### Dual-Level Configuration
|
data/docs/dispatcher.md
CHANGED
@@ -28,27 +28,49 @@ Located at `lib/smart_message/dispatcher.rb:11-147`, the dispatcher is the centr
|
|
28
28
|
|
29
29
|
### Adding Subscriptions
|
30
30
|
|
31
|
+
SmartMessage supports multiple subscription patterns:
|
32
|
+
|
31
33
|
```ruby
|
32
|
-
#
|
34
|
+
# 1. Default handler - uses self.process method
|
33
35
|
MyMessage.subscribe
|
34
36
|
# Registers "MyMessage.process" as the handler
|
35
37
|
|
36
|
-
# Custom
|
37
|
-
MyMessage.subscribe("
|
38
|
-
# Registers "
|
38
|
+
# 2. Custom method handler
|
39
|
+
MyMessage.subscribe("MyService.handle_message")
|
40
|
+
# Registers "MyService.handle_message" as the handler
|
41
|
+
|
42
|
+
# 3. Block handler (NEW!)
|
43
|
+
handler_id = MyMessage.subscribe do |header, payload|
|
44
|
+
puts "Processing: #{JSON.parse(payload)}"
|
45
|
+
end
|
46
|
+
# Registers a proc handler with generated ID like "MyMessage.proc_abc123"
|
47
|
+
|
48
|
+
# 4. Proc handler (NEW!)
|
49
|
+
my_proc = proc { |header, payload| log_message(payload) }
|
50
|
+
proc_id = MyMessage.subscribe(my_proc)
|
51
|
+
# Registers the proc with generated ID
|
52
|
+
|
53
|
+
# 5. Lambda handler (NEW!)
|
54
|
+
my_lambda = lambda { |header, payload| validate_message(payload) }
|
55
|
+
lambda_id = MyMessage.subscribe(my_lambda)
|
39
56
|
|
40
57
|
# Multiple handlers for the same message
|
41
|
-
MyMessage.subscribe("MyMessage.
|
42
|
-
MyMessage.subscribe("MyMessage.
|
43
|
-
|
58
|
+
MyMessage.subscribe("MyMessage.audit")
|
59
|
+
MyMessage.subscribe("MyMessage.notify")
|
60
|
+
MyMessage.subscribe { |h,p| puts "Quick log" }
|
61
|
+
# All handlers will receive the message
|
44
62
|
```
|
45
63
|
|
46
64
|
### Removing Subscriptions
|
47
65
|
|
48
66
|
```ruby
|
49
|
-
# Remove specific handler
|
67
|
+
# Remove specific method handler
|
50
68
|
MyMessage.unsubscribe("MyMessage.custom_handler")
|
51
69
|
|
70
|
+
# Remove specific proc/block handler using returned ID
|
71
|
+
block_id = MyMessage.subscribe { |h,p| puts p }
|
72
|
+
MyMessage.unsubscribe(block_id) # Cleans up proc from registry too
|
73
|
+
|
52
74
|
# Remove ALL handlers for a message class
|
53
75
|
MyMessage.unsubscribe!
|
54
76
|
|
@@ -101,7 +123,7 @@ end
|
|
101
123
|
|
102
124
|
### 3. Concurrent Processing
|
103
125
|
|
104
|
-
Each handler is processed in its own thread:
|
126
|
+
Each handler is processed in its own thread, with support for both method and proc handlers:
|
105
127
|
|
106
128
|
```ruby
|
107
129
|
@subscribers[message_klass].each do |message_processor|
|
@@ -109,21 +131,35 @@ Each handler is processed in its own thread:
|
|
109
131
|
|
110
132
|
@router_pool.post do
|
111
133
|
# This runs in a separate thread
|
112
|
-
parts = message_processor.split('.')
|
113
|
-
target_klass = parts[0] # "MyMessage"
|
114
|
-
class_method = parts[1] # "process"
|
115
|
-
|
116
134
|
begin
|
117
|
-
|
118
|
-
|
119
|
-
|
135
|
+
# Check if this is a proc handler or a regular method call
|
136
|
+
if proc_handler?(message_processor)
|
137
|
+
# Call the proc handler via SmartMessage::Base
|
138
|
+
SmartMessage::Base.call_proc_handler(message_processor, message_header, message_payload)
|
139
|
+
else
|
140
|
+
# Original method call logic
|
141
|
+
parts = message_processor.split('.')
|
142
|
+
target_klass = parts[0] # "MyMessage"
|
143
|
+
class_method = parts[1] # "process"
|
144
|
+
|
145
|
+
target_klass.constantize
|
146
|
+
.method(class_method)
|
147
|
+
.call(message_header, message_payload)
|
148
|
+
end
|
120
149
|
rescue Exception => e
|
121
150
|
# Error handling - doesn't crash the dispatcher
|
151
|
+
puts "Error processing message: #{e.message}" if $DEBUG
|
122
152
|
end
|
123
153
|
end
|
124
154
|
end
|
125
155
|
```
|
126
156
|
|
157
|
+
**Handler Types Processed:**
|
158
|
+
- **Method handlers**: `"ClassName.method_name"` → resolved via constantize
|
159
|
+
- **Proc handlers**: `"ClassName.proc_abc123"` → looked up in proc registry
|
160
|
+
- **Block handlers**: `"ClassName.proc_def456"` → treated as proc handlers
|
161
|
+
- **Lambda handlers**: `"ClassName.proc_ghi789"` → treated as proc handlers
|
162
|
+
|
127
163
|
## Thread Pool Management
|
128
164
|
|
129
165
|
### Thread Pool Configuration
|
data/docs/getting-started.md
CHANGED
@@ -64,11 +64,27 @@ end
|
|
64
64
|
|
65
65
|
### 2. Subscribe to Messages
|
66
66
|
|
67
|
-
Before publishing, set up a subscription to receive messages:
|
67
|
+
Before publishing, set up a subscription to receive messages. SmartMessage provides several ways to handle incoming messages:
|
68
68
|
|
69
69
|
```ruby
|
70
|
-
#
|
70
|
+
# 1. Default handler (uses the self.process method defined above)
|
71
71
|
WelcomeMessage.subscribe
|
72
|
+
|
73
|
+
# 2. Block handler (inline processing logic)
|
74
|
+
WelcomeMessage.subscribe do |header, payload|
|
75
|
+
data = JSON.parse(payload)
|
76
|
+
puts "👋 Quick welcome for #{data['user_name']}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# 3. Proc handler (reusable processing logic)
|
80
|
+
welcome_processor = proc do |header, payload|
|
81
|
+
data = JSON.parse(payload)
|
82
|
+
EmailService.send_welcome_email(data['email'], data['user_name'])
|
83
|
+
end
|
84
|
+
WelcomeMessage.subscribe(welcome_processor)
|
85
|
+
|
86
|
+
# 4. Custom method handler
|
87
|
+
WelcomeMessage.subscribe("UserService.handle_welcome")
|
72
88
|
```
|
73
89
|
|
74
90
|
### 3. Create and Publish a Message
|
@@ -153,6 +169,52 @@ Serializers handle message encoding/decoding:
|
|
153
169
|
serializer SmartMessage::Serializer::JSON.new
|
154
170
|
```
|
155
171
|
|
172
|
+
### Message Handlers
|
173
|
+
|
174
|
+
SmartMessage supports four types of message handlers to give you flexibility in how you process messages:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
class OrderMessage < SmartMessage::Base
|
178
|
+
# Define your message properties
|
179
|
+
property :order_id
|
180
|
+
property :amount
|
181
|
+
|
182
|
+
# Default handler - processed when no custom handler specified
|
183
|
+
def self.process(header, payload)
|
184
|
+
puts "Default processing for order"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# 1. Default handler (uses self.process)
|
189
|
+
OrderMessage.subscribe
|
190
|
+
|
191
|
+
# 2. Block handler - great for simple, inline logic
|
192
|
+
OrderMessage.subscribe do |header, payload|
|
193
|
+
data = JSON.parse(payload)
|
194
|
+
puts "Block handler: Processing order #{data['order_id']}"
|
195
|
+
end
|
196
|
+
|
197
|
+
# 3. Proc handler - reusable across message types
|
198
|
+
audit_logger = proc do |header, payload|
|
199
|
+
puts "Audit: #{header.message_class} at #{header.published_at}"
|
200
|
+
end
|
201
|
+
OrderMessage.subscribe(audit_logger)
|
202
|
+
|
203
|
+
# 4. Method handler - organized in service classes
|
204
|
+
class OrderService
|
205
|
+
def self.process_order(header, payload)
|
206
|
+
puts "Service processing order"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
OrderMessage.subscribe("OrderService.process_order")
|
210
|
+
```
|
211
|
+
|
212
|
+
**When to use each type:**
|
213
|
+
- **Default**: Simple built-in processing for the message type
|
214
|
+
- **Block**: Quick inline logic specific to one subscription
|
215
|
+
- **Proc**: Reusable handlers that work across multiple message types
|
216
|
+
- **Method**: Complex business logic organized in service classes
|
217
|
+
|
156
218
|
## Next Steps
|
157
219
|
|
158
220
|
Now that you have the basics working, explore:
|