easy_talk 3.1.0 → 3.3.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -39
  3. data/.yardopts +13 -0
  4. data/CHANGELOG.md +164 -0
  5. data/README.md +442 -1529
  6. data/Rakefile +27 -0
  7. data/docs/.gitignore +1 -0
  8. data/docs/about.markdown +28 -8
  9. data/docs/getting-started.markdown +102 -0
  10. data/docs/index.markdown +51 -4
  11. data/docs/json_schema_compliance.md +169 -0
  12. data/docs/nested-models.markdown +216 -0
  13. data/docs/primitive-schema-rfc.md +894 -0
  14. data/docs/property-types.markdown +212 -0
  15. data/docs/schema-definition.markdown +180 -0
  16. data/lib/easy_talk/builders/base_builder.rb +6 -3
  17. data/lib/easy_talk/builders/boolean_builder.rb +2 -1
  18. data/lib/easy_talk/builders/collection_helpers.rb +4 -0
  19. data/lib/easy_talk/builders/composition_builder.rb +16 -13
  20. data/lib/easy_talk/builders/integer_builder.rb +2 -1
  21. data/lib/easy_talk/builders/null_builder.rb +4 -1
  22. data/lib/easy_talk/builders/number_builder.rb +4 -1
  23. data/lib/easy_talk/builders/object_builder.rb +109 -33
  24. data/lib/easy_talk/builders/registry.rb +182 -0
  25. data/lib/easy_talk/builders/string_builder.rb +3 -1
  26. data/lib/easy_talk/builders/temporal_builder.rb +7 -0
  27. data/lib/easy_talk/builders/tuple_builder.rb +89 -0
  28. data/lib/easy_talk/builders/typed_array_builder.rb +19 -6
  29. data/lib/easy_talk/builders/union_builder.rb +5 -1
  30. data/lib/easy_talk/configuration.rb +47 -2
  31. data/lib/easy_talk/error_formatter/base.rb +100 -0
  32. data/lib/easy_talk/error_formatter/error_code_mapper.rb +82 -0
  33. data/lib/easy_talk/error_formatter/flat.rb +38 -0
  34. data/lib/easy_talk/error_formatter/json_pointer.rb +38 -0
  35. data/lib/easy_talk/error_formatter/jsonapi.rb +64 -0
  36. data/lib/easy_talk/error_formatter/path_converter.rb +53 -0
  37. data/lib/easy_talk/error_formatter/rfc7807.rb +69 -0
  38. data/lib/easy_talk/error_formatter.rb +143 -0
  39. data/lib/easy_talk/errors.rb +3 -0
  40. data/lib/easy_talk/errors_helper.rb +66 -34
  41. data/lib/easy_talk/json_schema_equality.rb +46 -0
  42. data/lib/easy_talk/keywords.rb +0 -1
  43. data/lib/easy_talk/model.rb +148 -89
  44. data/lib/easy_talk/model_helper.rb +17 -0
  45. data/lib/easy_talk/naming_strategies.rb +24 -0
  46. data/lib/easy_talk/property.rb +23 -94
  47. data/lib/easy_talk/ref_helper.rb +33 -0
  48. data/lib/easy_talk/schema.rb +199 -0
  49. data/lib/easy_talk/schema_definition.rb +57 -5
  50. data/lib/easy_talk/schema_methods.rb +111 -0
  51. data/lib/easy_talk/sorbet_extension.rb +1 -0
  52. data/lib/easy_talk/tools/function_builder.rb +1 -1
  53. data/lib/easy_talk/type_introspection.rb +222 -0
  54. data/lib/easy_talk/types/base_composer.rb +2 -1
  55. data/lib/easy_talk/types/composer.rb +4 -0
  56. data/lib/easy_talk/types/tuple.rb +77 -0
  57. data/lib/easy_talk/validation_adapters/active_model_adapter.rb +617 -0
  58. data/lib/easy_talk/validation_adapters/active_model_schema_validation.rb +106 -0
  59. data/lib/easy_talk/validation_adapters/base.rb +156 -0
  60. data/lib/easy_talk/validation_adapters/none_adapter.rb +45 -0
  61. data/lib/easy_talk/validation_adapters/registry.rb +87 -0
  62. data/lib/easy_talk/validation_builder.rb +29 -309
  63. data/lib/easy_talk/version.rb +1 -1
  64. data/lib/easy_talk.rb +42 -0
  65. metadata +38 -7
  66. data/docs/404.html +0 -25
  67. data/docs/_posts/2024-05-07-welcome-to-jekyll.markdown +0 -29
  68. data/easy_talk.gemspec +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b091eaff6c33ddcc23f0c5d0147bf28f94a7421c95a0ca47ad917af8e42fad20
