easy_talk 1.0.4 → 3.0.0
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/.rubocop.yml +97 -17
- data/CHANGELOG.md +66 -0
- data/README.md +316 -210
- data/easy_talk.gemspec +39 -0
- data/lib/easy_talk/builders/composition_builder.rb +12 -1
- data/lib/easy_talk/builders/integer_builder.rb +1 -0
- data/lib/easy_talk/builders/object_builder.rb +9 -23
- data/lib/easy_talk/builders/string_builder.rb +9 -0
- data/lib/easy_talk/configuration.rb +2 -8
- data/lib/easy_talk/errors_helper.rb +1 -0
- data/lib/easy_talk/model.rb +83 -56
- data/lib/easy_talk/property.rb +108 -32
- data/lib/easy_talk/schema_definition.rb +13 -18
- data/lib/easy_talk/types/composer.rb +7 -6
- data/lib/easy_talk/validation_builder.rb +327 -0
- data/lib/easy_talk/version.rb +1 -1
- metadata +28 -140
- data/lib/easy_talk/active_record_schema_builder.rb +0 -295
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# EasyTalk
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/easy_talk)
|
4
|
+
[](https://github.com/sergiobayona/easy_talk/actions/workflows/dev-build.yml)
|
5
|
+
|
3
6
|
## Introduction
|
4
7
|
|
5
8
|
### What is EasyTalk?
|
@@ -7,10 +10,12 @@ EasyTalk is a Ruby library that simplifies defining and generating JSON Schema.
|
|
7
10
|
|
8
11
|
### Key Features
|
9
12
|
* **Intuitive Schema Definition**: Use Ruby classes and methods to define JSON Schema documents easily.
|
10
|
-
* **
|
13
|
+
* **Automatic ActiveModel Validations**: Schema constraints automatically generate corresponding ActiveModel validations (configurable).
|
14
|
+
* **Works for plain Ruby classes and ActiveModel classes**: Integrate with existing code or build from scratch.
|
11
15
|
* **LLM Function Support**: Ideal for integrating with Large Language Models (LLMs) such as OpenAI's GPT series. EasyTalk enables you to effortlessly create JSON Schema documents describing the inputs and outputs of LLM function calls.
|
12
16
|
* **Schema Composition**: Define EasyTalk models and reference them in other EasyTalk models to create complex schemas.
|
13
|
-
* **
|
17
|
+
* **Enhanced Model Integration**: Automatic instantiation of nested EasyTalk models from hash attributes.
|
18
|
+
* **Flexible Configuration**: Global and per-model configuration options for fine-tuned control.
|
14
19
|
|
15
20
|
### Use Cases
|
16
21
|
- API request/response validation
|
@@ -26,8 +31,39 @@ Inspired by Python's Pydantic library, EasyTalk brings similar functionality to
|
|
26
31
|
|
27
32
|
### Requirements
|
28
33
|
- Ruby 3.2 or higher
|
29
|
-
|
30
|
-
|
34
|
+
|
35
|
+
### Version 2.0.0 Breaking Changes
|
36
|
+
|
37
|
+
⚠️ **IMPORTANT**: Version 2.0.0 includes breaking changes. Please review before upgrading:
|
38
|
+
|
39
|
+
- **Removed**: Block-style nested object definitions (using `Hash do ... end`)
|
40
|
+
- **Migration**: Use class references instead of inline Hash definitions
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
# ❌ No longer supported (v1.x style)
|
44
|
+
define_schema do
|
45
|
+
property :address, Hash do
|
46
|
+
property :street, String
|
47
|
+
property :city, String
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# ✅ New approach (v2.x style)
|
52
|
+
class Address
|
53
|
+
include EasyTalk::Model
|
54
|
+
define_schema do
|
55
|
+
property :street, String
|
56
|
+
property :city, String
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class User
|
61
|
+
include EasyTalk::Model
|
62
|
+
define_schema do
|
63
|
+
property :address, Address # Reference the class directly
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
31
67
|
|
32
68
|
### Installation Steps
|
33
69
|
Add EasyTalk to your application's Gemfile:
|
@@ -109,10 +145,11 @@ Creating and validating an instance of your model:
|
|
109
145
|
|
110
146
|
```ruby
|
111
147
|
user = User.new(name: "John Doe", email: "john@example.com", age: 25)
|
112
|
-
user.valid? # => true
|
148
|
+
user.valid? # => true (automatically validates based on schema constraints)
|
113
149
|
|
114
150
|
user.age = 17
|
115
|
-
user.valid? # => false
|
151
|
+
user.valid? # => false (violates minimum: 18 constraint)
|
152
|
+
user.errors[:age] # => ["must be greater than or equal to 18"]
|
116
153
|
```
|
117
154
|
|
118
155
|
## Core Concepts
|
@@ -143,7 +180,6 @@ EasyTalk supports standard Ruby types directly:
|
|
143
180
|
- `Float`: Floating-point numbers
|
144
181
|
- `Date`: Date values
|
145
182
|
- `DateTime`: Date and time values
|
146
|
-
- `Hash`: Object/dictionary values
|
147
183
|
|
148
184
|
#### Sorbet-Style Types
|
149
185
|
For complex types, EasyTalk uses Sorbet-style type notation:
|
@@ -184,25 +220,57 @@ end
|
|
184
220
|
|
185
221
|
In this example, `name` is required but `middle_name` is optional.
|
186
222
|
|
187
|
-
###
|
188
|
-
EasyTalk
|
223
|
+
### Automatic Validation Generation
|
224
|
+
EasyTalk automatically generates ActiveModel validations from your schema constraints. This feature is enabled by default but can be configured:
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
class User
|
228
|
+
include EasyTalk::Model
|
229
|
+
|
230
|
+
define_schema do
|
231
|
+
property :name, String, min_length: 2, max_length: 50
|
232
|
+
property :email, String, format: "email"
|
233
|
+
property :age, Integer, minimum: 18, maximum: 120
|
234
|
+
property :status, String, enum: ["active", "inactive", "pending"]
|
235
|
+
end
|
236
|
+
# Validations are automatically generated:
|
237
|
+
# validates :name, presence: true, length: { minimum: 2, maximum: 50 }
|
238
|
+
# validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
239
|
+
# validates :age, presence: true, numericality: { greater_than_or_equal_to: 18, less_than_or_equal_to: 120 }
|
240
|
+
# validates :status, presence: true, inclusion: { in: ["active", "inactive", "pending"] }
|
241
|
+
end
|
242
|
+
|
243
|
+
user = User.new(name: "Jo", email: "invalid-email", age: 17)
|
244
|
+
user.valid? # => false
|
245
|
+
user.errors.full_messages
|
246
|
+
# => ["Name is too short (minimum is 2 characters)",
|
247
|
+
# "Email is invalid",
|
248
|
+
# "Age must be greater than or equal to 18"]
|
249
|
+
```
|
250
|
+
|
251
|
+
### Manual Validation Overrides
|
252
|
+
You can still add manual validations alongside automatic ones:
|
189
253
|
|
190
254
|
```ruby
|
191
255
|
class User
|
192
256
|
include EasyTalk::Model
|
193
257
|
|
194
|
-
|
195
|
-
validates :
|
258
|
+
# Custom validation in addition to automatic ones
|
259
|
+
validates :email, uniqueness: true
|
260
|
+
validate :complex_business_rule
|
196
261
|
|
197
262
|
define_schema do
|
198
263
|
property :name, String
|
264
|
+
property :email, String, format: "email"
|
199
265
|
property :age, Integer, minimum: 18
|
200
266
|
end
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
def complex_business_rule
|
271
|
+
# Custom validation logic
|
272
|
+
end
|
201
273
|
end
|
202
|
-
|
203
|
-
user = User.new(name: "John", age: 17)
|
204
|
-
user.valid? # => false
|
205
|
-
user.errors.full_messages # => ["Age must be greater than or equal to 18"]
|
206
274
|
```
|
207
275
|
|
208
276
|
## Defining Schemas
|
@@ -230,16 +298,6 @@ property :name, String, description: "The person's name", title: "Full Name"
|
|
230
298
|
property :age, Integer, minimum: 0, maximum: 120, description: "The person's age"
|
231
299
|
```
|
232
300
|
|
233
|
-
### Nested Objects
|
234
|
-
You can define nested objects using a block:
|
235
|
-
|
236
|
-
```ruby
|
237
|
-
property :email, Hash do
|
238
|
-
property :address, String, format: "email"
|
239
|
-
property :verified, T::Boolean
|
240
|
-
end
|
241
|
-
```
|
242
|
-
|
243
301
|
### Arrays and Collections
|
244
302
|
Arrays can be defined using the `T::Array` type:
|
245
303
|
|
@@ -254,22 +312,38 @@ You can also define arrays of complex types:
|
|
254
312
|
property :addresses, T::Array[Address], description: "List of addresses"
|
255
313
|
```
|
256
314
|
|
257
|
-
### Constraints and Validations
|
258
|
-
Constraints
|
315
|
+
### Constraints and Automatic Validations
|
316
|
+
Constraints are added to properties and are used for both schema generation and automatic validation generation:
|
259
317
|
|
260
318
|
```ruby
|
261
|
-
|
262
|
-
property :
|
263
|
-
property :
|
319
|
+
define_schema do
|
320
|
+
property :name, String, min_length: 2, max_length: 50
|
321
|
+
property :email, String, format: "email"
|
322
|
+
property :category, String, enum: ["A", "B", "C"]
|
323
|
+
property :score, Float, minimum: 0.0, maximum: 100.0
|
324
|
+
property :tags, T::Array[String], min_items: 1, max_items: 10
|
325
|
+
end
|
326
|
+
# Automatically generates equivalent ActiveModel validations:
|
327
|
+
# validates :name, presence: true, length: { minimum: 2, maximum: 50 }
|
328
|
+
# validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
329
|
+
# validates :category, presence: true, inclusion: { in: ["A", "B", "C"] }
|
330
|
+
# validates :score, presence: true, numericality: { greater_than_or_equal_to: 0.0, less_than_or_equal_to: 100.0 }
|
331
|
+
# validates :tags, presence: true, length: { minimum: 1, maximum: 10 }
|
264
332
|
```
|
265
333
|
|
266
|
-
|
334
|
+
### Supported Constraint-to-Validation Mappings
|
267
335
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
336
|
+
| Constraint | Validation Generated |
|
337
|
+
|------------|---------------------|
|
338
|
+
| `min_length`, `max_length` | `length: { minimum: X, maximum: Y }` |
|
339
|
+
| `minimum`, `maximum` | `numericality: { greater_than_or_equal_to: X, less_than_or_equal_to: Y }` |
|
340
|
+
| `format: "email"` | `format: { with: URI::MailTo::EMAIL_REGEXP }` |
|
341
|
+
| `format: "url"` or `format: "uri"` | `format: { with: URI::regexp }` |
|
342
|
+
| `pattern: /regex/` | `format: { with: /regex/ }` |
|
343
|
+
| `enum: [...]` | `inclusion: { in: [...] }` |
|
344
|
+
| `min_items`, `max_items` (arrays) | `length: { minimum: X, maximum: Y }` |
|
345
|
+
| `optional: true` | Skips presence validation |
|
346
|
+
| `T.nilable(Type)` | Allows nil values, skips presence validation |
|
273
347
|
|
274
348
|
### Additional Properties
|
275
349
|
By default, EasyTalk models do not allow additional properties beyond those defined in the schema. You can change this behavior using the `additional_properties` keyword:
|
@@ -373,20 +447,21 @@ end
|
|
373
447
|
|
374
448
|
## ActiveModel Integration
|
375
449
|
|
376
|
-
###
|
377
|
-
EasyTalk models include ActiveModel
|
450
|
+
### Enhanced Validation System
|
451
|
+
EasyTalk models include comprehensive ActiveModel validation support with automatic generation:
|
378
452
|
|
379
453
|
```ruby
|
380
454
|
class User
|
381
455
|
include EasyTalk::Model
|
382
456
|
|
383
|
-
|
384
|
-
validates :
|
457
|
+
# Manual validations work alongside automatic ones
|
458
|
+
validates :age, comparison: { greater_than: 21 } # Additional business rule
|
459
|
+
validates :height, numericality: { greater_than: 0 } # Overrides auto-validation
|
385
460
|
|
386
461
|
define_schema do
|
387
|
-
property :name, String
|
388
|
-
property :age, Integer
|
389
|
-
property :height, Float
|
462
|
+
property :name, String, min_length: 2 # Auto-generates presence + length validations
|
463
|
+
property :age, Integer, minimum: 18 # Auto-generates presence + numericality validations
|
464
|
+
property :height, Float # Auto-generates presence validation (overridden above)
|
390
465
|
end
|
391
466
|
end
|
392
467
|
```
|
@@ -395,10 +470,11 @@ end
|
|
395
470
|
You can access validation errors using the standard ActiveModel methods:
|
396
471
|
|
397
472
|
```ruby
|
398
|
-
user = User.new(name: "
|
473
|
+
user = User.new(name: "J", age: 18, height: -5.9)
|
399
474
|
user.valid? # => false
|
400
|
-
user.errors[:
|
401
|
-
user.errors[:
|
475
|
+
user.errors[:name] # => ["is too short (minimum is 2 characters)"]
|
476
|
+
user.errors[:age] # => ["must be greater than 21"] # Custom validation
|
477
|
+
user.errors[:height] # => ["must be greater than 0"] # Overridden validation
|
402
478
|
```
|
403
479
|
|
404
480
|
### Model Attributes
|
@@ -411,123 +487,35 @@ user.age = 30
|
|
411
487
|
puts user.name # => "John"
|
412
488
|
```
|
413
489
|
|
414
|
-
You can also initialize a model with a hash of attributes:
|
490
|
+
You can also initialize a model with a hash of attributes, including nested EasyTalk models:
|
415
491
|
|
416
492
|
```ruby
|
417
493
|
user = User.new(name: "John", age: 30, height: 5.9)
|
418
|
-
```
|
419
|
-
|
420
|
-
## ActiveRecord Integration
|
421
|
-
|
422
|
-
### Automatic Schema Generation
|
423
|
-
For ActiveRecord models, EasyTalk automatically generates a schema based on the database columns:
|
424
|
-
|
425
|
-
```ruby
|
426
|
-
class Product < ActiveRecord::Base
|
427
|
-
include EasyTalk::Model
|
428
|
-
end
|
429
|
-
```
|
430
|
-
|
431
|
-
This will create a schema with properties for each column in the `products` table.
|
432
494
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
```ruby
|
437
|
-
class Product < ActiveRecord::Base
|
438
|
-
include EasyTalk::Model
|
439
|
-
|
440
|
-
enhance_schema({
|
441
|
-
title: "Retail Product",
|
442
|
-
description: "A product available for purchase",
|
443
|
-
properties: {
|
444
|
-
name: {
|
445
|
-
description: "Product display name",
|
446
|
-
title: "Product Name"
|
447
|
-
},
|
448
|
-
price: {
|
449
|
-
description: "Retail price in USD"
|
450
|
-
}
|
451
|
-
}
|
452
|
-
})
|
453
|
-
end
|
454
|
-
```
|
455
|
-
|
456
|
-
### Column Exclusion Options
|
457
|
-
EasyTalk provides several ways to exclude columns from your JSON schema:
|
458
|
-
|
459
|
-
#### 1. Global Configuration
|
460
|
-
|
461
|
-
```ruby
|
462
|
-
EasyTalk.configure do |config|
|
463
|
-
# Exclude specific columns by name from all models
|
464
|
-
config.excluded_columns = [:created_at, :updated_at, :deleted_at]
|
465
|
-
|
466
|
-
# Exclude all foreign key columns (columns ending with '_id')
|
467
|
-
config.exclude_foreign_keys = true # Default: false
|
468
|
-
|
469
|
-
# Exclude all primary key columns ('id')
|
470
|
-
config.exclude_primary_key = true # Default: true
|
471
|
-
|
472
|
-
# Exclude timestamp columns ('created_at', 'updated_at')
|
473
|
-
config.exclude_timestamps = true # Default: true
|
474
|
-
|
475
|
-
# Exclude all association properties
|
476
|
-
config.exclude_associations = true # Default: false
|
477
|
-
end
|
478
|
-
```
|
479
|
-
|
480
|
-
#### 2. Model-Specific Column Ignoring
|
481
|
-
|
482
|
-
```ruby
|
483
|
-
class Product < ActiveRecord::Base
|
484
|
-
include EasyTalk::Model
|
485
|
-
|
486
|
-
enhance_schema({
|
487
|
-
ignore: [:internal_ref_id, :legacy_code] # Model-specific exclusions
|
488
|
-
})
|
489
|
-
end
|
490
|
-
```
|
491
|
-
|
492
|
-
### Virtual Properties
|
493
|
-
You can add properties that don't exist as database columns:
|
494
|
-
|
495
|
-
```ruby
|
496
|
-
class Product < ActiveRecord::Base
|
495
|
+
# NEW in v2.0.0: Automatic nested model instantiation
|
496
|
+
class Address
|
497
497
|
include EasyTalk::Model
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
virtual: true,
|
503
|
-
type: :string,
|
504
|
-
description: "Complete product information"
|
505
|
-
}
|
506
|
-
}
|
507
|
-
})
|
498
|
+
define_schema do
|
499
|
+
property :street, String
|
500
|
+
property :city, String
|
501
|
+
end
|
508
502
|
end
|
509
|
-
```
|
510
503
|
|
511
|
-
|
512
|
-
By default, EasyTalk includes your model's associations in the schema:
|
513
|
-
|
514
|
-
```ruby
|
515
|
-
class Product < ActiveRecord::Base
|
504
|
+
class User
|
516
505
|
include EasyTalk::Model
|
517
|
-
|
518
|
-
|
506
|
+
define_schema do
|
507
|
+
property :name, String
|
508
|
+
property :address, Address
|
509
|
+
end
|
519
510
|
end
|
520
|
-
```
|
521
|
-
|
522
|
-
This will include `category` (as an object) and `reviews` (as an array) in the schema.
|
523
|
-
|
524
|
-
You can control this behavior with configuration:
|
525
511
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
512
|
+
# Hash attributes automatically instantiate nested models
|
513
|
+
user = User.new(
|
514
|
+
name: "John",
|
515
|
+
address: { street: "123 Main St", city: "Boston" }
|
516
|
+
)
|
517
|
+
user.address.class # => Address (automatically instantiated)
|
518
|
+
user.address.street # => "123 Main St"
|
531
519
|
```
|
532
520
|
|
533
521
|
## Advanced Features
|
@@ -597,79 +585,99 @@ You can configure EasyTalk globally:
|
|
597
585
|
|
598
586
|
```ruby
|
599
587
|
EasyTalk.configure do |config|
|
600
|
-
|
601
|
-
config.
|
602
|
-
config.
|
603
|
-
config.
|
604
|
-
config.exclude_associations = false
|
605
|
-
config.default_additional_properties = false
|
588
|
+
# Schema behavior options
|
589
|
+
config.default_additional_properties = false # Control additional properties on all models
|
590
|
+
config.nilable_is_optional = false # Makes T.nilable properties also optional
|
591
|
+
config.auto_validations = true # Automatically generate ActiveModel validations
|
606
592
|
end
|
607
593
|
```
|
608
594
|
|
609
|
-
###
|
610
|
-
|
595
|
+
### Automatic Validation Configuration
|
596
|
+
The new `auto_validations` option (enabled by default) automatically generates ActiveModel validations from your schema constraints:
|
611
597
|
|
612
598
|
```ruby
|
613
|
-
|
599
|
+
# Disable automatic validations globally
|
600
|
+
EasyTalk.configure do |config|
|
601
|
+
config.auto_validations = false
|
602
|
+
end
|
603
|
+
|
604
|
+
# Now you must manually define validations
|
605
|
+
class User
|
614
606
|
include EasyTalk::Model
|
615
607
|
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
608
|
+
validates :name, presence: true, length: { minimum: 2 }
|
609
|
+
validates :age, presence: true, numericality: { greater_than_or_equal_to: 18 }
|
610
|
+
|
611
|
+
define_schema do
|
612
|
+
property :name, String, min_length: 2
|
613
|
+
property :age, Integer, minimum: 18
|
614
|
+
end
|
620
615
|
end
|
621
616
|
```
|
622
617
|
|
623
|
-
###
|
624
|
-
|
625
|
-
|
626
|
-
1. Explicitly listed in `excluded_columns` global setting
|
627
|
-
2. Listed in the model's `schema_enhancements[:ignore]` array
|
628
|
-
3. Is a primary key when `exclude_primary_key` is true (default)
|
629
|
-
4. Is a timestamp column when `exclude_timestamps` is true (default)
|
630
|
-
5. Matches a foreign key pattern when `exclude_foreign_keys` is true
|
631
|
-
|
632
|
-
### Customizing Output
|
633
|
-
You can customize the JSON Schema output by enhancing the schema:
|
618
|
+
### Per-Model Configuration
|
619
|
+
You can configure additional properties for individual models:
|
634
620
|
|
635
621
|
```ruby
|
636
|
-
class User
|
622
|
+
class User
|
637
623
|
include EasyTalk::Model
|
638
624
|
|
639
|
-
|
640
|
-
title
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
description: "User's full name"
|
646
|
-
}
|
647
|
-
}
|
648
|
-
})
|
625
|
+
define_schema do
|
626
|
+
title "User"
|
627
|
+
additional_properties true # Allow arbitrary additional properties on this model
|
628
|
+
property :name, String
|
629
|
+
property :email, String, format: "email"
|
630
|
+
end
|
649
631
|
end
|
650
632
|
```
|
651
633
|
|
652
634
|
## Examples
|
653
635
|
|
654
|
-
### User Registration
|
636
|
+
### User Registration (with Auto-Validations)
|
655
637
|
|
656
638
|
```ruby
|
657
639
|
class User
|
658
640
|
include EasyTalk::Model
|
659
641
|
|
660
|
-
|
661
|
-
validates :
|
662
|
-
validates :
|
642
|
+
# Additional custom validations beyond automatic ones
|
643
|
+
validates :email, uniqueness: true
|
644
|
+
validates :password, confirmation: true
|
663
645
|
|
664
646
|
define_schema do
|
665
647
|
title "User Registration"
|
666
648
|
description "User registration information"
|
667
|
-
property :name, String, description: "User's full name"
|
649
|
+
property :name, String, min_length: 2, max_length: 100, description: "User's full name"
|
668
650
|
property :email, String, format: "email", description: "User's email address"
|
669
|
-
property :password, String, min_length: 8, description: "User's password"
|
670
|
-
property :notify, T::Boolean,
|
651
|
+
property :password, String, min_length: 8, max_length: 128, description: "User's password"
|
652
|
+
property :notify, T::Boolean, optional: true, description: "Whether to send notifications"
|
671
653
|
end
|
654
|
+
# Auto-generated validations:
|
655
|
+
# validates :name, presence: true, length: { minimum: 2, maximum: 100 }
|
656
|
+
# validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
657
|
+
# validates :password, presence: true, length: { minimum: 8, maximum: 128 }
|
658
|
+
# validates :notify, inclusion: { in: [true, false] } - only if present (optional: true)
|
672
659
|
end
|
660
|
+
|
661
|
+
# Usage with automatic validation
|
662
|
+
user = User.new(
|
663
|
+
name: "John Doe",
|
664
|
+
email: "john@example.com",
|
665
|
+
password: "secretpassword123",
|
666
|
+
notify: true
|
667
|
+
)
|
668
|
+
user.valid? # => true (assuming email is unique)
|
669
|
+
|
670
|
+
# Invalid data triggers auto-generated validations
|
671
|
+
invalid_user = User.new(
|
672
|
+
name: "J", # Too short
|
673
|
+
email: "invalid-email", # Invalid format
|
674
|
+
password: "123" # Too short
|
675
|
+
)
|
676
|
+
invalid_user.valid? # => false
|
677
|
+
invalid_user.errors.full_messages
|
678
|
+
# => ["Name is too short (minimum is 2 characters)",
|
679
|
+
# "Email is invalid",
|
680
|
+
# "Password is too short (minimum is 8 characters)"]
|
673
681
|
```
|
674
682
|
|
675
683
|
### Payment Processing
|
@@ -852,13 +860,16 @@ property :status, String, enum: ["active", "inactive", "pending"]
|
|
852
860
|
|
853
861
|
### Best Practices
|
854
862
|
|
855
|
-
1. Define clear property names and descriptions
|
856
|
-
2. Use appropriate types for each property
|
857
|
-
3.
|
858
|
-
4. Keep schemas focused and modular
|
859
|
-
5. Reuse models when appropriate
|
860
|
-
6. Use explicit types instead of relying on inference
|
861
|
-
7. Test your schemas with sample data
|
863
|
+
1. **Define clear property names and descriptions** for better documentation
|
864
|
+
2. **Use appropriate types** for each property with proper constraints
|
865
|
+
3. **Leverage automatic validations** by defining schema constraints instead of manual validations
|
866
|
+
4. **Keep schemas focused and modular** - extract nested objects to separate classes
|
867
|
+
5. **Reuse models when appropriate** instead of duplicating schema definitions
|
868
|
+
6. **Use explicit types** instead of relying on inference
|
869
|
+
7. **Test your schemas with sample data** to ensure validations work as expected
|
870
|
+
8. **Configure auto-validations globally** to maintain consistency across your application
|
871
|
+
9. **Use nullable_optional_property** for fields that can be omitted or null
|
872
|
+
10. **Document breaking changes** when updating schema definitions
|
862
873
|
|
863
874
|
# Nullable vs Optional Properties in EasyTalk
|
864
875
|
|
@@ -983,14 +994,6 @@ class UserProfile
|
|
983
994
|
|
984
995
|
# Optional and nullable (can be omitted, can be null if present)
|
985
996
|
nullable_optional_property :bio, String
|
986
|
-
|
987
|
-
# Nested object with mixed property types
|
988
|
-
property :address, Hash do
|
989
|
-
property :street, String # Required
|
990
|
-
property :city, String # Required
|
991
|
-
property :state, String, optional: true # Optional
|
992
|
-
nullable_optional_property :zip, String # Optional and nullable
|
993
|
-
end
|
994
997
|
end
|
995
998
|
end
|
996
999
|
```
|
@@ -1041,6 +1044,102 @@ We recommend updating your schema definitions to explicitly declare which proper
|
|
1041
1044
|
| `property :p, String, optional: true` | No | No | `{ "properties": { "p": { "type": "string" } } }` |
|
1042
1045
|
| `nullable_optional_property :p, String` | No | Yes | `{ "properties": { "p": { "type": ["string", "null"] } } }` |
|
1043
1046
|
|
1047
|
+
## Migration Guide from v1.x to v2.0
|
1048
|
+
|
1049
|
+
### Breaking Changes Summary
|
1050
|
+
|
1051
|
+
1. **Removed Block-Style Sub-Schemas**: Hash-based nested definitions are no longer supported
|
1052
|
+
2. **Enhanced Validation System**: Automatic validation generation is now enabled by default
|
1053
|
+
3. **Improved Model Initialization**: Better support for nested model instantiation
|
1054
|
+
|
1055
|
+
### Migration Steps
|
1056
|
+
|
1057
|
+
#### 1. Replace Hash-based Nested Schemas
|
1058
|
+
|
1059
|
+
```ruby
|
1060
|
+
# OLD (v1.x) - No longer works
|
1061
|
+
class User
|
1062
|
+
include EasyTalk::Model
|
1063
|
+
define_schema do
|
1064
|
+
property :address, Hash do
|
1065
|
+
property :street, String
|
1066
|
+
property :city, String
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
# NEW (v2.x) - Extract to separate classes
|
1072
|
+
class Address
|
1073
|
+
include EasyTalk::Model
|
1074
|
+
define_schema do
|
1075
|
+
property :street, String
|
1076
|
+
property :city, String
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
class User
|
1081
|
+
include EasyTalk::Model
|
1082
|
+
define_schema do
|
1083
|
+
property :address, Address
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
```
|
1087
|
+
|
1088
|
+
#### 2. Review Automatic Validations
|
1089
|
+
|
1090
|
+
With `auto_validations: true` (default), you may need to remove redundant manual validations:
|
1091
|
+
|
1092
|
+
```ruby
|
1093
|
+
# OLD (v1.x) - Manual validations required
|
1094
|
+
class User
|
1095
|
+
include EasyTalk::Model
|
1096
|
+
|
1097
|
+
validates :name, presence: true, length: { minimum: 2 }
|
1098
|
+
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
1099
|
+
|
1100
|
+
define_schema do
|
1101
|
+
property :name, String
|
1102
|
+
property :email, String
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
# NEW (v2.x) - Automatic validations from constraints
|
1107
|
+
class User
|
1108
|
+
include EasyTalk::Model
|
1109
|
+
|
1110
|
+
# Only add validations not covered by schema constraints
|
1111
|
+
validates :email, uniqueness: true
|
1112
|
+
|
1113
|
+
define_schema do
|
1114
|
+
property :name, String, min_length: 2 # Auto-generates presence + length
|
1115
|
+
property :email, String, format: "email" # Auto-generates presence + format
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
```
|
1119
|
+
|
1120
|
+
#### 3. Configuration Updates
|
1121
|
+
|
1122
|
+
Review your configuration for new options:
|
1123
|
+
|
1124
|
+
```ruby
|
1125
|
+
EasyTalk.configure do |config|
|
1126
|
+
# New option in v2.0
|
1127
|
+
config.auto_validations = true # Enable/disable automatic validation generation
|
1128
|
+
|
1129
|
+
# Existing options (unchanged)
|
1130
|
+
config.nilable_is_optional = false
|
1131
|
+
config.default_additional_properties = false
|
1132
|
+
# ... other existing config
|
1133
|
+
end
|
1134
|
+
```
|
1135
|
+
|
1136
|
+
### Compatibility Notes
|
1137
|
+
|
1138
|
+
- **Ruby Version**: Still requires Ruby 3.2+
|
1139
|
+
- **Dependencies**: Core dependencies remain the same
|
1140
|
+
- **JSON Schema Output**: No changes to generated schemas
|
1141
|
+
- **ActiveModel Integration**: Fully backward compatible
|
1142
|
+
|
1044
1143
|
## Development and Contributing
|
1045
1144
|
|
1046
1145
|
### Setting Up the Development Environment
|
@@ -1059,6 +1158,13 @@ Run the test suite with:
|
|
1059
1158
|
bundle exec rake spec
|
1060
1159
|
```
|
1061
1160
|
|
1161
|
+
### Code Quality
|
1162
|
+
Run the linter:
|
1163
|
+
|
1164
|
+
```bash
|
1165
|
+
bundle exec rubocop
|
1166
|
+
```
|
1167
|
+
|
1062
1168
|
### Contributing Guidelines
|
1063
1169
|
Bug reports and pull requests are welcome on GitHub at https://github.com/sergiobayona/easy_talk.
|
1064
1170
|
|