smart_message 0.0.4 → 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/README.md +244 -9
- data/docs/architecture.md +2 -0
- data/docs/examples.md +2 -0
- data/docs/getting-started.md +11 -0
- data/docs/properties.md +213 -7
- 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 +34 -13
- 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/property_descriptions.rb +5 -4
- data/lib/smart_message/property_validations.rb +141 -0
- data/lib/smart_message/version.rb +1 -1
- metadata +3 -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/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/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
|
data/docs/properties.md
CHANGED
@@ -4,6 +4,7 @@ The SmartMessage property system builds on Hashie::Dash to provide a robust, dec
|
|
4
4
|
|
5
5
|
## Table of Contents
|
6
6
|
- [Basic Property Definition](#basic-property-definition)
|
7
|
+
- [Schema Versioning](#schema-versioning)
|
7
8
|
- [Class-Level Description](#class-level-description)
|
8
9
|
- [Property Options](#property-options)
|
9
10
|
- [Accessing Property Information](#accessing-property-information)
|
@@ -20,9 +21,115 @@ class MyMessage < SmartMessage::Base
|
|
20
21
|
end
|
21
22
|
```
|
22
23
|
|
24
|
+
## Schema Versioning
|
25
|
+
|
26
|
+
SmartMessage supports schema versioning to enable message evolution while maintaining compatibility:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class OrderMessage < SmartMessage::Base
|
30
|
+
version 2 # Declare schema version
|
31
|
+
|
32
|
+
property :order_id, required: true
|
33
|
+
property :customer_email # Added in version 2
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
### Version Management
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# Version 1 message
|
41
|
+
class V1OrderMessage < SmartMessage::Base
|
42
|
+
version 1 # or omit for default version 1
|
43
|
+
|
44
|
+
property :order_id, required: true
|
45
|
+
property :amount, required: true
|
46
|
+
end
|
47
|
+
|
48
|
+
# Version 2 message with additional field
|
49
|
+
class V2OrderMessage < SmartMessage::Base
|
50
|
+
version 2
|
51
|
+
|
52
|
+
property :order_id, required: true
|
53
|
+
property :amount, required: true
|
54
|
+
property :customer_email # New in version 2
|
55
|
+
end
|
56
|
+
|
57
|
+
# Version 3 message with validation
|
58
|
+
class V3OrderMessage < SmartMessage::Base
|
59
|
+
version 3
|
60
|
+
|
61
|
+
property :order_id,
|
62
|
+
required: true,
|
63
|
+
validate: ->(v) { v.is_a?(String) && v.length > 0 }
|
64
|
+
|
65
|
+
property :amount,
|
66
|
+
required: true,
|
67
|
+
validate: ->(v) { v.is_a?(Numeric) && v > 0 }
|
68
|
+
|
69
|
+
property :customer_email,
|
70
|
+
validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
### Version Validation
|
75
|
+
|
76
|
+
The framework automatically validates version compatibility:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
message = V2OrderMessage.new(order_id: "123", amount: 99.99)
|
80
|
+
# Header automatically gets version: 2
|
81
|
+
|
82
|
+
# Version validation happens automatically
|
83
|
+
message.validate! # Validates message + header + version compatibility
|
84
|
+
|
85
|
+
# Manual version validation
|
86
|
+
message.validate_header_version! # Checks header version matches class version
|
87
|
+
|
88
|
+
# Check version information
|
89
|
+
V2OrderMessage.version # => 2
|
90
|
+
V2OrderMessage.expected_header_version # => 2
|
91
|
+
message._sm_header.version # => 2
|
92
|
+
```
|
93
|
+
|
94
|
+
### Version Evolution Patterns
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
# Pattern 1: Additive changes (safe)
|
98
|
+
class UserMessageV1 < SmartMessage::Base
|
99
|
+
version 1
|
100
|
+
property :user_id, required: true
|
101
|
+
property :name, required: true
|
102
|
+
end
|
103
|
+
|
104
|
+
class UserMessageV2 < SmartMessage::Base
|
105
|
+
version 2
|
106
|
+
property :user_id, required: true
|
107
|
+
property :name, required: true
|
108
|
+
property :email # Optional addition - backward compatible
|
109
|
+
end
|
110
|
+
|
111
|
+
# Pattern 2: Field validation evolution
|
112
|
+
class ProductMessageV1 < SmartMessage::Base
|
113
|
+
version 1
|
114
|
+
property :product_id, required: true
|
115
|
+
property :price, required: true
|
116
|
+
end
|
117
|
+
|
118
|
+
class ProductMessageV2 < SmartMessage::Base
|
119
|
+
version 2
|
120
|
+
property :product_id,
|
121
|
+
required: true,
|
122
|
+
validate: ->(v) { v.is_a?(String) && v.match?(/\APROD-\d+\z/) }
|
123
|
+
|
124
|
+
property :price,
|
125
|
+
required: true,
|
126
|
+
validate: ->(v) { v.is_a?(Numeric) && v > 0 }
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
23
130
|
## Class-Level Description
|
24
131
|
|
25
|
-
In addition to property-level descriptions, you can add a description for the entire message class:
|
132
|
+
In addition to property-level descriptions, you can add a description for the entire message class using the `description` DSL method:
|
26
133
|
|
27
134
|
```ruby
|
28
135
|
class OrderMessage < SmartMessage::Base
|
@@ -35,15 +142,29 @@ end
|
|
35
142
|
# Access the class description
|
36
143
|
OrderMessage.description # => "Handles order processing and fulfillment workflow"
|
37
144
|
|
38
|
-
#
|
145
|
+
# Instance access to class description
|
146
|
+
order = OrderMessage.new(order_id: "123", amount: 9999)
|
147
|
+
order.description # => "Handles order processing and fulfillment workflow"
|
148
|
+
```
|
149
|
+
|
150
|
+
### Setting Descriptions
|
151
|
+
|
152
|
+
Class descriptions can be set in multiple ways:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# 1. During class definition
|
39
156
|
class PaymentMessage < SmartMessage::Base
|
157
|
+
description "Processes payment transactions"
|
40
158
|
property :payment_id
|
41
159
|
end
|
42
160
|
|
43
|
-
|
44
|
-
|
161
|
+
# 2. After class definition
|
162
|
+
class RefundMessage < SmartMessage::Base
|
163
|
+
property :refund_id
|
164
|
+
end
|
165
|
+
RefundMessage.description "Handles payment refunds and reversals"
|
45
166
|
|
46
|
-
#
|
167
|
+
# 3. Within config block
|
47
168
|
class NotificationMessage < SmartMessage::Base
|
48
169
|
config do
|
49
170
|
description "Sends notifications to users"
|
@@ -53,13 +174,41 @@ class NotificationMessage < SmartMessage::Base
|
|
53
174
|
end
|
54
175
|
```
|
55
176
|
|
177
|
+
### Default Descriptions
|
178
|
+
|
179
|
+
Classes without explicit descriptions automatically receive a default description:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
class MyMessage < SmartMessage::Base
|
183
|
+
property :data
|
184
|
+
end
|
185
|
+
|
186
|
+
MyMessage.description # => "MyMessage is a SmartMessage"
|
187
|
+
|
188
|
+
# This applies to all unnamed message classes
|
189
|
+
class SomeModule::ComplexMessage < SmartMessage::Base
|
190
|
+
property :info
|
191
|
+
end
|
192
|
+
|
193
|
+
SomeModule::ComplexMessage.description
|
194
|
+
# => "SomeModule::ComplexMessage is a SmartMessage"
|
195
|
+
```
|
196
|
+
|
197
|
+
### Use Cases
|
198
|
+
|
56
199
|
Class descriptions are useful for:
|
57
200
|
- Documenting the overall purpose of a message class
|
58
201
|
- Providing context for code generation tools
|
59
202
|
- Integration with documentation systems
|
60
203
|
- API documentation generation
|
204
|
+
- Dynamic message introspection in gateway applications
|
61
205
|
|
62
|
-
|
206
|
+
### Important Notes
|
207
|
+
|
208
|
+
- Class descriptions are not inherited by subclasses - each class maintains its own description
|
209
|
+
- Setting a description to `nil` will revert to the default description
|
210
|
+
- Descriptions are stored as strings and can include multiline content
|
211
|
+
- Both class and instance methods are available to access descriptions
|
63
212
|
|
64
213
|
## Property Options
|
65
214
|
|
@@ -181,7 +330,64 @@ msg.tags # => ['important'] (Array)
|
|
181
330
|
msg.metadata # => {} (Hash)
|
182
331
|
```
|
183
332
|
|
184
|
-
### 6. Property
|
333
|
+
### 6. Property Validation (SmartMessage Enhancement)
|
334
|
+
|
335
|
+
Add custom validation logic to ensure data integrity:
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
class ValidatedMessage < SmartMessage::Base
|
339
|
+
# Lambda validation
|
340
|
+
property :age,
|
341
|
+
validate: ->(v) { v.is_a?(Integer) && v.between?(1, 120) },
|
342
|
+
validation_message: "Age must be an integer between 1 and 120"
|
343
|
+
|
344
|
+
# Regex validation for email
|
345
|
+
property :email,
|
346
|
+
validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
|
347
|
+
validation_message: "Must be a valid email address"
|
348
|
+
|
349
|
+
# Array inclusion validation
|
350
|
+
property :status,
|
351
|
+
validate: ['active', 'inactive', 'pending'],
|
352
|
+
validation_message: "Status must be active, inactive, or pending"
|
353
|
+
|
354
|
+
# Range validation
|
355
|
+
property :score,
|
356
|
+
validate: (0..100),
|
357
|
+
validation_message: "Score must be between 0 and 100"
|
358
|
+
|
359
|
+
# Class type validation
|
360
|
+
property :created_at,
|
361
|
+
validate: Time,
|
362
|
+
validation_message: "Must be a Time object"
|
363
|
+
|
364
|
+
# Symbol method validation
|
365
|
+
property :username,
|
366
|
+
validate: :valid_username?,
|
367
|
+
validation_message: "Username contains invalid characters"
|
368
|
+
|
369
|
+
private
|
370
|
+
|
371
|
+
def valid_username?(value)
|
372
|
+
value.to_s.match?(/\A[a-zA-Z0-9_]+\z/)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# Validation usage
|
377
|
+
message = ValidatedMessage.new(age: 25, email: "test@example.com")
|
378
|
+
|
379
|
+
# Validate entire message
|
380
|
+
message.validate! # Raises SmartMessage::Errors::ValidationError on failure
|
381
|
+
message.valid? # Returns true/false
|
382
|
+
|
383
|
+
# Get validation errors
|
384
|
+
errors = message.validation_errors
|
385
|
+
errors.each do |error|
|
386
|
+
puts "#{error[:property]}: #{error[:message]}"
|
387
|
+
end
|
388
|
+
```
|
389
|
+
|
390
|
+
### 7. Property Descriptions (SmartMessage Enhancement)
|
185
391
|
|
186
392
|
Add human-readable descriptions to document your properties for dynamic LLM integration:
|
187
393
|
|