4
- data.tar.gz: 9a6c0de5afff453a566940b7b1684e2075e70b53379b2bf3b7583a46ca370c58
3
+ metadata.gz: 3f11b7be6e0fa32087d5be57be3d3e37d5bd5b96cc61ae8626b33d41a420e401
4
+ data.tar.gz: 82ba98f220e8e75e3bb3e208a595a168f188c226a130e88f13991da9a386cfc6
5
5
  SHA512:
6
- metadata.gz: 32a49bfa38a5279340b6699ff26d8ac415c1574f83da1488d403d422d4b49cd4aea94d5424d858c5ae2326e914c4e83995a97eb67fe4b005543e6f8984d6a60c
7
- data.tar.gz: 02ad31d7b34aac67a4777a709ffb286487675f28e6a2c18757dd87e7a006743ab6cd0f75b817fdb6abb23d3f9ef63384fcbd5f063e1eb2d0dc8a06c7107ae0e0
6
+ metadata.gz: 90fb93ef486adb8151e883e6929437be2f52bdbe4a51bb45485006884a983b80ce9f10de1d14b313031be4b8126d6fe21c3bd54a998863543a1be1d752c124d9
7
+ data.tar.gz: 2ff9102ba57ae6738cb79511fdd095b5f798a760c009b10d2b5577c30575f04d94dfb11e410036e566971d92e27e2c4bc2d17336e44c64f9d35b966bbe1b4960
data/.rubocop.yml CHANGED
@@ -1,7 +1,3 @@
1
- plugins:
2
- - rubocop-rspec
3
- - rubocop-rake
4
-
5
1
  AllCops:
6
2
  NewCops: enable
7
3
  TargetRubyVersion: 3.2
@@ -33,6 +29,8 @@ Style/StringLiterals:
33
29
 
34
30
  Metrics/ClassLength:
35
31
  Max: 240
32
+ Exclude:
33
+ - lib/easy_talk/validation_adapters/active_model_adapter.rb
36
34
 
37
35
  Metrics/MethodLength:
38
36
  Max: 60
@@ -55,6 +53,7 @@ Metrics/BlockNesting:
55
53
  Metrics/BlockLength:
56
54
  Exclude:
57
55
  - spec/**/*
56
+ - easy_talk.gemspec
58
57
 
59
58
  Layout/EndAlignment:
60
59
  Enabled: false
@@ -70,47 +69,24 @@ Lint/EmptyBlock:
70
69
  Exclude:
71
70
  - spec/**/*
72
71
 
73
- RSpec/DescribeClass:
74
- Enabled: false
75
-
76
- RSpec/LeakyConstantDeclaration:
77
- Enabled: false
78
-
79
- RSpec/RemoveConst:
80
- Enabled: false
81
-
82
- RSpec/BeforeAfterAll:
83
- Enabled: false
84
-
85
- RSpec/NestedGroups:
86
- Max: 4
87
-
88
- RSpec/RepeatedDescription:
89
- Enabled: false
90
-
91
- RSpec/PendingWithoutReason:
92
- Enabled: false
93
-
94
- RSpec/MultipleDescribes:
72
+ Lint/DuplicateBranch:
95
73
  Enabled: false
96
74
 
97
- RSpec/ContextWording:
75
+ Gemspec/DevelopmentDependencies:
98
76
  Enabled: false
99
77
 
100
- RSpec/MultipleMemoizedHelpers:
101
- Max: 10
102
-
103
- RSpec/ExampleLength:
104
- Max: 40
105
-
106
- RSpec/MultipleExpectations:
107
- Max: 10
78
+ Naming/PredicateMethod:
79
+ Exclude:
80
+ - spec/support/schema_validation_matcher.rb
108
81
 
109
- RSpec/SpecFilePathFormat:
82
+ Naming/BlockForwarding:
110
83
  Enabled: false
111
84
 
