smart_message 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +11 -7
- data/docs/README.md +5 -4
- data/docs/properties.md +471 -0
- data/lib/smart_message/base.rb +14 -0
- data/lib/smart_message/property_descriptions.rb +41 -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: 946ebe27d52785f76921ac75921c49e9a9d6532f8714c4aed3b59a6a0eb7b92d
|
4
|
+
data.tar.gz: a40e2fba5460cb900b9ac3fd19689b5a596b069c41c6b17fde7edb7013ef027f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '06813ab254720233a6136f960ddeb2f9b9e919627c8f4f8ff189a7d9749f0295d42ca8a2366021b7d037fe701db681036705b4b7442e32f1ff054c1e58e2487e'
|
7
|
+
data.tar.gz: 70d76f12b9fa4e0c2c09bfb2482fbd19acee4418fd173be6163de43e703ecc4d3b05329251bab2da5f9760603dafec2178b186ad10f5c779690160245e5f32ce
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -36,10 +36,10 @@ Or install it yourself as:
|
|
36
36
|
|
37
37
|
```ruby
|
38
38
|
class OrderMessage < SmartMessage::Base
|
39
|
-
property :order_id
|
40
|
-
property :customer_id
|
41
|
-
property :amount
|
42
|
-
property :items
|
39
|
+
property :order_id, description: "Unique order identifier"
|
40
|
+
property :customer_id, description: "Customer's unique ID"
|
41
|
+
property :amount, description: "Total order amount in dollars"
|
42
|
+
property :items, description: "Array of ordered items"
|
43
43
|
|
44
44
|
# Configure transport and serializer at class level
|
45
45
|
config do
|
@@ -255,9 +255,9 @@ puts dispatcher.subscribers
|
|
255
255
|
|
256
256
|
```ruby
|
257
257
|
class MyMessage < SmartMessage::Base
|
258
|
-
property :user_id
|
259
|
-
property :action
|
260
|
-
property :timestamp, default: -> { Time.now }
|
258
|
+
property :user_id, description: "User's unique identifier"
|
259
|
+
property :action, description: "Action performed by the user"
|
260
|
+
property :timestamp, default: -> { Time.now }, description: "When the action occurred"
|
261
261
|
end
|
262
262
|
|
263
263
|
message = MyMessage.new(user_id: 123, action: "login")
|
@@ -266,6 +266,10 @@ message = MyMessage.new(user_id: 123, action: "login")
|
|
266
266
|
puts message.user_id
|
267
267
|
puts message.fields # Returns Set of property names (excluding internal _sm_ properties)
|
268
268
|
|
269
|
+
# Access property descriptions
|
270
|
+
puts MyMessage.property_description(:user_id) # => "User's unique identifier"
|
271
|
+
puts MyMessage.property_descriptions # => Hash of all descriptions
|
272
|
+
|
269
273
|
# Access message header
|
270
274
|
puts message._sm_header.uuid
|
271
275
|
puts message._sm_header.message_class
|
data/docs/README.md
CHANGED
@@ -8,14 +8,15 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
8
8
|
- [Installation & Quick Start](getting-started.md)
|
9
9
|
- [Basic Usage Examples](examples.md)
|
10
10
|
|
11
|
-
### Core Concepts
|
11
|
+
### Core Concepts
|
12
12
|
- [Architecture Overview](architecture.md)
|
13
13
|
- [Message Lifecycle](message-lifecycle.md)
|
14
14
|
- [Plugin System](plugin-system.md)
|
15
15
|
|
16
16
|
### Components
|
17
17
|
- [SmartMessage::Base](base.md)
|
18
|
-
- [
|
18
|
+
- [Property System](properties.md)
|
19
|
+
- [Transport Layer](transports.md)
|
19
20
|
- [Serializers](serializers.md)
|
20
21
|
- [Dispatcher & Routing](dispatcher.md)
|
21
22
|
- [Message Headers](headers.md)
|
@@ -47,6 +48,6 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
47
48
|
|
48
49
|
## Version
|
49
50
|
|
50
|
-
This documentation is for SmartMessage v0.0.
|
51
|
+
This documentation is for SmartMessage v0.0.2.
|
51
52
|
|
52
|
-
For older versions, please check the git tags and corresponding documentation.
|
53
|
+
For older versions, please check the git tags and corresponding documentation.
|
data/docs/properties.md
ADDED
@@ -0,0 +1,471 @@
|
|
1
|
+
# SmartMessage Property System
|
2
|
+
|
3
|
+
The SmartMessage property system builds on Hashie::Dash to provide a robust, declarative way to define message attributes. This document covers all available property options and features.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
- [Basic Property Definition](#basic-property-definition)
|
7
|
+
- [Class-Level Description](#class-level-description)
|
8
|
+
- [Property Options](#property-options)
|
9
|
+
- [Accessing Property Information](#accessing-property-information)
|
10
|
+
- [Hashie Extensions](#hashie-extensions)
|
11
|
+
- [Examples](#examples)
|
12
|
+
|
13
|
+
## Basic Property Definition
|
14
|
+
|
15
|
+
Properties are defined using the `property` method in your message class:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class MyMessage < SmartMessage::Base
|
19
|
+
property :field_name
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
## Class-Level Description
|
24
|
+
|
25
|
+
In addition to property-level descriptions, you can add a description for the entire message class:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class OrderMessage < SmartMessage::Base
|
29
|
+
description "Handles order processing and fulfillment workflow"
|
30
|
+
|
31
|
+
property :order_id, description: "Unique order identifier"
|
32
|
+
property :amount, description: "Total amount in cents"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Access the class description
|
36
|
+
OrderMessage.description # => "Handles order processing and fulfillment workflow"
|
37
|
+
|
38
|
+
# Class descriptions can also be set after class definition
|
39
|
+
class PaymentMessage < SmartMessage::Base
|
40
|
+
property :payment_id
|
41
|
+
end
|
42
|
+
|
43
|
+
PaymentMessage.description "Processes payment transactions"
|
44
|
+
PaymentMessage.description # => "Processes payment transactions"
|
45
|
+
|
46
|
+
# Can be set within config block
|
47
|
+
class NotificationMessage < SmartMessage::Base
|
48
|
+
config do
|
49
|
+
description "Sends notifications to users"
|
50
|
+
transport MyTransport.new
|
51
|
+
serializer MySerializer.new
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
Class descriptions are useful for:
|
57
|
+
- Documenting the overall purpose of a message class
|
58
|
+
- Providing context for code generation tools
|
59
|
+
- Integration with documentation systems
|
60
|
+
- API documentation generation
|
61
|
+
|
62
|
+
Note: Class descriptions are not inherited by subclasses. Each class maintains its own description.
|
63
|
+
|
64
|
+
## Property Options
|
65
|
+
|
66
|
+
SmartMessage supports all Hashie::Dash property options plus additional features:
|
67
|
+
|
68
|
+
### 1. Default Values
|
69
|
+
|
70
|
+
Specify a default value for a property when not provided during initialization:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
class OrderMessage < SmartMessage::Base
|
74
|
+
# Static default
|
75
|
+
property :status, default: 'pending'
|
76
|
+
|
77
|
+
# Dynamic default using a Proc
|
78
|
+
property :created_at, default: -> { Time.now }
|
79
|
+
|
80
|
+
# Default array
|
81
|
+
property :items, default: []
|
82
|
+
end
|
83
|
+
|
84
|
+
order = OrderMessage.new
|
85
|
+
order.status # => 'pending'
|
86
|
+
order.created_at # => Current time
|
87
|
+
order.items # => []
|
88
|
+
```
|
89
|
+
|
90
|
+
### 2. Required Properties
|
91
|
+
|
92
|
+
Mark properties as required to ensure they're provided during initialization:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
class PaymentMessage < SmartMessage::Base
|
96
|
+
property :payment_id, required: true
|
97
|
+
property :amount, required: true
|
98
|
+
property :note # Optional
|
99
|
+
end
|
100
|
+
|
101
|
+
# This raises ArgumentError: The property 'payment_id' is required
|
102
|
+
PaymentMessage.new(amount: 100)
|
103
|
+
|
104
|
+
# This works
|
105
|
+
PaymentMessage.new(payment_id: 'PAY-123', amount: 100)
|
106
|
+
```
|
107
|
+
|
108
|
+
### 3. Property Transformation
|
109
|
+
|
110
|
+
Transform property values when they're set:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
class UserMessage < SmartMessage::Base
|
114
|
+
property :email, transform_with: ->(v) { v.to_s.downcase }
|
115
|
+
property :name, transform_with: ->(v) { v.to_s.strip.capitalize }
|
116
|
+
property :tags, transform_with: ->(v) { Array(v).map(&:to_s) }
|
117
|
+
end
|
118
|
+
|
119
|
+
user = UserMessage.new(
|
120
|
+
email: 'USER@EXAMPLE.COM',
|
121
|
+
name: ' john ',
|
122
|
+
tags: 'admin'
|
123
|
+
)
|
124
|
+
|
125
|
+
user.email # => 'user@example.com'
|
126
|
+
user.name # => 'John'
|
127
|
+
user.tags # => ['admin']
|
128
|
+
```
|
129
|
+
|
130
|
+
### 4. Property Translation (from Hashie::Extensions::Dash::PropertyTranslation)
|
131
|
+
|
132
|
+
Map external field names to internal property names:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
class ApiMessage < SmartMessage::Base
|
136
|
+
property :user_id, from: :userId
|
137
|
+
property :order_date, from: 'orderDate'
|
138
|
+
property :total_amount, from: [:totalAmount, :total, :amount]
|
139
|
+
end
|
140
|
+
|
141
|
+
# All of these work
|
142
|
+
msg1 = ApiMessage.new(userId: 123)
|
143
|
+
msg2 = ApiMessage.new(user_id: 123)
|
144
|
+
msg3 = ApiMessage.new('orderDate' => '2024-01-01')
|
145
|
+
msg4 = ApiMessage.new(totalAmount: 100) # or total: 100, or amount: 100
|
146
|
+
|
147
|
+
msg1.user_id # => 123
|
148
|
+
```
|
149
|
+
|
150
|
+
### 5. Type Coercion (from Hashie::Extensions::Coercion)
|
151
|
+
|
152
|
+
Automatically coerce property values to specific types:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class TypedMessage < SmartMessage::Base
|
156
|
+
property :count
|
157
|
+
property :price
|
158
|
+
property :active
|
159
|
+
property :tags
|
160
|
+
property :metadata
|
161
|
+
|
162
|
+
coerce_key :count, Integer
|
163
|
+
coerce_key :price, Float
|
164
|
+
coerce_key :active, ->(v) { v.to_s.downcase == 'true' }
|
165
|
+
coerce_key :tags, Array[String]
|
166
|
+
coerce_key :metadata, Hash
|
167
|
+
end
|
168
|
+
|
169
|
+
msg = TypedMessage.new(
|
170
|
+
count: '42',
|
171
|
+
price: '19.99',
|
172
|
+
active: 'yes',
|
173
|
+
tags: 'important',
|
174
|
+
metadata: nil
|
175
|
+
)
|
176
|
+
|
177
|
+
msg.count # => 42 (Integer)
|
178
|
+
msg.price # => 19.99 (Float)
|
179
|
+
msg.active # => false (Boolean logic)
|
180
|
+
msg.tags # => ['important'] (Array)
|
181
|
+
msg.metadata # => {} (Hash)
|
182
|
+
```
|
183
|
+
|
184
|
+
### 6. Property Descriptions (SmartMessage Enhancement)
|
185
|
+
|
186
|
+
Add human-readable descriptions to document your properties for dynamic LLM integration:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
class DocumentedMessage < SmartMessage::Base
|
190
|
+
property :transaction_id,
|
191
|
+
required: true,
|
192
|
+
description: "Unique identifier for the transaction"
|
193
|
+
|
194
|
+
property :amount,
|
195
|
+
transform_with: ->(v) { BigDecimal(v.to_s) },
|
196
|
+
description: "Transaction amount in the smallest currency unit"
|
197
|
+
|
198
|
+
property :currency,
|
199
|
+
default: 'USD',
|
200
|
+
description: "ISO 4217 currency code"
|
201
|
+
|
202
|
+
property :status,
|
203
|
+
default: 'pending',
|
204
|
+
description: "Current transaction status: pending, completed, failed"
|
205
|
+
|
206
|
+
property :metadata,
|
207
|
+
default: {},
|
208
|
+
description: "Additional transaction metadata as key-value pairs"
|
209
|
+
end
|
210
|
+
|
211
|
+
# Access descriptions programmatically
|
212
|
+
DocumentedMessage.property_description(:amount)
|
213
|
+
# => "Transaction amount in the smallest currency unit"
|
214
|
+
|
215
|
+
DocumentedMessage.property_descriptions
|
216
|
+
# => {
|
217
|
+
# transaction_id: "Unique identifier for the transaction",
|
218
|
+
# amount: "Transaction amount in the smallest currency unit",
|
219
|
+
# currency: "ISO 4217 currency code",
|
220
|
+
# status: "Current transaction status: pending, completed, failed",
|
221
|
+
# metadata: "Additional transaction metadata as key-value pairs"
|
222
|
+
# }
|
223
|
+
|
224
|
+
DocumentedMessage.described_properties
|
225
|
+
# => [:transaction_id, :amount, :currency, :status, :metadata]
|
226
|
+
```
|
227
|
+
|
228
|
+
## Accessing Property Information
|
229
|
+
|
230
|
+
SmartMessage provides several methods to introspect properties:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
class IntrospectionExample < SmartMessage::Base
|
234
|
+
property :id, required: true, description: "Unique identifier"
|
235
|
+
property :name, description: "Display name"
|
236
|
+
property :created_at, default: -> { Time.now }
|
237
|
+
property :tags
|
238
|
+
end
|
239
|
+
|
240
|
+
# Instance methods
|
241
|
+
instance = IntrospectionExample.new(id: 1, name: "Test")
|
242
|
+
instance.fields # => Set[:id, :name, :created_at, :tags]
|
243
|
+
instance.to_h # => Hash of all properties and values
|
244
|
+
|
245
|
+
# Class methods
|
246
|
+
IntrospectionExample.fields # => Set[:id, :name, :created_at, :tags]
|
247
|
+
IntrospectionExample.property_descriptions # => Hash of descriptions
|
248
|
+
IntrospectionExample.described_properties # => [:id, :name]
|
249
|
+
```
|
250
|
+
|
251
|
+
## Hashie Extensions
|
252
|
+
|
253
|
+
SmartMessage::Base automatically includes these Hashie extensions:
|
254
|
+
|
255
|
+
### 1. DeepMerge
|
256
|
+
Allows deep merging of nested hash properties:
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
msg = MyMessage.new(config: { a: 1, b: { c: 2 } })
|
260
|
+
msg.deep_merge(config: { b: { d: 3 } })
|
261
|
+
# => config: { a: 1, b: { c: 2, d: 3 } }
|
262
|
+
```
|
263
|
+
|
264
|
+
### 2. IgnoreUndeclared
|
265
|
+
Silently ignores properties that haven't been declared:
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
# Won't raise an error for unknown properties
|
269
|
+
msg = MyMessage.new(known: 'value', unknown: 'ignored')
|
270
|
+
```
|
271
|
+
|
272
|
+
### 3. IndifferentAccess
|
273
|
+
Access properties with strings or symbols:
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
msg = MyMessage.new('name' => 'John')
|
277
|
+
msg[:name] # => 'John'
|
278
|
+
msg['name'] # => 'John'
|
279
|
+
msg.name # => 'John'
|
280
|
+
```
|
281
|
+
|
282
|
+
### 4. MethodAccess
|
283
|
+
Access properties as methods:
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
msg = MyMessage.new(name: 'John')
|
287
|
+
msg.name # => 'John'
|
288
|
+
msg.name = 'Jane'
|
289
|
+
msg.name # => 'Jane'
|
290
|
+
```
|
291
|
+
|
292
|
+
### 5. MergeInitializer
|
293
|
+
Allows initializing with merged hash values:
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
defaults = { status: 'active', retries: 3 }
|
297
|
+
msg = MyMessage.new(defaults.merge(status: 'pending'))
|
298
|
+
# => status: 'pending', retries: 3
|
299
|
+
```
|
300
|
+
|
301
|
+
## Examples
|
302
|
+
|
303
|
+
### Complete Example: Order Processing Message
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
class OrderProcessingMessage < SmartMessage::Base
|
307
|
+
description "Manages the complete order lifecycle from placement to delivery"
|
308
|
+
|
309
|
+
# Required fields with descriptions
|
310
|
+
property :order_id,
|
311
|
+
required: true,
|
312
|
+
description: "Unique order identifier from the ordering system"
|
313
|
+
|
314
|
+
property :customer_id,
|
315
|
+
required: true,
|
316
|
+
description: "Customer who placed the order"
|
317
|
+
|
318
|
+
# Amount with transformation and description
|
319
|
+
property :total_amount,
|
320
|
+
transform_with: ->(v) { BigDecimal(v.to_s) },
|
321
|
+
description: "Total order amount including tax and shipping"
|
322
|
+
|
323
|
+
# Status with default and validation description
|
324
|
+
property :status,
|
325
|
+
default: 'pending',
|
326
|
+
description: "Order status: pending, processing, shipped, delivered, cancelled"
|
327
|
+
|
328
|
+
# Items with coercion
|
329
|
+
property :items,
|
330
|
+
default: [],
|
331
|
+
description: "Array of order line items"
|
332
|
+
|
333
|
+
# Timestamps with dynamic defaults
|
334
|
+
property :created_at,
|
335
|
+
default: -> { Time.now },
|
336
|
+
description: "When the order was created"
|
337
|
+
|
338
|
+
property :updated_at,
|
339
|
+
default: -> { Time.now },
|
340
|
+
description: "Last modification timestamp"
|
341
|
+
|
342
|
+
# Optional fields
|
343
|
+
property :notes,
|
344
|
+
description: "Optional order notes or special instructions"
|
345
|
+
|
346
|
+
property :shipping_address,
|
347
|
+
description: "Shipping address as a nested hash"
|
348
|
+
|
349
|
+
# Field translation for external APIs
|
350
|
+
property :external_ref,
|
351
|
+
from: [:externalReference, :ext_ref],
|
352
|
+
description: "Reference ID from external system"
|
353
|
+
end
|
354
|
+
|
355
|
+
# Usage
|
356
|
+
order = OrderProcessingMessage.new(
|
357
|
+
order_id: 'ORD-2024-001',
|
358
|
+
customer_id: 'CUST-123',
|
359
|
+
total_amount: '149.99',
|
360
|
+
items: [
|
361
|
+
{ sku: 'WIDGET-A', quantity: 2, price: 49.99 },
|
362
|
+
{ sku: 'WIDGET-B', quantity: 1, price: 50.01 }
|
363
|
+
],
|
364
|
+
shipping_address: {
|
365
|
+
street: '123 Main St',
|
366
|
+
city: 'Springfield',
|
367
|
+
state: 'IL',
|
368
|
+
zip: '62701'
|
369
|
+
},
|
370
|
+
externalReference: 'EXT-789' # Note: uses translated field name
|
371
|
+
)
|
372
|
+
|
373
|
+
# Access properties
|
374
|
+
order.order_id # => 'ORD-2024-001'
|
375
|
+
order.total_amount # => BigDecimal('149.99')
|
376
|
+
order.status # => 'pending' (default)
|
377
|
+
order.external_ref # => 'EXT-789' (translated)
|
378
|
+
order.created_at # => Time object
|
379
|
+
|
380
|
+
# Get class and property information
|
381
|
+
OrderProcessingMessage.description
|
382
|
+
# => "Manages the complete order lifecycle from placement to delivery"
|
383
|
+
|
384
|
+
OrderProcessingMessage.property_description(:total_amount)
|
385
|
+
# => "Total order amount including tax and shipping"
|
386
|
+
|
387
|
+
OrderProcessingMessage.property_descriptions.keys
|
388
|
+
# => [:order_id, :customer_id, :total_amount, :status, :items, ...]
|
389
|
+
```
|
390
|
+
|
391
|
+
### Example: API Integration Message
|
392
|
+
|
393
|
+
```ruby
|
394
|
+
class ApiWebhookMessage < SmartMessage::Base
|
395
|
+
# Handle different API naming conventions
|
396
|
+
property :event_type,
|
397
|
+
from: [:eventType, :event, :type],
|
398
|
+
required: true,
|
399
|
+
description: "Type of webhook event"
|
400
|
+
|
401
|
+
property :payload,
|
402
|
+
required: true,
|
403
|
+
description: "Event payload data"
|
404
|
+
|
405
|
+
property :timestamp,
|
406
|
+
from: [:timestamp, :created_at, :occurredAt],
|
407
|
+
transform_with: ->(v) { Time.parse(v.to_s) },
|
408
|
+
description: "When the event occurred"
|
409
|
+
|
410
|
+
property :retry_count,
|
411
|
+
from: :retryCount,
|
412
|
+
default: 0,
|
413
|
+
transform_with: ->(v) { v.to_i },
|
414
|
+
description: "Number of delivery attempts"
|
415
|
+
|
416
|
+
property :signature,
|
417
|
+
description: "HMAC signature for webhook validation"
|
418
|
+
end
|
419
|
+
|
420
|
+
# Can initialize with various field names
|
421
|
+
webhook1 = ApiWebhookMessage.new(
|
422
|
+
eventType: 'order.completed',
|
423
|
+
payload: { order_id: 123 },
|
424
|
+
occurredAt: '2024-01-01T10:00:00Z',
|
425
|
+
retryCount: '2'
|
426
|
+
)
|
427
|
+
|
428
|
+
webhook2 = ApiWebhookMessage.new(
|
429
|
+
type: 'order.completed', # Alternative field name
|
430
|
+
payload: { order_id: 123 },
|
431
|
+
timestamp: Time.now, # Alternative field name
|
432
|
+
retry_count: 2 # Internal field name
|
433
|
+
)
|
434
|
+
```
|
435
|
+
|
436
|
+
## Best Practices
|
437
|
+
|
438
|
+
1. **Always add descriptions** to document the purpose and format of each property
|
439
|
+
2. **Use required properties** for fields that must be present for valid messages
|
440
|
+
3. **Provide sensible defaults** for optional fields to reduce boilerplate
|
441
|
+
4. **Use transformations** to ensure data consistency and type safety
|
442
|
+
5. **Leverage field translation** when integrating with external APIs that use different naming conventions
|
443
|
+
6. **Document valid values** in descriptions for enum-like fields (e.g., status fields)
|
444
|
+
7. **Use type coercion** for fields that may come from untrusted sources (like HTTP parameters)
|
445
|
+
|
446
|
+
## Property Option Compatibility
|
447
|
+
|
448
|
+
Multiple options can be combined on a single property:
|
449
|
+
|
450
|
+
```ruby
|
451
|
+
property :amount,
|
452
|
+
required: true,
|
453
|
+
from: [:amount, :total, :value],
|
454
|
+
transform_with: ->(v) { BigDecimal(v.to_s) },
|
455
|
+
description: "Transaction amount in cents"
|
456
|
+
```
|
457
|
+
|
458
|
+
The processing order is:
|
459
|
+
1. Field translation (from)
|
460
|
+
2. Default value (if not provided)
|
461
|
+
3. Required validation
|
462
|
+
4. Type coercion
|
463
|
+
5. Transformation
|
464
|
+
6. Value assignment
|
465
|
+
|
466
|
+
## Limitations
|
467
|
+
|
468
|
+
- Property names must be valid Ruby method names
|
469
|
+
- The `_sm_` prefix is reserved for internal SmartMessage properties
|
470
|
+
- Descriptions are metadata only and don't affect runtime behavior
|
471
|
+
- Some Hashie options may conflict if used incorrectly (e.g., required with default)
|
data/lib/smart_message/base.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
require 'securerandom' # STDLIB
|
6
6
|
|
7
7
|
require_relative './wrapper.rb'
|
8
|
+
require_relative './property_descriptions.rb'
|
8
9
|
|
9
10
|
module SmartMessage
|
10
11
|
# The foundation class for the smart message
|
@@ -18,6 +19,8 @@ module SmartMessage
|
|
18
19
|
|
19
20
|
include Hashie::Extensions::Dash::PropertyTranslation
|
20
21
|
|
22
|
+
include SmartMessage::PropertyDescriptions
|
23
|
+
|
21
24
|
include Hashie::Extensions::Coercion
|
22
25
|
include Hashie::Extensions::DeepMerge
|
23
26
|
include Hashie::Extensions::IgnoreUndeclared
|
@@ -162,6 +165,17 @@ module SmartMessage
|
|
162
165
|
|
163
166
|
class << self
|
164
167
|
|
168
|
+
#########################################################
|
169
|
+
## class-level description
|
170
|
+
|
171
|
+
def description(desc = nil)
|
172
|
+
if desc.nil?
|
173
|
+
@description
|
174
|
+
else
|
175
|
+
@description = desc.to_s
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
165
179
|
#########################################################
|
166
180
|
## class-level configuration
|
167
181
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# lib/smart_message/property_descriptions.rb
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module SmartMessage
|
6
|
+
module PropertyDescriptions
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
base.class_eval do
|
10
|
+
@property_descriptions = {}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def property(property_name, options = {})
|
16
|
+
description = options.delete(:description)
|
17
|
+
|
18
|
+
# Store description if provided
|
19
|
+
if description
|
20
|
+
@property_descriptions ||= {}
|
21
|
+
@property_descriptions[property_name.to_sym] = description
|
22
|
+
end
|
23
|
+
|
24
|
+
# Call original property method
|
25
|
+
super(property_name, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def property_description(property_name)
|
29
|
+
@property_descriptions&.[](property_name.to_sym)
|
30
|
+
end
|
31
|
+
|
32
|
+
def property_descriptions
|
33
|
+
@property_descriptions&.dup || {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def described_properties
|
37
|
+
@property_descriptions&.keys || []
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_message
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dewayne VanHoozer
|
@@ -190,6 +190,7 @@ files:
|
|
190
190
|
- docs/examples.md
|
191
191
|
- docs/getting-started.md
|
192
192
|
- docs/ideas_to_think_about.md
|
193
|
+
- docs/properties.md
|
193
194
|
- docs/serializers.md
|
194
195
|
- docs/transports.md
|
195
196
|
- docs/troubleshooting.md
|
@@ -213,6 +214,7 @@ files:
|
|
213
214
|
- lib/smart_message/header.rb
|
214
215
|
- lib/smart_message/logger.rb
|
215
216
|
- lib/smart_message/logger/base.rb
|
217
|
+
- lib/smart_message/property_descriptions.rb
|
216
218
|
- lib/smart_message/serializer.rb
|
217
219
|
- lib/smart_message/serializer/base.rb
|
218
220
|
- lib/smart_message/serializer/json.rb
|