smart_message 0.0.3 → 0.0.5
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 +51 -0
- data/Gemfile.lock +1 -1
- data/README.md +244 -9
- data/docs/README.md +1 -0
- data/docs/architecture.md +2 -0
- data/docs/examples.md +2 -0
- data/docs/getting-started.md +11 -0
- data/docs/logging.md +452 -0
- data/docs/properties.md +213 -7
- data/examples/.gitignore +2 -0
- data/examples/01_point_to_point_orders.rb +27 -11
- data/examples/02_publish_subscribe_events.rb +16 -7
- data/examples/03_many_to_many_chat.rb +56 -22
- data/examples/04_redis_smart_home_iot.rb +48 -21
- data/examples/05_proc_handlers.rb +12 -5
- data/examples/06_custom_logger_example.rb +641 -0
- data/examples/07_error_handling_scenarios.rb +477 -0
- data/examples/tmux_chat/bot_agent.rb +4 -1
- data/examples/tmux_chat/shared_chat_system.rb +50 -22
- data/lib/smart_message/base.rb +105 -8
- data/lib/smart_message/errors.rb +3 -0
- data/lib/smart_message/header.rb +32 -5
- data/lib/smart_message/logger/default.rb +217 -0
- data/lib/smart_message/logger.rb +9 -1
- data/lib/smart_message/property_descriptions.rb +5 -4
- data/lib/smart_message/property_validations.rb +141 -0
- data/lib/smart_message/version.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c388c9738746603d14a57d6c4e04fb7d6e48156c4e571cd82dffd461217e111
|
4
|
+
data.tar.gz: 975779de0b52686b8b08baa94070fea52578c86f5fb1a3752aa25cad98a812d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b425fa9d66a2716394a1dc695b8ba82c9acc9c82dc616fc00cf8eeab2639a7833640e71240969614f31836ee0d325ae8980fa4f4d266e6961dfd6a8971172420
|
7
|
+
data.tar.gz: 711381ff80115a8582551fc58f22630c9c103c24d46f04b1edc54b87d41fd181d80a0f730cc6d14a000ec3b5478330062385edb6e5344fe1e541559e82652c97
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.0.5] 2025-08-18
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- **Enhanced Description DSL**: Extended class-level `description` method with automatic defaults
|
14
|
+
- Classes without explicit descriptions now automatically get `"ClassName is a SmartMessage"` default
|
15
|
+
- Instance method `message.description` provides access to class description
|
16
|
+
- Backward compatible with existing description implementations
|
17
|
+
- Improves developer experience by eliminating nil description values
|
18
|
+
- **Comprehensive Error Handling Example** (`examples/07_error_handling_scenarios.rb`)
|
19
|
+
- Demonstrates missing required property validation with detailed error messages
|
20
|
+
- Shows custom property validation failures with multiple validation types (regex, range, enum, proc)
|
21
|
+
- Illustrates version mismatch detection between message classes and headers
|
22
|
+
- **NEW**: Documents Hashie::Dash limitation where only first missing required property is reported
|
23
|
+
- Provides educational content about SmartMessage's robust validation framework
|
24
|
+
- Includes suggestions for potential improvements to multi-property validation
|
25
|
+
- **Complete Property Documentation**: All examples now include comprehensive descriptions
|
26
|
+
- Message class descriptions added to all example programs (01-06 + tmux_chat)
|
27
|
+
- Individual property descriptions added with detailed explanations of purpose and format
|
28
|
+
- Enhanced readability and educational value of all demonstration code
|
29
|
+
- Consistent documentation patterns across all messaging scenarios
|
30
|
+
|
31
|
+
### Fixed
|
32
|
+
- **Critical: Infinite Loop Bug in Chat Examples**
|
33
|
+
- Fixed infinite message loops in `examples/03_many_to_many_chat.rb` and `examples/tmux_chat/bot_agent.rb`
|
34
|
+
- **Root Cause**: Bots responding to other bots' messages containing trigger keywords ("hello", "hi")
|
35
|
+
- **Solution**: Added `return if chat_data['message_type'] == 'bot'` to prevent bot-to-bot responses
|
36
|
+
- Examples now terminate properly without requiring manual intervention
|
37
|
+
- Maintains proper bot functionality for human interactions and explicit commands
|
38
|
+
- **Example Termination**: Chat examples now shutdown cleanly without hanging indefinitely
|
39
|
+
|
40
|
+
### Changed
|
41
|
+
- **Enhanced Documentation Coverage**
|
42
|
+
- Updated README.md with comprehensive description DSL documentation
|
43
|
+
- Enhanced `docs/properties.md` with updated class description behavior and default handling
|
44
|
+
- Updated `docs/getting-started.md`, `docs/architecture.md`, and `docs/examples.md` with description examples
|
45
|
+
- All documentation now demonstrates proper message and property description usage
|
46
|
+
- **Improved Example Educational Value**
|
47
|
+
- All example programs enhanced with descriptive class and property documentation
|
48
|
+
- Better demonstration of SmartMessage's self-documenting capabilities
|
49
|
+
- Examples serve as both functional demonstrations and documentation references
|
50
|
+
|
51
|
+
### Documentation
|
52
|
+
- **Hashie::Dash Limitation Discovery**: Documented important framework limitation in error handling example
|
53
|
+
- Only first missing required property reported during validation failures
|
54
|
+
- Provides clear explanation of incremental error discovery behavior
|
55
|
+
- Suggests potential solutions for improved multi-property validation
|
56
|
+
- **Comprehensive Description System**: Complete documentation of message documentation capabilities
|
57
|
+
- Class-level description DSL with automatic defaults
|
58
|
+
- Property-level descriptions with usage patterns
|
59
|
+
- Integration with existing SmartMessage validation and introspection systems
|
60
|
+
|
10
61
|
## [0.0.2] - 2025-08-17
|
11
62
|
|
12
63
|
### Added
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,6 +9,9 @@ SmartMessage is a message abstraction framework that decouples business logic fr
|
|
9
9
|
|
10
10
|
- **Transport Abstraction**: Plugin architecture supporting multiple message transports (Redis, RabbitMQ, Kafka, etc.)
|
11
11
|
- **Serialization Flexibility**: Pluggable serialization formats (JSON, MessagePack, etc.)
|
12
|
+
- **Schema Versioning**: Built-in version management with automatic compatibility validation
|
13
|
+
- **Comprehensive Validation**: Property validation with custom error messages and automatic validation before publishing
|
14
|
+
- **Message Documentation**: Built-in documentation support for message classes and properties with automatic defaults
|
12
15
|
- **Flexible Message Handlers**: Multiple subscription patterns - default methods, custom methods, blocks, procs, and lambdas
|
13
16
|
- **Dual-Level Configuration**: Class and instance-level plugin overrides for gateway patterns
|
14
17
|
- **Concurrent Processing**: Thread-safe message routing using `Concurrent::CachedThreadPool`
|
@@ -38,10 +41,35 @@ Or install it yourself as:
|
|
38
41
|
|
39
42
|
```ruby
|
40
43
|
class OrderMessage < SmartMessage::Base
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
# Declare schema version for compatibility tracking
|
45
|
+
version 2
|
46
|
+
|
47
|
+
# Add a description for the message class
|
48
|
+
description "Represents customer order data for processing and fulfillment"
|
49
|
+
|
50
|
+
# Required properties with validation
|
51
|
+
property :order_id,
|
52
|
+
required: true,
|
53
|
+
message: "Order ID is required",
|
54
|
+
validate: ->(v) { v.is_a?(String) && v.length > 0 },
|
55
|
+
validation_message: "Order ID must be a non-empty string",
|
56
|
+
description: "Unique order identifier"
|
57
|
+
|
58
|
+
property :customer_id,
|
59
|
+
required: true,
|
60
|
+
message: "Customer ID is required",
|
61
|
+
description: "Customer's unique ID"
|
62
|
+
|
63
|
+
property :amount,
|
64
|
+
required: true,
|
65
|
+
message: "Amount is required",
|
66
|
+
validate: ->(v) { v.is_a?(Numeric) && v > 0 },
|
67
|
+
validation_message: "Amount must be a positive number",
|
68
|
+
description: "Total order amount in dollars"
|
69
|
+
|
70
|
+
property :items,
|
71
|
+
default: [],
|
72
|
+
description: "Array of ordered items"
|
45
73
|
|
46
74
|
# Configure transport and serializer at class level
|
47
75
|
config do
|
@@ -74,7 +102,7 @@ end
|
|
74
102
|
### 2. Publish Messages
|
75
103
|
|
76
104
|
```ruby
|
77
|
-
# Create and publish a message
|
105
|
+
# Create and publish a message (automatically validated before publishing)
|
78
106
|
order = OrderMessage.new(
|
79
107
|
order_id: "ORD-123",
|
80
108
|
customer_id: "CUST-456",
|
@@ -82,7 +110,16 @@ order = OrderMessage.new(
|
|
82
110
|
items: ["Widget A", "Widget B"]
|
83
111
|
)
|
84
112
|
|
85
|
-
|
113
|
+
# Message is automatically validated before publishing
|
114
|
+
order.publish # Validates all properties, header, and version compatibility
|
115
|
+
|
116
|
+
# Or validate manually
|
117
|
+
if order.valid?
|
118
|
+
order.publish
|
119
|
+
else
|
120
|
+
errors = order.validation_errors
|
121
|
+
errors.each { |err| puts "#{err[:property]}: #{err[:message]}" }
|
122
|
+
end
|
86
123
|
```
|
87
124
|
|
88
125
|
### 3. Subscribe to Messages
|
@@ -287,13 +324,211 @@ end
|
|
287
324
|
|
288
325
|
1. **Definition**: Create message class inheriting from `SmartMessage::Base`
|
289
326
|
2. **Configuration**: Set transport, serializer, and logger plugins
|
290
|
-
3. **
|
291
|
-
4. **
|
327
|
+
3. **Validation**: Messages are automatically validated before publishing (properties, header, version compatibility)
|
328
|
+
4. **Publishing**: Message instance is encoded and sent through transport
|
329
|
+
5. **Subscription**: Message classes register handlers with dispatcher for processing
|
292
330
|
- Default handlers (`self.process` method)
|
293
331
|
- Custom method handlers (`"ClassName.method_name"`)
|
294
332
|
- Block handlers (`subscribe do |h,p|...end`)
|
295
333
|
- Proc/Lambda handlers (`subscribe(proc {...})`)
|
296
|
-
|
334
|
+
6. **Processing**: Received messages are decoded and routed to registered handlers
|
335
|
+
|
336
|
+
## Schema Versioning and Validation
|
337
|
+
|
338
|
+
SmartMessage includes comprehensive validation and versioning capabilities to ensure message integrity and schema evolution support.
|
339
|
+
|
340
|
+
### Version Declaration
|
341
|
+
|
342
|
+
Declare your message schema version using the `version` class method:
|
343
|
+
|
344
|
+
```ruby
|
345
|
+
class OrderMessage < SmartMessage::Base
|
346
|
+
version 2 # Schema version 2
|
347
|
+
|
348
|
+
property :order_id, required: true
|
349
|
+
property :customer_email # Added in version 2
|
350
|
+
end
|
351
|
+
```
|
352
|
+
|
353
|
+
### Property Validation
|
354
|
+
|
355
|
+
Properties support multiple validation types with custom error messages:
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
class UserMessage < SmartMessage::Base
|
359
|
+
version 1
|
360
|
+
|
361
|
+
# Required field validation (Hashie built-in)
|
362
|
+
property :user_id,
|
363
|
+
required: true,
|
364
|
+
message: "User ID is required and cannot be blank"
|
365
|
+
|
366
|
+
# Custom validation with lambda
|
367
|
+
property :age,
|
368
|
+
validate: ->(v) { v.is_a?(Integer) && v.between?(1, 120) },
|
369
|
+
validation_message: "Age must be an integer between 1 and 120"
|
370
|
+
|
371
|
+
# Email validation with regex
|
372
|
+
property :email,
|
373
|
+
validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
|
374
|
+
validation_message: "Must be a valid email address"
|
375
|
+
|
376
|
+
# Inclusion validation with array
|
377
|
+
property :status,
|
378
|
+
validate: ['active', 'inactive', 'pending'],
|
379
|
+
validation_message: "Status must be active, inactive, or pending"
|
380
|
+
end
|
381
|
+
```
|
382
|
+
|
383
|
+
### Validation Methods
|
384
|
+
|
385
|
+
All message instances include validation methods:
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
user = UserMessage.new(user_id: "123", age: 25)
|
389
|
+
|
390
|
+
# Validate entire message (properties + header + version)
|
391
|
+
user.validate! # Raises SmartMessage::Errors::ValidationError on failure
|
392
|
+
user.valid? # Returns true/false
|
393
|
+
|
394
|
+
# Get detailed validation errors
|
395
|
+
errors = user.validation_errors
|
396
|
+
errors.each do |error|
|
397
|
+
puts "#{error[:source]}.#{error[:property]}: #{error[:message]}"
|
398
|
+
# Example output:
|
399
|
+
# message.age: Age must be an integer between 1 and 120
|
400
|
+
# header.version: Header version must be a positive integer
|
401
|
+
# version_mismatch.version: Expected version 1, got: 2
|
402
|
+
end
|
403
|
+
```
|
404
|
+
|
405
|
+
### Automatic Validation
|
406
|
+
|
407
|
+
Messages are automatically validated during publishing:
|
408
|
+
|
409
|
+
```ruby
|
410
|
+
# This will raise ValidationError if invalid
|
411
|
+
message = UserMessage.new(user_id: "", age: 150)
|
412
|
+
message.publish # Automatically validates before publishing
|
413
|
+
```
|
414
|
+
|
415
|
+
### Version Compatibility
|
416
|
+
|
417
|
+
The framework automatically validates version compatibility:
|
418
|
+
|
419
|
+
```ruby
|
420
|
+
class V2Message < SmartMessage::Base
|
421
|
+
version 2
|
422
|
+
property :data
|
423
|
+
end
|
424
|
+
|
425
|
+
message = V2Message.new(data: "test")
|
426
|
+
# Header automatically gets version: 2
|
427
|
+
|
428
|
+
# Simulate version mismatch (e.g., from older message)
|
429
|
+
message._sm_header.version = 1
|
430
|
+
message.validate! # Raises: "V2Message expects version 2, but header has version 1"
|
431
|
+
```
|
432
|
+
|
433
|
+
### Supported Validation Types
|
434
|
+
|
435
|
+
- **Proc/Lambda**: `validate: ->(v) { v.length > 5 }`
|
436
|
+
- **Regexp**: `validate: /\A[a-z]+\z/`
|
437
|
+
- **Class**: `validate: String` (type checking)
|
438
|
+
- **Array**: `validate: ['red', 'green', 'blue']` (inclusion)
|
439
|
+
- **Range**: `validate: (1..100)` (range checking)
|
440
|
+
- **Symbol**: `validate: :custom_validator_method`
|
441
|
+
|
442
|
+
## Message Documentation
|
443
|
+
|
444
|
+
SmartMessage provides built-in documentation capabilities for both message classes and their properties.
|
445
|
+
|
446
|
+
### Class-Level Descriptions
|
447
|
+
|
448
|
+
Use the `description` DSL method to document what your message class represents:
|
449
|
+
|
450
|
+
```ruby
|
451
|
+
class OrderMessage < SmartMessage::Base
|
452
|
+
description "Represents customer order data for processing and fulfillment"
|
453
|
+
|
454
|
+
property :order_id, required: true
|
455
|
+
property :amount, required: true
|
456
|
+
end
|
457
|
+
|
458
|
+
class UserMessage < SmartMessage::Base
|
459
|
+
description "Handles user management operations including registration and updates"
|
460
|
+
|
461
|
+
property :user_id, required: true
|
462
|
+
property :email, required: true
|
463
|
+
end
|
464
|
+
|
465
|
+
# Access descriptions
|
466
|
+
puts OrderMessage.description
|
467
|
+
# => "Represents customer order data for processing and fulfillment"
|
468
|
+
|
469
|
+
puts UserMessage.description
|
470
|
+
# => "Handles user management operations including registration and updates"
|
471
|
+
|
472
|
+
# Instance access to class description
|
473
|
+
order = OrderMessage.new(order_id: "123", amount: 99.99)
|
474
|
+
puts order.description
|
475
|
+
# => "Represents customer order data for processing and fulfillment"
|
476
|
+
```
|
477
|
+
|
478
|
+
### Default Descriptions
|
479
|
+
|
480
|
+
Classes without explicit descriptions automatically get a default description:
|
481
|
+
|
482
|
+
```ruby
|
483
|
+
class MyMessage < SmartMessage::Base
|
484
|
+
property :data
|
485
|
+
end
|
486
|
+
|
487
|
+
puts MyMessage.description
|
488
|
+
# => "MyMessage is a SmartMessage"
|
489
|
+
```
|
490
|
+
|
491
|
+
### Property Documentation
|
492
|
+
|
493
|
+
Combine class descriptions with property descriptions for comprehensive documentation:
|
494
|
+
|
495
|
+
```ruby
|
496
|
+
class FullyDocumented < SmartMessage::Base
|
497
|
+
description "A fully documented message class for demonstration purposes"
|
498
|
+
|
499
|
+
property :id,
|
500
|
+
description: "Unique identifier for the record"
|
501
|
+
property :name,
|
502
|
+
description: "Display name for the entity"
|
503
|
+
property :status,
|
504
|
+
description: "Current processing status",
|
505
|
+
validate: ['active', 'inactive', 'pending']
|
506
|
+
end
|
507
|
+
|
508
|
+
# Access all documentation
|
509
|
+
puts FullyDocumented.description
|
510
|
+
# => "A fully documented message class for demonstration purposes"
|
511
|
+
|
512
|
+
puts FullyDocumented.property_description(:id)
|
513
|
+
# => "Unique identifier for the record"
|
514
|
+
|
515
|
+
puts FullyDocumented.property_descriptions
|
516
|
+
# => {:id=>"Unique identifier for the record", :name=>"Display name for the entity", ...}
|
517
|
+
```
|
518
|
+
|
519
|
+
### Documentation in Config Blocks
|
520
|
+
|
521
|
+
You can also set descriptions within configuration blocks:
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
class ConfiguredMessage < SmartMessage::Base
|
525
|
+
config do
|
526
|
+
description "Set within config block"
|
527
|
+
transport SmartMessage::Transport.create(:stdout)
|
528
|
+
serializer SmartMessage::Serializer::JSON.new
|
529
|
+
end
|
530
|
+
end
|
531
|
+
```
|
297
532
|
|
298
533
|
## Advanced Usage
|
299
534
|
|
data/docs/README.md
CHANGED
@@ -18,6 +18,7 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
18
18
|
- [Property System](properties.md)
|
19
19
|
- [Transport Layer](transports.md)
|
20
20
|
- [Serializers](serializers.md)
|
21
|
+
- [Logging System](logging.md)
|
21
22
|
- [Dispatcher & Routing](dispatcher.md)
|
22
23
|
- [Message Headers](headers.md)
|
23
24
|
|
data/docs/architecture.md
CHANGED
data/docs/examples.md
CHANGED
@@ -10,6 +10,8 @@ This document provides practical examples of using SmartMessage in real-world sc
|
|
10
10
|
require 'smart_message'
|
11
11
|
|
12
12
|
class NotificationMessage < SmartMessage::Base
|
13
|
+
description "Sends notifications to users via multiple channels"
|
14
|
+
|
13
15
|
property :recipient
|
14
16
|
property :subject
|
15
17
|
property :body
|
data/docs/getting-started.md
CHANGED
@@ -37,6 +37,9 @@ Let's create a simple message class and see it in action:
|
|
37
37
|
require 'smart_message'
|
38
38
|
|
39
39
|
class WelcomeMessage < SmartMessage::Base
|
40
|
+
# Add a description for the message class
|
41
|
+
description "Welcomes new users after successful signup"
|
42
|
+
|
40
43
|
# Define message properties
|
41
44
|
property :user_name
|
42
45
|
property :email
|
@@ -132,6 +135,8 @@ Messages use Hashie::Dash properties for type-safe attributes:
|
|
132
135
|
|
133
136
|
```ruby
|
134
137
|
class OrderMessage < SmartMessage::Base
|
138
|
+
description "Represents customer orders for processing and fulfillment"
|
139
|
+
|
135
140
|
property :order_id, required: true
|
136
141
|
property :amount, transform_with: ->(v) { BigDecimal(v.to_s) }
|
137
142
|
property :items, default: []
|
@@ -175,6 +180,8 @@ SmartMessage supports four types of message handlers to give you flexibility in
|
|
175
180
|
|
176
181
|
```ruby
|
177
182
|
class OrderMessage < SmartMessage::Base
|
183
|
+
description "Handles customer order processing and fulfillment"
|
184
|
+
|
178
185
|
# Define your message properties
|
179
186
|
property :order_id
|
180
187
|
property :amount
|
@@ -230,6 +237,8 @@ Now that you have the basics working, explore:
|
|
230
237
|
|
231
238
|
```ruby
|
232
239
|
class NotificationMessage < SmartMessage::Base
|
240
|
+
description "Sends notifications to users via email, SMS, or push"
|
241
|
+
|
233
242
|
property :recipient
|
234
243
|
property :subject
|
235
244
|
property :body
|
@@ -271,6 +280,8 @@ NotificationMessage.new(
|
|
271
280
|
|
272
281
|
```ruby
|
273
282
|
class EventMessage < SmartMessage::Base
|
283
|
+
description "Logs application events for monitoring and analytics"
|
284
|
+
|
274
285
|
property :event_type
|
275
286
|
property :user_id
|
276
287
|
property :data
|