112
- Lint/DuplicateBranch:
113
- Enabled: false
85
+ Lint/UnusedMethodArgument:
86
+ AllowUnusedKeywordArguments: true
87
+ Exclude:
88
+ - lib/easy_talk/configuration.rb
89
+ - lib/easy_talk/schema_definition.rb
114
90
 
115
- Gemspec/DevelopmentDependencies:
91
+ Style/DefWithParentheses:
116
92
  Enabled: false
data/.yardopts ADDED
@@ -0,0 +1,13 @@
1
+ --readme README.md
2
+ --title "EasyTalk API Documentation"
3
+ --markup markdown
4
+ --output-dir docs/api
5
+ --protected
6
+ --no-private
7
+ --hide-void-return
8
+ --embed-mixins
9
+ lib/**/*.rb
10
+ -
11
+ README.md
12
+ CHANGELOG.md
13
+ CONSTRAINTS.md
data/CHANGELOG.md CHANGED
@@ -1,3 +1,167 @@
1
+ ## [3.3.0] - 2026-01-12
2
+
3
+ ### Added
4
+
5
+ - **Schema Objects in additionalProperties**: Extended `additionalProperties` to support type constraints and schema objects (#160)
6
+ - New syntax: `additional_properties Integer, minimum: 0, maximum: 100`
7
+ - Three supported forms: boolean, type class, or type with constraints
8
+ - Generates full JSON Schema for additional properties validation
9
+ - New methods: `ObjectBuilder#process_additional_properties`, `ObjectBuilder#build_additional_properties_schema`
10
+ - Fully backwards compatible with existing boolean usage
11
+
12
+ - **External $ref Support via $id**: Enhanced schema referencing with external URI support (#158)
13
+ - New configuration options:
14
+ - `base_schema_uri` - Base URI for auto-generating $id values
15
+ - `auto_generate_ids` - Enable automatic $id generation (default: false)
16
+ - `prefer_external_refs` - Use external URI in $ref when model has $id (default: false)
17
+ - Three-level schema ID resolution: explicit per-model → auto-generated → global
18
+ - Dynamic $ref templates conditionally use external URIs or local `#/$defs/ModelName` format
19
+ - Supports composition types (T::OneOf, T::AnyOf, T::AllOf) with external refs
20
+
21
+ - **T::Tuple Type for Positional Array Validation**: New tuple type for JSON Schema tuple support (#154, #155)
22
+ - New syntax: `property :coords, T::Tuple[Float, Float]`
23
+ - Generates JSON Schema with `items` as array of schemas
24
+ - Supports `additional_items: false`, `true`, or a type constraint
25
+ - Combines with array constraints: `min_items`, `max_items`, `unique_items`
26
+ - ActiveModel validation for runtime type checking at each position
27
+ - New files: `lib/easy_talk/types/tuple.rb`, `lib/easy_talk/builders/tuple_builder.rb`
28
+
29
+ - **Object-Level JSON Schema Keywords**: Support for schema-wide object constraints (#148, #151)
30
+ - New keywords: `patternProperties`, `minProperties`, `maxProperties`, `dependencies`, `dependentRequired`
31
+ - ActiveModel validators for object-level constraints via `ActiveModelSchemaValidation` module
32
+ - Auto-defined `count_present_properties` private method on model classes
33
+ - Thread-safe validator application with double-checked locking
34
+
35
+ ### Changed
36
+
37
+ - **Format Validation Enhancements**: Improved format validation accuracy and security (#157, #156, #144)
38
+ - **Scope Fix**: Format and pattern validations now only apply to string values (per JSON Schema spec)
39
+ - **URI/URL Validation**: Uses `URI.parse()` with `.absolute?` check instead of regex
40
+ - **Parsing-Based Validators**: Date, DateTime, and Time formats now use parsing instead of regex
41
+ - **Email Validation**: Replaced ReDoS-vulnerable regex with simple linear-time pattern
42
+ - New validation methods: `apply_uri_format_validation`, `apply_date_format_validation`, `apply_datetime_format_validation`, `apply_time_format_validation`
43
+
44
+ - **JSON Schema Equality for uniqueItems**: Implements correct JSON Schema equality semantics for array uniqueness (#152)
45
+ - Objects with same keys/values in different order are equal
46
+ - Numbers are mathematically equal (1 == 1.0)
47
+ - Type matters for non-numbers (true != 1, false != 0)
48
+ - New module: `EasyTalk::JsonSchemaEquality` with `normalize()` and `duplicates?()` methods
49
+ - MAX_DEPTH = 100 limit to prevent SystemStackError on deeply nested structures
50
+
51
+ - **Array Presence Validation**: Required array properties now properly reject nil values (#140)
52
+ - New method: `apply_array_presence_validation()` for array-specific nil checks
53
+ - Rejects nil but allows empty arrays `[]`
54
+ - Aligns with JSON Schema spec: 'optional' means property can be omitted, not that nil is accepted
55
+
56
+ ### Fixed
57
+
58
+ - **Default Additional Properties Configuration**: Fixed `default_additional_properties` config option being ignored (#142)
59
+ - Both `ObjectBuilder` and `SchemaDefinition` now use configured default
60
+ - Added schema hash duplication to prevent mutation side effects
61
+
62
+ - **Property Name Validation**: Fixed validation incorrectly applying to JSON output name (`:as` constraint) instead of Ruby property name (#143)
63
+ - `validate_property_name()` now validates Ruby name only
64
+ - Allows using `:as` for JSON-LD (@type, @id), JSON Schema ($id, $ref), and other special JSON keys
65
+
66
+ - **Method Redefinition Warning**: Fixed "method redefined" warning for `:property` keyword (#153)
67
+ - Removed `:property` from `KEYWORDS` constant since it has dedicated implementation
68
+
69
+ ### Internal
70
+
71
+ - **Type Introspection Improvements**: Enhanced type checking capabilities (#156)
72
+ - New helper methods: `array_type?()`, `boolean_union_type?()`, extracted `boolean_type?()`
73
+ - Improved encapsulation and testability
74
+
75
+ - **ValidationContext Decoupling**: Refactored validation adapter internals (#150)
76
+ - New plain data class `ValidationContext` for pre-computed validation values
77
+ - Replaces `__send__` usage with direct value passing
78
+ - Improved encapsulation and testability
79
+
80
+ - **Code Coverage Integration**: Added SimpleCov and Codecov for test coverage tracking
81
+ - Local HTML reports via SimpleCov
82
+ - CI badge reporting via Codecov
83
+ - Coverage uploads on Ruby 3.4.7 builds only
84
+
85
+ - **Test Infrastructure**: Comprehensive test expansion (~1800 lines of new tests)
86
+ - New test files: `external_ref_spec.rb`, `additional_properties_schema_spec.rb`, `tuple_validation_spec.rb`, `json_schema_equality_spec.rb`
87
+ - Enhanced builder test coverage
88
+ - Improved JSON Schema compliance testing
89
+
90
+ ## [3.2.0] - 2025-12-28
91
+
92
+ ### Added
93
+
94
+ - **Pluggable Validation Adapter System**: Complete overhaul of the validation layer to make it a distinct, pluggable component (#89)
95
+ - New `EasyTalk::ValidationAdapters::Base` abstract class defining the adapter interface
96
+ - New `EasyTalk::ValidationAdapters::Registry` for adapter registration and lookup
97
+ - New `EasyTalk::ValidationAdapters::ActiveModelAdapter` as the default adapter
98
+ - New `EasyTalk::ValidationAdapters::NoneAdapter` for schema-only use cases
99
+ - Per-model validation configuration: `define_schema(validations: false)`, `define_schema(validations: :none)`, or `define_schema(validations: CustomAdapter)`
100
+ - Per-property validation control with `validate: false` constraint
101
+ - Global configuration via `config.validation_adapter = :active_model`
102
+
103
+ - **Schema-Only Module**: New `EasyTalk::Schema` module for schema generation without ActiveModel (#89)
104
+ - Does not include `ActiveModel::API` or `ActiveModel::Validations`
105
+ - Ideal for API documentation, OpenAPI specs, and schema-first design
106
+
107
+ - **Pluggable Type Registry**: Runtime registration of custom types with their corresponding schema builders (#80)
108
+ - New `EasyTalk::Builders::Registry` class with `register`, `resolve`, `registered?`, `unregister`, `registered_types`, and `reset!` methods
109
+ - Added `EasyTalk.register_type` convenience method at module level
110
+ - Added `config.register_type` for configuration block registration
111
+ - Allows extending EasyTalk with custom types (e.g., Money, GeoPoint) without modifying gem source
112
+
113
+ - **Robust Type Introspection**: New `TypeIntrospection` module replacing brittle string-based type detection (#83)
114
+ - Predicate methods: `boolean_type?`, `typed_array?`, `nilable_type?`, `primitive_type?`
115
+ - Helper methods: `json_schema_type`, `get_type_class`, `extract_inner_type`
116
+ - Uses Sorbet's type system properly instead of string pattern matching
117
+
118
+ - **Standardized Validation Error Output**: Helper methods for API-friendly error formats (#88)
119
+ - **Flat format**: Simple array of field/message/code objects
120
+ - **JSON Pointer (RFC 6901)**: Paths like `/properties/name`
121
+ - **RFC 7807**: Problem Details for HTTP APIs
122
+ - **JSON:API**: Standard JSON:API error format
123
+ - Instance methods: `validation_errors(format:)`, `validation_errors_flat`, `validation_errors_json_pointer`, `validation_errors_rfc7807`, `validation_errors_jsonapi`
124
+ - Configuration options: `default_error_format`, `error_type_base_uri`, `include_error_codes`
125
+
126
+ - **Naming Strategies**: Support for automatic property name transformation (#61)
127
+ - Built-in strategies: `CAMEL_CASE`, `SNAKE_CASE`
128
+ - Optional `as:` property constraint for per-property name override
129
+ - Per-schema configuration: `define_schema { naming_strategy :camel_case }`
130
+ - Global configuration: `config.naming_strategy = :camel_case`
131
+
132
+ - **Array Composition Support**: `T::AnyOf`, `T::OneOf`, and `T::AllOf` now work with `T::Array` (#63)
133
+ - Example: `property :items, T::Array[T::OneOf[ProductA, ProductB]]`
134
+
135
+ - **Nested Model Validation in Arrays**: Arrays of EasyTalk::Model objects are now recursively validated (#112)
136
+ - Hash items in arrays are auto-instantiated to model instances
137
+ - Errors from nested models are merged with indexed paths (e.g., `addresses[0].street`)
138
+
139
+ ### Changed
140
+
141
+ - **Deprecated `EasyTalk::ValidationBuilder`**: Use `EasyTalk::ValidationAdapters::ActiveModelAdapter` instead (deprecation warning shown on first use)
142
+
143
+ ### Fixed
144
+
145
+ - **Default Value Assignment**: Default values are now properly assigned during initialization (#72)
146
+ - **Explicit Nil Preservation**: Explicitly passed `nil` values are preserved instead of being replaced with defaults (#79)
147
+ - **Optional Enum Validation**: Allow `nil` for optional properties with enum constraints (#64)
148
+ - **Optional Pattern Validation**: Allow `nil` for optional properties with pattern validation (#65)
149
+ - **Optional Format Validation**: Allow `nil` for optional properties with format validation (email, uri, uuid, etc.) (#75)
150
+ - **Optional Length Validation**: Allow `nil` for optional properties with length constraints (#76)
151
+ - **Schema Definition Mutation**: Avoid mutating schema definition during property building (#95)
152
+ - **Unknown Property Types**: Fail fast with `UnknownPropertyTypeError` instead of silently returning 'object' (#97)
153
+ - **respond_to_missing?**: Fixed `respond_to_missing?` implementation for additional properties (#98)
154
+ - **VALID_OPTIONS Mutation**: Avoid mutating `VALID_OPTIONS` constant in TypedArrayBuilder (#99)
155
+ - **Registry Reset**: `ValidationAdapters::Registry.reset!` now repopulates defaults (#100)
156
+ - **FunctionBuilder Error**: Replace deprecated Instructor error with `EasyTalk::UnsupportedTypeError` (#96)
157
+ - **Missing snake_case Strategy**: Added `SNAKE_CASE` constant to NamingStrategies module (#77)
158
+
159
+ ### Internal
160
+
161
+ - Extracted shared schema methods into `SchemaMethods` mixin for code reuse between `Model` and `Schema` modules (#103)
162
+ - Centralized built-in type registration in `Builders::Registry` (#102)
163
+ - Added json_schemer for systematic validation testing with custom RSpec matchers
164
+
1
165
  ## [3.1.0] - 2025-12-18
2
166
 
3
167
  ### Added