smart_message 0.0.4 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de5fce8e9cb793850301df5fdb9ad8a6954cf3a481cadc2deb625c373706723a
4
- data.tar.gz: 0cc8067011377f0d574bc7f9bf1168ac2176fad7c7981cbd43f708e622a98fa0
3
+ metadata.gz: eac068dc19706e57d2c66a1dfe36981fbac33e6d7b180fe0429f81d874727cd4
4
+ data.tar.gz: 91585bc79637c4feab1256716519c0c3c7aa36c2b313ed09ebee7788b11d3f1a
5
5
  SHA512:
6
- metadata.gz: bf7d1fc90d41bdbd3a8661f1f3e294915d29f267a68c13869926a4f829a00338f3878b03e14f26a7ccc9b248dc55b566e9988cb67aca7f6de596b94a51b5e885
7
- data.tar.gz: 51c784d4183fe12862ff9250389a57233718604442e50303cdb941265971978e291fe7c3c1fec92509e6fa32bcc67b6b2fe8813b3cc1d06fa9d1f16645b181f3
6
+ metadata.gz: a0c0ead05e3cb104873e5cfa53cfc182ee06c6fd2f2e56fe02c0d64a4313dc4356b84d3b5be251e5438e6e381812848f6f587b005a315282204d8160f785f87b
7
+ data.tar.gz: 3a84537cca151b3b8f3efa16b9ad8591811be19ab5547013fe9f33d7df58474e5db2a74e85e669fb95c17e5b375dd2d889b3a3051b6ded765120dd208246c26a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,124 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.6] 2025-08-19
11
+
12
+ ### Added
13
+ - **Entity-Aware Message Filtering**: Advanced subscription filtering system enabling precise message routing
14
+ - New subscription filter parameters: `broadcast:`, `to:`, `from:` for `subscribe` method
15
+ - Filter by broadcast messages only: `MyMessage.subscribe(broadcast: true)`
16
+ - Filter by destination entity: `MyMessage.subscribe(to: 'service-name')`
17
+ - Filter by sender entity: `MyMessage.subscribe(from: 'sender-service')`
18
+ - Support for array-based filters: `subscribe(from: ['admin', 'system'])`
19
+ - Combined filters with OR logic: `subscribe(broadcast: true, to: 'my-service')`
20
+ - String-to-array automatic conversion for convenience: `subscribe(to: 'service')` becomes `['service']`
21
+ - Full backward compatibility - existing subscriptions work unchanged
22
+ - **Real-Time Header Synchronization**: Message headers now update automatically when addressing fields change
23
+ - Instance-level `from()`, `to()`, `reply_to()` methods now update both instance variables and message headers
24
+ - Enables filtering to work correctly when addressing is changed after message creation
25
+ - Reset methods (`reset_from`, `reset_to`, `reset_reply_to`) also update headers
26
+ - Critical for entity-aware filtering functionality
27
+ - **Enhanced Example Programs**
28
+ - New comprehensive filtering example: `examples/08_entity_addressing_with_filtering.rb`
29
+ - Demonstrates all filtering capabilities with real-world microservice scenarios
30
+ - Improved timing with sleep statements for better output visualization
31
+ - Updated basic addressing example: `examples/08_entity_addressing_basic.rb`
32
+ - Both examples now show clear interleaved publish/receive output flow
33
+
34
+ ### Changed
35
+ - **Message Subscription Architecture**: Subscription storage updated to support filtering
36
+ - Dispatcher now stores subscription objects with filter criteria instead of simple method strings
37
+ - Subscription objects contain `{process_method: string, filters: hash}` structure
38
+ - All transport implementations updated to pass filter options through subscription chain
39
+ - RedisTransport updated to accept new filter_options parameter
40
+ - **Header Addressing Behavior**: Message headers now reflect real-time addressing changes
41
+ - **BREAKING**: Headers are no longer immutable after creation - they update when addressing methods are called
42
+ - This change enables entity-aware filtering to work correctly with dynamic addressing
43
+ - Previous behavior kept original addressing in headers while instance methods showed new values
44
+
45
+ ### Fixed
46
+ - **Message Filtering Logic**: Resolved critical filtering implementation issues
47
+ - Fixed message_matches_filters? method to properly evaluate filter criteria
48
+ - Corrected AND/OR logic for combined broadcast and entity-specific filters
49
+ - Ensured filtering works with both class-level and instance-level addressing
50
+ - **Test Suite Compatibility**: Updated all tests to work with new subscription structure
51
+ - Fixed ProcHandlerTest to expect subscription objects instead of string arrays
52
+ - Updated TransportTest to handle new subscription object format
53
+ - Fixed AddressingTest to reflect new real-time header update behavior
54
+ - All existing functionality preserved with full backward compatibility
55
+
56
+ ### Enhanced
57
+ - **Message Processing Performance**: Filtering happens at subscription level, reducing processing overhead
58
+ - Messages not matching filter criteria are ignored by handlers, improving efficiency
59
+ - Enables microservices to process only relevant messages
60
+ - Reduces unnecessary handler invocations and processing cycles
61
+ - **Developer Experience**: Clear examples demonstrate filtering benefits and usage patterns
62
+ - Examples show both what DOES and DOESN'T get processed
63
+ - Demonstrates gateway patterns, admin message handling, and microservice communication
64
+ - Educational value enhanced with comprehensive console output and timing
65
+
66
+ ### Documentation
67
+ - **Entity-Aware Filtering Guide**: Comprehensive examples showing all filtering capabilities
68
+ - Broadcast vs directed message handling patterns
69
+ - Admin/monitoring system integration examples
70
+ - Request-reply routing with filters
71
+ - Gateway pattern implementations with dynamic routing
72
+ - Best practices for microservice message filtering
73
+ - **Breaking Change Documentation**: Clear explanation of header behavior changes
74
+ - Migration guide for applications depending on immutable headers
75
+ - Benefits of real-time header updates for filtering functionality
76
+
77
+ ## [0.0.5] 2025-08-18
78
+
79
+ ### Added
80
+ - **Enhanced Description DSL**: Extended class-level `description` method with automatic defaults
81
+ - Classes without explicit descriptions now automatically get `"ClassName is a SmartMessage"` default
82
+ - Instance method `message.description` provides access to class description
83
+ - Backward compatible with existing description implementations
84
+ - Improves developer experience by eliminating nil description values
85
+ - **Comprehensive Error Handling Example** (`examples/07_error_handling_scenarios.rb`)
86
+ - Demonstrates missing required property validation with detailed error messages
87
+ - Shows custom property validation failures with multiple validation types (regex, range, enum, proc)
88
+ - Illustrates version mismatch detection between message classes and headers
89
+ - **NEW**: Documents Hashie::Dash limitation where only first missing required property is reported
90
+ - Provides educational content about SmartMessage's robust validation framework
91
+ - Includes suggestions for potential improvements to multi-property validation
92
+ - **Complete Property Documentation**: All examples now include comprehensive descriptions
93
+ - Message class descriptions added to all example programs (01-06 + tmux_chat)
94
+ - Individual property descriptions added with detailed explanations of purpose and format
95
+ - Enhanced readability and educational value of all demonstration code
96
+ - Consistent documentation patterns across all messaging scenarios
97
+
98
+ ### Fixed
99
+ - **Critical: Infinite Loop Bug in Chat Examples**
100
+ - Fixed infinite message loops in `examples/03_many_to_many_chat.rb` and `examples/tmux_chat/bot_agent.rb`
101
+ - **Root Cause**: Bots responding to other bots' messages containing trigger keywords ("hello", "hi")
102
+ - **Solution**: Added `return if chat_data['message_type'] == 'bot'` to prevent bot-to-bot responses
103
+ - Examples now terminate properly without requiring manual intervention
104
+ - Maintains proper bot functionality for human interactions and explicit commands
105
+ - **Example Termination**: Chat examples now shutdown cleanly without hanging indefinitely
106
+
107
+ ### Changed
108
+ - **Enhanced Documentation Coverage**
109
+ - Updated README.md with comprehensive description DSL documentation
110
+ - Enhanced `docs/properties.md` with updated class description behavior and default handling
111
+ - Updated `docs/getting-started.md`, `docs/architecture.md`, and `docs/examples.md` with description examples
112
+ - All documentation now demonstrates proper message and property description usage
113
+ - **Improved Example Educational Value**
114
+ - All example programs enhanced with descriptive class and property documentation
115
+ - Better demonstration of SmartMessage's self-documenting capabilities
116
+ - Examples serve as both functional demonstrations and documentation references
117
+
118
+ ### Documentation
119
+ - **Hashie::Dash Limitation Discovery**: Documented important framework limitation in error handling example
120
+ - Only first missing required property reported during validation failures
121
+ - Provides clear explanation of incremental error discovery behavior
122
+ - Suggests potential solutions for improved multi-property validation
123
+ - **Comprehensive Description System**: Complete documentation of message documentation capabilities
124
+ - Class-level description DSL with automatic defaults
125
+ - Property-level descriptions with usage patterns
126
+ - Integration with existing SmartMessage validation and introspection systems
127
+
10
128
  ## [0.0.2] - 2025-08-17
