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
@@ -0,0 +1,477 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/07_error_handling_scenarios.rb
|
3
|
+
#
|
4
|
+
# Error Handling Example: SmartMessage Validation and Version Control
|
5
|
+
#
|
6
|
+
# This example demonstrates how SmartMessage handles various error conditions:
|
7
|
+
# 1. Missing required properties
|
8
|
+
# 2. Property validation failures
|
9
|
+
# 3. Version mismatches between publishers and subscribers
|
10
|
+
#
|
11
|
+
# These scenarios help developers understand SmartMessage's robust error handling
|
12
|
+
# and how to build resilient message-based systems.
|
13
|
+
|
14
|
+
require_relative '../lib/smart_message'
|
15
|
+
|
16
|
+
puts "=== SmartMessage Example: Error Handling Scenarios ==="
|
17
|
+
puts
|
18
|
+
|
19
|
+
# Message for testing required property validation
|
20
|
+
class UserRegistrationMessage < SmartMessage::Base
|
21
|
+
version 1
|
22
|
+
description "User registration data with strict validation requirements"
|
23
|
+
|
24
|
+
property :user_id,
|
25
|
+
required: true,
|
26
|
+
description: "Unique identifier for the new user account"
|
27
|
+
property :email,
|
28
|
+
required: true,
|
29
|
+
validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
|
30
|
+
validation_message: "Must be a valid email address",
|
31
|
+
description: "User's email address (must be valid format)"
|
32
|
+
property :age,
|
33
|
+
required: true,
|
34
|
+
validate: ->(v) { v.is_a?(Integer) && v.between?(13, 120) },
|
35
|
+
validation_message: "Age must be an integer between 13 and 120",
|
36
|
+
description: "User's age in years (13-120)"
|
37
|
+
property :username,
|
38
|
+
required: true,
|
39
|
+
validate: ->(v) { v.is_a?(String) && v.match?(/\A[a-zA-Z0-9_]{3,20}\z/) },
|
40
|
+
validation_message: "Username must be 3-20 characters, letters/numbers/underscores only",
|
41
|
+
description: "Unique username (3-20 chars, alphanumeric + underscore)"
|
42
|
+
property :subscription_type,
|
43
|
+
required: false,
|
44
|
+
validate: ['free', 'premium', 'enterprise'],
|
45
|
+
validation_message: "Subscription type must be 'free', 'premium', or 'enterprise'",
|
46
|
+
description: "User's subscription tier"
|
47
|
+
property :created_at,
|
48
|
+
default: -> { Time.now.iso8601 },
|
49
|
+
description: "Timestamp when user account was created"
|
50
|
+
|
51
|
+
config do
|
52
|
+
transport SmartMessage::Transport::StdoutTransport.new(loopback: true)
|
53
|
+
serializer SmartMessage::Serializer::JSON.new
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.process(message_header, message_payload)
|
57
|
+
user_data = JSON.parse(message_payload)
|
58
|
+
puts "✅ User registration processed: #{user_data['username']} (#{user_data['email']})"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Version 2 of the same message (for version mismatch testing)
|
63
|
+
class UserRegistrationMessageV2 < SmartMessage::Base
|
64
|
+
version 2
|
65
|
+
description "Version 2 of user registration with additional required fields"
|
66
|
+
|
67
|
+
property :user_id,
|
68
|
+
required: true,
|
69
|
+
description: "Unique identifier for the new user account"
|
70
|
+
property :email,
|
71
|
+
required: true,
|
72
|
+
validate: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
|
73
|
+
validation_message: "Must be a valid email address",
|
74
|
+
description: "User's email address (must be valid format)"
|
75
|
+
property :age,
|
76
|
+
required: true,
|
77
|
+
validate: ->(v) { v.is_a?(Integer) && v.between?(13, 120) },
|
78
|
+
validation_message: "Age must be an integer between 13 and 120",
|
79
|
+
description: "User's age in years (13-120)"
|
80
|
+
property :username,
|
81
|
+
required: true,
|
82
|
+
validate: ->(v) { v.is_a?(String) && v.match?(/\A[a-zA-Z0-9_]{3,20}\z/) },
|
83
|
+
validation_message: "Username must be 3-20 characters, letters/numbers/underscores only",
|
84
|
+
description: "Unique username (3-20 chars, alphanumeric + underscore)"
|
85
|
+
property :phone_number,
|
86
|
+
required: true, # New required field in version 2
|
87
|
+
validate: ->(v) { v.is_a?(String) && v.match?(/\A\+?[1-9]\d{1,14}\z/) },
|
88
|
+
validation_message: "Phone number must be in international format",
|
89
|
+
description: "User's phone number in international format (new in v2)"
|
90
|
+
property :subscription_type,
|
91
|
+
required: false,
|
92
|
+
validate: ['free', 'premium', 'enterprise'],
|
93
|
+
validation_message: "Subscription type must be 'free', 'premium', or 'enterprise'",
|
94
|
+
description: "User's subscription tier"
|
95
|
+
property :created_at,
|
96
|
+
default: -> { Time.now.iso8601 },
|
97
|
+
description: "Timestamp when user account was created"
|
98
|
+
|
99
|
+
config do
|
100
|
+
transport SmartMessage::Transport::StdoutTransport.new(loopback: true)
|
101
|
+
serializer SmartMessage::Serializer::JSON.new
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.process(message_header, message_payload)
|
105
|
+
user_data = JSON.parse(message_payload)
|
106
|
+
puts "✅ User registration V2 processed: #{user_data['username']} (#{user_data['email']}, #{user_data['phone_number']})"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Message for testing Hashie::Dash limitation with multiple required fields
|
111
|
+
class MultiRequiredMessage < SmartMessage::Base
|
112
|
+
description "Test message demonstrating Hashie::Dash required property limitation"
|
113
|
+
|
114
|
+
property :field_a, required: true, description: "First required field"
|
115
|
+
property :field_b, required: true, description: "Second required field"
|
116
|
+
property :field_c, required: true, description: "Third required field"
|
117
|
+
property :field_d, required: true, description: "Fourth required field"
|
118
|
+
property :optional_field, description: "Optional field for comparison"
|
119
|
+
|
120
|
+
config do
|
121
|
+
transport SmartMessage::Transport::StdoutTransport.new(loopback: true)
|
122
|
+
serializer SmartMessage::Serializer::JSON.new
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Error demonstration class
|
127
|
+
class ErrorDemonstrator
|
128
|
+
def initialize
|
129
|
+
puts "🚀 Starting Error Handling Demonstrations\n"
|
130
|
+
end
|
131
|
+
|
132
|
+
def run_all_scenarios
|
133
|
+
scenario_1_missing_required_properties
|
134
|
+
puts "\n" + "="*60 + "\n"
|
135
|
+
|
136
|
+
scenario_2_validation_failures
|
137
|
+
puts "\n" + "="*60 + "\n"
|
138
|
+
|
139
|
+
scenario_3_version_mismatches
|
140
|
+
puts "\n" + "="*60 + "\n"
|
141
|
+
|
142
|
+
scenario_4_hashie_limitation
|
143
|
+
puts "\n" + "="*60 + "\n"
|
144
|
+
|
145
|
+
puts "✨ All error scenarios demonstrated!"
|
146
|
+
puts "\nKey Takeaways:"
|
147
|
+
puts "• SmartMessage validates required properties at creation time"
|
148
|
+
puts "• Custom validation rules provide detailed error messages"
|
149
|
+
puts "• Version mismatches are detected during message processing"
|
150
|
+
puts "• Hashie::Dash limitation: only reports first missing required property"
|
151
|
+
puts "• Proper error handling ensures system resilience"
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def scenario_1_missing_required_properties
|
157
|
+
puts "📋 SCENARIO 1: Missing Required Properties"
|
158
|
+
puts "="*50
|
159
|
+
puts
|
160
|
+
|
161
|
+
puts "Attempting to create UserRegistrationMessage with missing required fields..."
|
162
|
+
puts
|
163
|
+
|
164
|
+
# Test Case 1: Missing user_id
|
165
|
+
puts "🔴 Test Case 1A: Missing user_id (required field)"
|
166
|
+
begin
|
167
|
+
missing_user_id = UserRegistrationMessage.new(
|
168
|
+
email: "john@example.com",
|
169
|
+
age: 25,
|
170
|
+
username: "johndoe"
|
171
|
+
)
|
172
|
+
puts "❌ ERROR: Should have failed but didn't!"
|
173
|
+
rescue => e
|
174
|
+
puts "✅ Expected error caught: #{e.class.name}"
|
175
|
+
puts " Message: #{e.message}"
|
176
|
+
end
|
177
|
+
puts
|
178
|
+
|
179
|
+
# Test Case 2: Missing multiple required fields
|
180
|
+
puts "🔴 Test Case 1B: Missing multiple required fields (email, age)"
|
181
|
+
begin
|
182
|
+
missing_multiple = UserRegistrationMessage.new(
|
183
|
+
user_id: "USER-123",
|
184
|
+
username: "johndoe"
|
185
|
+
)
|
186
|
+
puts "❌ ERROR: Should have failed but didn't!"
|
187
|
+
rescue => e
|
188
|
+
puts "✅ Expected error caught: #{e.class.name}"
|
189
|
+
puts " Message: #{e.message}"
|
190
|
+
end
|
191
|
+
puts
|
192
|
+
|
193
|
+
# Test Case 3: Valid message (should work)
|
194
|
+
puts "🟢 Test Case 1C: All required fields provided (should succeed)"
|
195
|
+
begin
|
196
|
+
valid_user = UserRegistrationMessage.new(
|
197
|
+
user_id: "USER-123",
|
198
|
+
email: "john@example.com",
|
199
|
+
age: 25,
|
200
|
+
username: "johndoe"
|
201
|
+
)
|
202
|
+
puts "✅ Message created successfully"
|
203
|
+
puts " User: #{valid_user.username} (#{valid_user.email})"
|
204
|
+
|
205
|
+
# Subscribe and publish to test processing
|
206
|
+
UserRegistrationMessage.subscribe
|
207
|
+
valid_user.publish
|
208
|
+
rescue => e
|
209
|
+
puts "❌ Unexpected error: #{e.class.name}: #{e.message}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def scenario_2_validation_failures
|
214
|
+
puts "📋 SCENARIO 2: Property Validation Failures"
|
215
|
+
puts "="*50
|
216
|
+
puts
|
217
|
+
|
218
|
+
puts "Testing custom validation rules..."
|
219
|
+
puts "Note: SmartMessage validates properties when validate! is called explicitly"
|
220
|
+
puts " or automatically during publish operations."
|
221
|
+
puts
|
222
|
+
|
223
|
+
# Test Case 1: Invalid email format
|
224
|
+
puts "🔴 Test Case 2A: Invalid email format"
|
225
|
+
begin
|
226
|
+
invalid_email = UserRegistrationMessage.new(
|
227
|
+
user_id: "USER-124",
|
228
|
+
email: "not-an-email", # Invalid format
|
229
|
+
age: 25,
|
230
|
+
username: "janedoe"
|
231
|
+
)
|
232
|
+
puts " Message created, now validating..."
|
233
|
+
invalid_email.validate! # Explicitly trigger validation
|
234
|
+
puts "❌ ERROR: Should have failed validation but didn't!"
|
235
|
+
rescue => e
|
236
|
+
puts "✅ Expected validation error caught: #{e.class.name}"
|
237
|
+
puts " Message: #{e.message}"
|
238
|
+
end
|
239
|
+
puts
|
240
|
+
|
241
|
+
# Test Case 2: Invalid age (too young)
|
242
|
+
puts "🔴 Test Case 2B: Invalid age (too young)"
|
243
|
+
begin
|
244
|
+
invalid_age = UserRegistrationMessage.new(
|
245
|
+
user_id: "USER-125",
|
246
|
+
email: "kid@example.com",
|
247
|
+
age: 10, # Too young (< 13)
|
248
|
+
username: "kiduser"
|
249
|
+
)
|
250
|
+
puts " Message created, now validating..."
|
251
|
+
invalid_age.validate! # Explicitly trigger validation
|
252
|
+
puts "❌ ERROR: Should have failed validation but didn't!"
|
253
|
+
rescue => e
|
254
|
+
puts "✅ Expected validation error caught: #{e.class.name}"
|
255
|
+
puts " Message: #{e.message}"
|
256
|
+
end
|
257
|
+
puts
|
258
|
+
|
259
|
+
# Test Case 3: Invalid username format
|
260
|
+
puts "🔴 Test Case 2C: Invalid username (special characters)"
|
261
|
+
begin
|
262
|
+
invalid_username = UserRegistrationMessage.new(
|
263
|
+
user_id: "USER-126",
|
264
|
+
email: "user@example.com",
|
265
|
+
age: 30,
|
266
|
+
username: "user@123!" # Contains invalid characters
|
267
|
+
)
|
268
|
+
puts " Message created, now validating..."
|
269
|
+
invalid_username.validate! # Explicitly trigger validation
|
270
|
+
puts "❌ ERROR: Should have failed validation but didn't!"
|
271
|
+
rescue => e
|
272
|
+
puts "✅ Expected validation error caught: #{e.class.name}"
|
273
|
+
puts " Message: #{e.message}"
|
274
|
+
end
|
275
|
+
puts
|
276
|
+
|
277
|
+
# Test Case 4: Invalid subscription type
|
278
|
+
puts "🔴 Test Case 2D: Invalid subscription type"
|
279
|
+
begin
|
280
|
+
invalid_subscription = UserRegistrationMessage.new(
|
281
|
+
user_id: "USER-127",
|
282
|
+
email: "user@example.com",
|
283
|
+
age: 30,
|
284
|
+
username: "validuser",
|
285
|
+
subscription_type: "platinum" # Not in allowed list
|
286
|
+
)
|
287
|
+
puts " Message created, now validating..."
|
288
|
+
invalid_subscription.validate! # Explicitly trigger validation
|
289
|
+
puts "❌ ERROR: Should have failed validation but didn't!"
|
290
|
+
rescue => e
|
291
|
+
puts "✅ Expected validation error caught: #{e.class.name}"
|
292
|
+
puts " Message: #{e.message}"
|
293
|
+
end
|
294
|
+
puts
|
295
|
+
|
296
|
+
# Test Case 5: All validations pass
|
297
|
+
puts "🟢 Test Case 2E: All validations pass (should succeed)"
|
298
|
+
begin
|
299
|
+
valid_user = UserRegistrationMessage.new(
|
300
|
+
user_id: "USER-128",
|
301
|
+
email: "valid@example.com",
|
302
|
+
age: 25,
|
303
|
+
username: "validuser123",
|
304
|
+
subscription_type: "premium"
|
305
|
+
)
|
306
|
+
puts " Message created, now validating..."
|
307
|
+
valid_user.validate! # Explicitly trigger validation
|
308
|
+
puts "✅ All validations passed successfully"
|
309
|
+
puts " User: #{valid_user.username} (#{valid_user.subscription_type} plan)"
|
310
|
+
|
311
|
+
valid_user.publish
|
312
|
+
rescue => e
|
313
|
+
puts "❌ Unexpected error: #{e.class.name}: #{e.message}"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def scenario_3_version_mismatches
|
318
|
+
puts "📋 SCENARIO 3: Version Mismatches"
|
319
|
+
puts "="*50
|
320
|
+
puts
|
321
|
+
|
322
|
+
puts "Testing version compatibility between publishers and subscribers..."
|
323
|
+
puts
|
324
|
+
|
325
|
+
# Subscribe to V1 messages
|
326
|
+
puts "🔧 Setting up V1 subscriber..."
|
327
|
+
UserRegistrationMessage.subscribe
|
328
|
+
puts
|
329
|
+
|
330
|
+
# Subscribe to V2 messages
|
331
|
+
puts "🔧 Setting up V2 subscriber..."
|
332
|
+
UserRegistrationMessageV2.subscribe
|
333
|
+
puts
|
334
|
+
|
335
|
+
# Test Case 1: V1 message to V1 subscriber (should work)
|
336
|
+
puts "🟢 Test Case 3A: V1 message → V1 subscriber (compatible)"
|
337
|
+
begin
|
338
|
+
v1_message = UserRegistrationMessage.new(
|
339
|
+
user_id: "USER-V1-001",
|
340
|
+
email: "v1user@example.com",
|
341
|
+
age: 28,
|
342
|
+
username: "v1user"
|
343
|
+
)
|
344
|
+
puts "✅ V1 Message created (version #{v1_message._sm_header.version})"
|
345
|
+
v1_message.publish
|
346
|
+
rescue => e
|
347
|
+
puts "❌ Unexpected error: #{e.class.name}: #{e.message}"
|
348
|
+
end
|
349
|
+
puts
|
350
|
+
|
351
|
+
# Test Case 2: V2 message to V2 subscriber (should work)
|
352
|
+
puts "🟢 Test Case 3B: V2 message → V2 subscriber (compatible)"
|
353
|
+
begin
|
354
|
+
v2_message = UserRegistrationMessageV2.new(
|
355
|
+
user_id: "USER-V2-001",
|
356
|
+
email: "v2user@example.com",
|
357
|
+
age: 32,
|
358
|
+
username: "v2user",
|
359
|
+
phone_number: "+1234567890"
|
360
|
+
)
|
361
|
+
puts "✅ V2 Message created (version #{v2_message._sm_header.version})"
|
362
|
+
v2_message.publish
|
363
|
+
rescue => e
|
364
|
+
puts "❌ Unexpected error: #{e.class.name}: #{e.message}"
|
365
|
+
end
|
366
|
+
puts
|
367
|
+
|
368
|
+
# Test Case 3: Demonstrate version mismatch handling
|
369
|
+
puts "🔴 Test Case 3C: Version mismatch demonstration"
|
370
|
+
puts " Creating a message with manually modified version header..."
|
371
|
+
begin
|
372
|
+
# Create a V1 message but manually change its version
|
373
|
+
version_mismatch_message = UserRegistrationMessage.new(
|
374
|
+
user_id: "USER-MISMATCH-001",
|
375
|
+
email: "mismatch@example.com",
|
376
|
+
age: 35,
|
377
|
+
username: "mismatchuser"
|
378
|
+
)
|
379
|
+
|
380
|
+
puts "✅ Original message created with version #{version_mismatch_message._sm_header.version}"
|
381
|
+
|
382
|
+
# Manually modify the header version to simulate a mismatch
|
383
|
+
version_mismatch_message._sm_header.version = 99
|
384
|
+
puts "🔧 Manually changed header version to #{version_mismatch_message._sm_header.version}"
|
385
|
+
|
386
|
+
# Try to validate - this should catch the version mismatch
|
387
|
+
puts "🔍 Attempting to validate message with mismatched version..."
|
388
|
+
version_mismatch_message.validate!
|
389
|
+
|
390
|
+
puts "❌ ERROR: Version mismatch should have been detected!"
|
391
|
+
rescue => e
|
392
|
+
puts "✅ Expected version mismatch error caught: #{e.class.name}"
|
393
|
+
puts " Message: #{e.message}"
|
394
|
+
end
|
395
|
+
puts
|
396
|
+
|
397
|
+
# Test Case 4: Show version information
|
398
|
+
puts "📊 Test Case 3D: Version information display"
|
399
|
+
v1_msg = UserRegistrationMessage.new(user_id: "INFO-V1", email: "info@example.com", age: 25, username: "infouser")
|
400
|
+
v2_msg = UserRegistrationMessageV2.new(user_id: "INFO-V2", email: "info@example.com", age: 25, username: "infouser", phone_number: "+1234567890")
|
401
|
+
|
402
|
+
puts "📋 Version Information:"
|
403
|
+
puts " UserRegistrationMessage (V1):"
|
404
|
+
puts " Class version: #{UserRegistrationMessage.version}"
|
405
|
+
puts " Expected header version: #{UserRegistrationMessage.expected_header_version}"
|
406
|
+
puts " Instance header version: #{v1_msg._sm_header.version}"
|
407
|
+
puts
|
408
|
+
puts " UserRegistrationMessageV2 (V2):"
|
409
|
+
puts " Class version: #{UserRegistrationMessageV2.version}"
|
410
|
+
puts " Expected header version: #{UserRegistrationMessageV2.expected_header_version}"
|
411
|
+
puts " Instance header version: #{v2_msg._sm_header.version}"
|
412
|
+
end
|
413
|
+
|
414
|
+
def scenario_4_hashie_limitation
|
415
|
+
puts "📋 SCENARIO 4: Hashie::Dash Limitation with Multiple Missing Required Properties"
|
416
|
+
puts "="*80
|
417
|
+
puts
|
418
|
+
|
419
|
+
puts "Demonstrating a limitation in Hashie::Dash (SmartMessage's base class)..."
|
420
|
+
puts "When multiple required properties are missing, only the FIRST one is reported."
|
421
|
+
puts
|
422
|
+
|
423
|
+
puts "🔴 Test Case 4A: All 4 required fields missing (field_a, field_b, field_c, field_d)"
|
424
|
+
puts "Expected: Ideally should report all missing fields"
|
425
|
+
puts "Actual Hashie::Dash behavior:"
|
426
|
+
begin
|
427
|
+
message = MultiRequiredMessage.new(optional_field: "present")
|
428
|
+
puts "❌ ERROR: Should have failed but didn't!"
|
429
|
+
rescue => e
|
430
|
+
puts "✅ Error caught: #{e.class.name}"
|
431
|
+
puts " Message: #{e.message}"
|
432
|
+
puts " 📊 Analysis: Only 'field_a' is reported, despite 3 other missing required fields"
|
433
|
+
end
|
434
|
+
puts
|
435
|
+
|
436
|
+
puts "🔴 Test Case 4B: Incremental discovery (fixing one field at a time)"
|
437
|
+
test_data = [
|
438
|
+
{ name: "Provide field_a only", data: { field_a: "value_a", optional_field: "test" } },
|
439
|
+
{ name: "Provide field_a + field_b", data: { field_a: "value_a", field_b: "value_b", optional_field: "test" } },
|
440
|
+
{ name: "Provide field_a + field_b + field_c", data: { field_a: "value_a", field_b: "value_b", field_c: "value_c", optional_field: "test" } }
|
441
|
+
]
|
442
|
+
|
443
|
+
test_data.each_with_index do |test_case, index|
|
444
|
+
puts " #{index + 1}. #{test_case[:name]}:"
|
445
|
+
begin
|
446
|
+
message = MultiRequiredMessage.new(test_case[:data])
|
447
|
+
puts " ✅ Success: Message created"
|
448
|
+
rescue => e
|
449
|
+
field_name = e.message.match(/property '([^']+)'/)[1] rescue 'unknown'
|
450
|
+
puts " ❌ Still missing: #{field_name}"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
puts
|
455
|
+
puts "📊 LIMITATION SUMMARY:"
|
456
|
+
puts "="*50
|
457
|
+
puts "🔍 Issue: Hashie::Dash stops validation at the first missing required property"
|
458
|
+
puts "📋 Impact: Poor developer experience - must fix errors one at a time"
|
459
|
+
puts "⚡ UX Problem: In forms with many required fields, users see only one error at a time"
|
460
|
+
puts
|
461
|
+
puts "💡 Potential Solutions:"
|
462
|
+
puts "• Override Hashie::Dash initialization to collect ALL missing required fields"
|
463
|
+
puts "• Add SmartMessage enhancement: validate_all_required! method"
|
464
|
+
puts "• Provide aggregate error messages listing all missing properties"
|
465
|
+
puts "• Use custom validation that reports multiple missing fields simultaneously"
|
466
|
+
puts
|
467
|
+
puts "🚀 Example of better error message:"
|
468
|
+
puts ' "Missing required properties: field_a, field_b, field_c, field_d"'
|
469
|
+
puts " vs current: \"The property 'field_a' is required\""
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# Run the demo if this file is executed directly
|
474
|
+
if __FILE__ == $0
|
475
|
+
demo = ErrorDemonstrator.new
|
476
|
+
demo.run_all_scenarios
|
477
|
+
end
|
@@ -73,6 +73,9 @@ class BotChatAgent < BaseAgent
|
|
73
73
|
return unless @active_rooms.include?(chat_data['room_id'])
|
74
74
|
return if chat_data['sender_id'] == @agent_id
|
75
75
|
|
76
|
+
# Don't respond to other bots to avoid infinite loops
|
77
|
+
return if chat_data['message_type'] == 'bot'
|
78
|
+
|
76
79
|
# Log the message
|
77
80
|
log_display("👁️ [#{chat_data['room_id']}] #{chat_data['sender_name']}: #{chat_data['content']}")
|
78
81
|
|
@@ -80,7 +83,7 @@ class BotChatAgent < BaseAgent
|
|
80
83
|
if chat_data['content'].start_with?('/')
|
81
84
|
handle_inline_command(chat_data)
|
82
85
|
else
|
83
|
-
# Respond to certain keywords
|
86
|
+
# Respond to certain keywords from human users only
|
84
87
|
respond_to_keywords(chat_data)
|
85
88
|
end
|
86
89
|
end
|
@@ -102,15 +102,26 @@ SmartMessage::Transport.register(:file, FileTransport)
|
|
102
102
|
|
103
103
|
# Define the Chat Message
|
104
104
|
class ChatMessage < SmartMessage::Base
|
105
|
-
|
106
|
-
|
107
|
-
property :
|
108
|
-
|
109
|
-
property :
|
110
|
-
|
111
|
-
property :
|
112
|
-
|
113
|
-
property :
|
105
|
+
description "Chat messages for tmux-based multi-pane chat demonstration"
|
106
|
+
|
107
|
+
property :message_id,
|
108
|
+
description: "Unique identifier for this chat message"
|
109
|
+
property :room_id,
|
110
|
+
description: "Chat room identifier for message routing"
|
111
|
+
property :sender_id,
|
112
|
+
description: "Unique ID of the user or bot sending the message"
|
113
|
+
property :sender_name,
|
114
|
+
description: "Display name of the message sender"
|
115
|
+
property :content,
|
116
|
+
description: "The actual text content of the chat message"
|
117
|
+
property :message_type,
|
118
|
+
description: "Message type: 'user', 'bot', or 'system'"
|
119
|
+
property :timestamp,
|
120
|
+
description: "ISO8601 timestamp when message was sent"
|
121
|
+
property :mentions,
|
122
|
+
description: "Array of user IDs mentioned in the message"
|
123
|
+
property :metadata,
|
124
|
+
description: "Additional message metadata for tmux display"
|
114
125
|
|
115
126
|
config do
|
116
127
|
transport SmartMessage::Transport.create(:file)
|
@@ -124,13 +135,22 @@ end
|
|
124
135
|
|
125
136
|
# Define Bot Command Message
|
126
137
|
class BotCommandMessage < SmartMessage::Base
|
127
|
-
|
128
|
-
|
129
|
-
property :
|
130
|
-
|
131
|
-
property :
|
132
|
-
|
133
|
-
property :
|
138
|
+
description "Commands sent to chat bots in the tmux chat system"
|
139
|
+
|
140
|
+
property :command_id,
|
141
|
+
description: "Unique identifier for this bot command"
|
142
|
+
property :room_id,
|
143
|
+
description: "Chat room where the command was issued"
|
144
|
+
property :user_id,
|
145
|
+
description: "User who issued the bot command"
|
146
|
+
property :user_name,
|
147
|
+
description: "Display name of the user issuing the command"
|
148
|
+
property :command,
|
149
|
+
description: "Bot command name (help, joke, weather, etc.)"
|
150
|
+
property :parameters,
|
151
|
+
description: "Array of parameters for the bot command"
|
152
|
+
property :timestamp,
|
153
|
+
description: "ISO8601 timestamp when command was issued"
|
134
154
|
|
135
155
|
config do
|
136
156
|
transport SmartMessage::Transport.create(:file)
|
@@ -144,12 +164,20 @@ end
|
|
144
164
|
|
145
165
|
# Define System Notification Message
|
146
166
|
class SystemNotificationMessage < SmartMessage::Base
|
147
|
-
|
148
|
-
|
149
|
-
property :
|
150
|
-
|
151
|
-
property :
|
152
|
-
|
167
|
+
description "System notifications for tmux chat room events and status updates"
|
168
|
+
|
169
|
+
property :notification_id,
|
170
|
+
description: "Unique identifier for this system notification"
|
171
|
+
property :room_id,
|
172
|
+
description: "Chat room affected by this notification"
|
173
|
+
property :notification_type,
|
174
|
+
description: "Type of notification (user_joined, user_left, etc.)"
|
175
|
+
property :content,
|
176
|
+
description: "Human-readable description of the system event"
|
177
|
+
property :timestamp,
|
178
|
+
description: "ISO8601 timestamp when the event occurred"
|
179
|
+
property :metadata,
|
180
|
+
description: "Additional system event metadata for tmux display"
|
153
181
|
|
154
182
|
config do
|
155
183
|
transport SmartMessage::Transport.create(:file)
|