11
129
 
12
130
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_message (0.0.4)
4
+ smart_message (0.0.6)
5
5
  activesupport
6
6
  concurrent-ruby
7
7
  hashie
data/README.md CHANGED
@@ -9,6 +9,10 @@ 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
+ - **Entity-to-Entity Addressing**: Built-in FROM/TO/REPLY_TO addressing for point-to-point and broadcast messaging patterns
13
+ - **Schema Versioning**: Built-in version management with automatic compatibility validation
14
+ - **Comprehensive Validation**: Property validation with custom error messages and automatic validation before publishing
15
+ - **Message Documentation**: Built-in documentation support for message classes and properties with automatic defaults
12
16
  - **Flexible Message Handlers**: Multiple subscription patterns - default methods, custom methods, blocks, procs, and lambdas
13
17
  - **Dual-Level Configuration**: Class and instance-level plugin overrides for gateway patterns
14
18
  - **Concurrent Processing**: Thread-safe message routing using `Concurrent::CachedThreadPool`
@@ -38,10 +42,40 @@ Or install it yourself as:
38
42
 
39
43
  ```ruby
40
44
  class OrderMessage < SmartMessage::Base
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"
45
+ # Declare schema version for compatibility tracking
46
+ version 2
47
+
48
+ # Add a description for the message class
49
+ description "Represents customer order data for processing and fulfillment"
50
+
51
+ # Configure entity addressing
52
+ from 'order-service'
53
+ to 'fulfillment-service' # Point-to-point message
54
+ reply_to 'order-service' # Responses come back here
55
+
56
+ # Required properties with validation
57
+ property :order_id,
58
+ required: true,
59
+ message: "Order ID is required",
60
+ validate: ->(v) { v.is_a?(String) && v.length > 0 },
61
+ validation_message: "Order ID must be a non-empty string",
62
+ description: "Unique order identifier"
63
+
64
+ property :customer_id,
65
+ required: true,
66
+ message: "Customer ID is required",
67
+ description: "Customer's unique ID"
68
+
69
+ property :amount,
70
+ required: true,
71
+ message: "Amount is required",
72
+ validate: ->(v) { v.is_a?(Numeric) && v > 0 },
73
+ validation_message: "Amount must be a positive number",
74
+ description: "Total order amount in dollars"
75
+
76
+ property :items,
77
+ default: [],
78
+ description: "Array of ordered items"
45
79
 
46
80
  # Configure transport and serializer at class level
47
81
  config do
@@ -74,7 +108,7 @@ end
74
108
  ### 2. Publish Messages
75
109
 
76
110
  ```ruby
77
- # Create and publish a message
111
+ # Create and publish a message (automatically validated before publishing)
78
112
  order = OrderMessage.new(
79
113
  order_id: "ORD-123",
80
114
  customer_id: "CUST-456",
@@ -82,7 +116,16 @@ order = OrderMessage.new(
82
116
  items: ["Widget A", "Widget B"]
83
117
  )
84
118
 
85
- order.publish
119
+ # Message is automatically validated before publishing
120
+ order.publish # Validates all properties, header, and version compatibility
121
+
122
+ # Or validate manually
123
+ if order.valid?
124
+ order.publish
125
+ else
126
+ errors = order.validation_errors
127
+ errors.each { |err| puts "#{err[:property]}: #{err[:message]}" }
128
+ end
86
129
  ```
87
130
 
88
131
  ### 3. Subscribe to Messages
@@ -116,6 +159,50 @@ end
116
159
  OrderMessage.subscribe(audit_handler)
117
160
  ```
118
161
 
162
+ ### 4. Entity Addressing
163
+
164
+ SmartMessage supports entity-to-entity addressing with FROM/TO/REPLY_TO fields for advanced message routing:
165
+
166
+ ```ruby
167
+ # Point-to-point messaging
168
+ class PaymentMessage < SmartMessage::Base
169
+ version 1
170
+ from 'payment-service' # Required: sender identity
171
+ to 'bank-gateway' # Optional: specific recipient
172
+ reply_to 'payment-service' # Optional: where responses go
173
+
174
+ property :amount, required: true
175
+ property :account_id, required: true
176
+ end
177
+
178
+ # Broadcast messaging (no 'to' specified)
179
+ class SystemAnnouncementMessage < SmartMessage::Base
180
+ version 1
181
+ from 'admin-service' # Required: sender identity
182
+ # No 'to' field = broadcast to all subscribers
183
+
184
+ property :message, required: true
185
+ property :priority, default: 'normal'
186
+ end
187
+
188
+ # Instance-level addressing override
189
+ payment = PaymentMessage.new(amount: 100.00, account_id: "ACCT-123")
190
+ payment.to('backup-gateway') # Override destination for this instance
191
+ payment.publish
192
+
193
+ # Access addressing information
194
+ puts payment._sm_header.from # => 'payment-service'
195
+ puts payment._sm_header.to # => 'backup-gateway'
196
+ puts payment._sm_header.reply_to # => 'payment-service'
197
+ ```
198
+
199
+ #### Messaging Patterns Supported
200
+
201
+ - **Point-to-Point**: Set `to` field for direct entity targeting
202
+ - **Broadcast**: Omit `to` field (nil) for message broadcast to all subscribers
203
+ - **Request-Reply**: Use `reply_to` field to specify response routing
204
+ - **Gateway Patterns**: Override addressing at instance level for message forwarding
205
+
119
206
  ## Architecture
120
207
 
121
208
  ### Core Components
@@ -286,14 +373,213 @@ end
286
373
  ## Message Lifecycle
287
374
 
288
375
  1. **Definition**: Create message class inheriting from `SmartMessage::Base`
289
- 2. **Configuration**: Set transport, serializer, and logger plugins
290
- 3. **Publishing**: Message instance is encoded and sent through transport
291
- 4. **Subscription**: Message classes register handlers with dispatcher for processing
376
+ 2. **Configuration**: Set transport, serializer, logger plugins, and entity addressing (from/to/reply_to)
377
+ 3. **Validation**: Messages are automatically validated before publishing (properties, header, addressing, version compatibility)
378
+ 4. **Publishing**: Message instance is encoded with addressing metadata and sent through transport
379
+ 5. **Subscription**: Message classes register handlers with dispatcher for processing
292
380
  - Default handlers (`self.process` method)
293
381
  - Custom method handlers (`"ClassName.method_name"`)
294
382
  - Block handlers (`subscribe do |h,p|...end`)
295
383
  - Proc/Lambda handlers (`subscribe(proc {...})`)
296
- 5. **Processing**: Received messages are decoded and routed to registered handlers
384
+ 6. **Routing**: Dispatcher uses addressing metadata to route messages (point-to-point vs broadcast)
385
+ 7. **Processing**: Received messages are decoded and routed to registered handlers
386
+
387
+ ## Schema Versioning and Validation
388
+
389
+ SmartMessage includes comprehensive validation and versioning capabilities to ensure message integrity and schema evolution support.
390
+
391
+ ### Version Declaration
392
+
393
+ Declare your message schema version using the `version` class method:
394
+
395
+ ```ruby
396
+ class OrderMessage < SmartMessage::Base
397
+ version 2 # Schema version 2
398
+
399
+ property :order_id, required: true
400
+ property :customer_email # Added in version 2
401
+ end
402
+ ```
403
+
404
+ ### Property Validation
405
+
406
+ Properties support multiple validation types with custom error messages:
407
+
408
+ ```ruby
409
+ class UserMessage < SmartMessage::Base
410
+ version 1
411
+
412
+ # Required field validation (Hashie built-in)
413
+ property :user_id,
414
+ required: true,
415
+ message: "User ID is required and cannot be blank"
416
+
417
+ # Custom validation with lambda
418
+ property :age,
419
+ validate: ->(v) { v.is_a?(Integer) && v.between?(1, 120) },
420
+ validation_message: "Age must be an integer between 1 and 120"
421
+
422
+ # Email validation with regex
423
+ property :email,
424
+ validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
425
+ validation_message: "Must be a valid email address"
426
+
427
+ # Inclusion validation with array
428
+ property :status,
429
+ validate: ['active', 'inactive', 'pending'],
430
+ validation_message: "Status must be active, inactive, or pending"
431
+ end
432
+ ```
433
+
434
+ ### Validation Methods
435
+
436
+ All message instances include validation methods:
437
+
438
+ ```ruby
439
+ user = UserMessage.new(user_id: "123", age: 25)
440
+
441
+ # Validate entire message (properties + header + version)
442
+ user.validate! # Raises SmartMessage::Errors::ValidationError on failure
443
+ user.valid? # Returns true/false
444
+
445
+ # Get detailed validation errors
446
+ errors = user.validation_errors
447
+ errors.each do |error|
448
+ puts "#{error[:source]}.#{error[:property]}: #{error[:message]}"
449
+ # Example output:
450
+ # message.age: Age must be an integer between 1 and 120
451
+ # header.version: Header version must be a positive integer
452
+ # version_mismatch.version: Expected version 1, got: 2
453
+ end
454
+ ```
455
+
456
+ ### Automatic Validation
457
+
458
+ Messages are automatically validated during publishing:
459
+
460
+ ```ruby
461
+ # This will raise ValidationError if invalid
462
+ message = UserMessage.new(user_id: "", age: 150)
463
+ message.publish # Automatically validates before publishing
464
+ ```
465
+
466
+ ### Version Compatibility
467
+
468
+ The framework automatically validates version compatibility:
469
+
470
+ ```ruby
471
+ class V2Message < SmartMessage::Base
472
+ version 2
473
+ property :data
474
+ end
475
+
476
+ message = V2Message.new(data: "test")
477
+ # Header automatically gets version: 2
478
+
479
+ # Simulate version mismatch (e.g., from older message)
480
+ message._sm_header.version = 1
481
+ message.validate! # Raises: "V2Message expects version 2, but header has version 1"
482
+ ```
483
+
484
+ ### Supported Validation Types
485
+
486
+ - **Proc/Lambda**: `validate: ->(v) { v.length > 5 }`
487
+ - **Regexp**: `validate: /\A[a-z]+\z/`
488
+ - **Class**: `validate: String` (type checking)
489
+ - **Array**: `validate: ['red', 'green', 'blue']` (inclusion)
490
+ - **Range**: `validate: (1..100)` (range checking)
491
+ - **Symbol**: `validate: :custom_validator_method`
492
+
493
+ ## Message Documentation
494
+
495
+ SmartMessage provides built-in documentation capabilities for both message classes and their properties.
496
+
497
+ ### Class-Level Descriptions
498
+
499
+ Use the `description` DSL method to document what your message class represents:
500
+
501
+ ```ruby
502
+ class OrderMessage < SmartMessage::Base
503
+ description "Represents customer order data for processing and fulfillment"
504
+
505
+ property :order_id, required: true
506
+ property :amount, required: true
507
+ end
508
+
509
+ class UserMessage < SmartMessage::Base
510
+ description "Handles user management operations including registration and updates"
511
+
512
+ property :user_id, required: true
513
+ property :email, required: true
514
+ end
515
+
516
+ # Access descriptions
517
+ puts OrderMessage.description
518
+ # => "Represents customer order data for processing and fulfillment"
519
+
520
+ puts UserMessage.description
521
+ # => "Handles user management operations including registration and updates"
522
+
523
+ # Instance access to class description
524
+ order = OrderMessage.new(order_id: "123", amount: 99.99)
525
+ puts order.description
526
+ # => "Represents customer order data for processing and fulfillment"
527
+ ```
528
+
529
+ ### Default Descriptions
530
+
531
+ Classes without explicit descriptions automatically get a default description:
532
+
533
+ ```ruby
534
+ class MyMessage < SmartMessage::Base
535
+ property :data
536
+ end
537
+
538
+ puts MyMessage.description
539
+ # => "MyMessage is a SmartMessage"
540
+ ```
541
+
542
+ ### Property Documentation
543
+
544
+ Combine class descriptions with property descriptions for comprehensive documentation:
545
+
546
+ ```ruby
547
+ class FullyDocumented < SmartMessage::Base
548
+ description "A fully documented message class for demonstration purposes"
549
+
550
+ property :id,
551
+ description: "Unique identifier for the record"
552
+ property :name,
553
+ description: "Display name for the entity"
554
+ property :status,
555
+ description: "Current processing status",
556
+ validate: ['active', 'inactive', 'pending']
557
+ end
558
+
559
+ # Access all documentation
560
+ puts FullyDocumented.description
561
+ # => "A fully documented message class for demonstration purposes"
562
+
563
+ puts FullyDocumented.property_description(:id)
564
+ # => "Unique identifier for the record"
565
+
566
+ puts FullyDocumented.property_descriptions
567
+ # => {:id=>"Unique identifier for the record", :name=>"Display name for the entity", ...}
568
+ ```
569
+
570
+ ### Documentation in Config Blocks
571
+
572
+ You can also set descriptions within configuration blocks:
573
+
574
+ ```ruby
575
+ class ConfiguredMessage < SmartMessage::Base
576
+ config do
577
+ description "Set within config block"
578
+ transport SmartMessage::Transport.create(:stdout)
579
+ serializer SmartMessage::Serializer::JSON.new
580
+ end
581
+ end
582
+ ```
297
583
 
298
584
  ## Advanced Usage
299
585
 
@@ -353,6 +639,9 @@ puts message._sm_header.uuid
353
639
  puts message._sm_header.message_class
354
640
  puts message._sm_header.published_at
355
641
  puts message._sm_header.publisher_pid
642
+ puts message._sm_header.from
643
+ puts message._sm_header.to
644
+ puts message._sm_header.reply_to
356
645
  ```
357
646
 
358
647
  ## Development
data/docs/README.md CHANGED
@@ -16,6 +16,7 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
16
16
  ### Components
17
17
  - [SmartMessage::Base](base.md)
18
18
  - [Property System](properties.md)
19
+ - [Entity Addressing](addressing.md)
19
20
  - [Transport Layer](transports.md)
20
21
  - [Serializers](serializers.md)
21
22
  - [Logging System](logging.md)