treaty 0.18.0 → 0.19.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/README.md +1 -1
- data/config/locales/en.yml +3 -3
- data/lib/treaty/engine.rb +1 -1
- data/lib/treaty/{attribute/entity → entity/attribute}/attribute.rb +4 -4
- data/lib/treaty/entity/attribute/base.rb +184 -0
- data/lib/treaty/entity/attribute/builder/base.rb +275 -0
- data/lib/treaty/entity/attribute/collection.rb +67 -0
- data/lib/treaty/entity/attribute/dsl.rb +92 -0
- data/lib/treaty/entity/attribute/helper_mapper.rb +74 -0
- data/lib/treaty/entity/attribute/option/base.rb +190 -0
- data/lib/treaty/entity/attribute/option/conditionals/base.rb +92 -0
- data/lib/treaty/entity/attribute/option/conditionals/if_conditional.rb +136 -0
- data/lib/treaty/entity/attribute/option/conditionals/unless_conditional.rb +153 -0
- data/lib/treaty/entity/attribute/option/modifiers/as_modifier.rb +93 -0
- data/lib/treaty/entity/attribute/option/modifiers/cast_modifier.rb +285 -0
- data/lib/treaty/entity/attribute/option/modifiers/computed_modifier.rb +128 -0
- data/lib/treaty/entity/attribute/option/modifiers/default_modifier.rb +105 -0
- data/lib/treaty/entity/attribute/option/modifiers/transform_modifier.rb +114 -0
- data/lib/treaty/entity/attribute/option/registry.rb +157 -0
- data/lib/treaty/entity/attribute/option/registry_initializer.rb +117 -0
- data/lib/treaty/entity/attribute/option/validators/format_validator.rb +222 -0
- data/lib/treaty/entity/attribute/option/validators/inclusion_validator.rb +94 -0
- data/lib/treaty/entity/attribute/option/validators/required_validator.rb +100 -0
- data/lib/treaty/entity/attribute/option/validators/type_validator.rb +219 -0
- data/lib/treaty/entity/attribute/option_normalizer.rb +168 -0
- data/lib/treaty/entity/attribute/option_orchestrator.rb +192 -0
- data/lib/treaty/entity/attribute/validation/attribute_validator.rb +147 -0
- data/lib/treaty/entity/attribute/validation/base.rb +76 -0
- data/lib/treaty/entity/attribute/validation/nested_array_validator.rb +207 -0
- data/lib/treaty/entity/attribute/validation/nested_object_validator.rb +105 -0
- data/lib/treaty/entity/attribute/validation/nested_transformer.rb +432 -0
- data/lib/treaty/entity/attribute/validation/orchestrator/base.rb +262 -0
- data/lib/treaty/entity/base.rb +90 -0
- data/lib/treaty/entity/builder.rb +44 -0
- data/lib/treaty/{info/entity → entity/info}/builder.rb +8 -8
- data/lib/treaty/{info/entity → entity/info}/dsl.rb +2 -2
- data/lib/treaty/{info/entity → entity/info}/result.rb +2 -2
- data/lib/treaty/entity.rb +7 -79
- data/lib/treaty/request/attribute/attribute.rb +1 -1
- data/lib/treaty/request/attribute/builder.rb +2 -2
- data/lib/treaty/request/entity.rb +1 -1
- data/lib/treaty/request/factory.rb +5 -5
- data/lib/treaty/request/validator.rb +1 -1
- data/lib/treaty/response/attribute/attribute.rb +1 -1
- data/lib/treaty/response/attribute/builder.rb +2 -2
- data/lib/treaty/response/entity.rb +1 -1
- data/lib/treaty/response/factory.rb +5 -5
- data/lib/treaty/response/validator.rb +1 -1
- data/lib/treaty/version.rb +1 -1
- metadata +35 -34
- data/lib/treaty/attribute/base.rb +0 -182
- data/lib/treaty/attribute/builder/base.rb +0 -273
- data/lib/treaty/attribute/collection.rb +0 -65
- data/lib/treaty/attribute/dsl.rb +0 -90
- data/lib/treaty/attribute/entity/builder.rb +0 -46
- data/lib/treaty/attribute/helper_mapper.rb +0 -72
- data/lib/treaty/attribute/option/base.rb +0 -188
- data/lib/treaty/attribute/option/conditionals/base.rb +0 -90
- data/lib/treaty/attribute/option/conditionals/if_conditional.rb +0 -134
- data/lib/treaty/attribute/option/conditionals/unless_conditional.rb +0 -151
- data/lib/treaty/attribute/option/modifiers/as_modifier.rb +0 -91
- data/lib/treaty/attribute/option/modifiers/cast_modifier.rb +0 -283
- data/lib/treaty/attribute/option/modifiers/computed_modifier.rb +0 -126
- data/lib/treaty/attribute/option/modifiers/default_modifier.rb +0 -103
- data/lib/treaty/attribute/option/modifiers/transform_modifier.rb +0 -112
- data/lib/treaty/attribute/option/registry.rb +0 -155
- data/lib/treaty/attribute/option/registry_initializer.rb +0 -115
- data/lib/treaty/attribute/option/validators/format_validator.rb +0 -220
- data/lib/treaty/attribute/option/validators/inclusion_validator.rb +0 -92
- data/lib/treaty/attribute/option/validators/required_validator.rb +0 -98
- data/lib/treaty/attribute/option/validators/type_validator.rb +0 -217
- data/lib/treaty/attribute/option_normalizer.rb +0 -166
- data/lib/treaty/attribute/option_orchestrator.rb +0 -190
- data/lib/treaty/attribute/validation/attribute_validator.rb +0 -145
- data/lib/treaty/attribute/validation/base.rb +0 -74
- data/lib/treaty/attribute/validation/nested_array_validator.rb +0 -205
- data/lib/treaty/attribute/validation/nested_object_validator.rb +0 -103
- data/lib/treaty/attribute/validation/nested_transformer.rb +0 -430
- data/lib/treaty/attribute/validation/orchestrator/base.rb +0 -260
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Entity
|
|
5
|
+
module Attribute
|
|
6
|
+
module Validation
|
|
7
|
+
# Validates array elements against nested attribute definitions.
|
|
8
|
+
#
|
|
9
|
+
# ## Purpose
|
|
10
|
+
#
|
|
11
|
+
# Performs validation for nested array attributes during the validation phase.
|
|
12
|
+
# Handles both simple arrays (with :_self attribute) and complex arrays (objects).
|
|
13
|
+
#
|
|
14
|
+
# ## Responsibilities
|
|
15
|
+
#
|
|
16
|
+
# 1. **Simple Array Validation** - Validates primitive values in arrays
|
|
17
|
+
# 2. **Complex Array Validation** - Validates hash objects within arrays
|
|
18
|
+
# 3. **Error Context** - Provides clear error messages with array index
|
|
19
|
+
# 4. **Type Checking** - Ensures elements match expected types
|
|
20
|
+
#
|
|
21
|
+
# ## Array Types
|
|
22
|
+
#
|
|
23
|
+
# ### Simple Array (`:_self` attribute)
|
|
24
|
+
# Array containing primitive values (strings, integers, etc.)
|
|
25
|
+
#
|
|
26
|
+
# ```ruby
|
|
27
|
+
# array :tags do
|
|
28
|
+
# string :_self # Array of strings
|
|
29
|
+
# end
|
|
30
|
+
# ```
|
|
31
|
+
#
|
|
32
|
+
# Validates: `["ruby", "rails", "api"]`
|
|
33
|
+
#
|
|
34
|
+
# ### Complex Array (regular attributes)
|
|
35
|
+
# Array containing hash objects with defined structure
|
|
36
|
+
#
|
|
37
|
+
# ```ruby
|
|
38
|
+
# array :authors do
|
|
39
|
+
# string :name, :required
|
|
40
|
+
# string :email
|
|
41
|
+
# end
|
|
42
|
+
# ```
|
|
43
|
+
#
|
|
44
|
+
# Validates: `[{ name: "Alice", email: "alice@example.com" }, ...]`
|
|
45
|
+
#
|
|
46
|
+
# ## Usage
|
|
47
|
+
#
|
|
48
|
+
# Called by AttributeValidator for nested arrays:
|
|
49
|
+
#
|
|
50
|
+
# validator = NestedArrayValidator.new(attribute)
|
|
51
|
+
# validator.validate!(array_value)
|
|
52
|
+
#
|
|
53
|
+
# ## Error Handling
|
|
54
|
+
#
|
|
55
|
+
# Provides contextual error messages including:
|
|
56
|
+
# - Array attribute name
|
|
57
|
+
# - Element index (0-based)
|
|
58
|
+
# - Specific validation error
|
|
59
|
+
#
|
|
60
|
+
# Example error:
|
|
61
|
+
# "Error in array 'tags' at index 2: Element must match one of the defined types"
|
|
62
|
+
#
|
|
63
|
+
# ## Architecture
|
|
64
|
+
#
|
|
65
|
+
# Uses:
|
|
66
|
+
# - `AttributeValidator` - Validates individual elements
|
|
67
|
+
# - Caches validators for performance
|
|
68
|
+
# - Separates self validators from regular validators
|
|
69
|
+
class NestedArrayValidator
|
|
70
|
+
# Creates a new nested array validator
|
|
71
|
+
#
|
|
72
|
+
# @param attribute [Attribute::Base] The array-type attribute with nested attributes
|
|
73
|
+
def initialize(attribute)
|
|
74
|
+
@attribute = attribute
|
|
75
|
+
@self_validators = nil
|
|
76
|
+
@regular_validators = nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Validates all items in an array
|
|
80
|
+
# Skips validation if value is not an Array
|
|
81
|
+
#
|
|
82
|
+
# @param array [Array] The array to validate
|
|
83
|
+
# @raise [Treaty::Exceptions::Validation] If any item validation fails
|
|
84
|
+
# @return [void]
|
|
85
|
+
def validate!(array)
|
|
86
|
+
return unless array.is_a?(Array)
|
|
87
|
+
|
|
88
|
+
array.each_with_index do |array_item, index|
|
|
89
|
+
validate_self_array_item!(array_item, index) if self_validators.any?
|
|
90
|
+
|
|
91
|
+
validate_regular_array_item!(array_item, index) if regular_validators.any?
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# Validates array item for simple arrays (with :_self attribute)
|
|
98
|
+
# Simple array contains primitive values: strings, integers, datetimes, etc.
|
|
99
|
+
# Example: ["ruby", "rails", "api"] where each item is a String
|
|
100
|
+
#
|
|
101
|
+
# @param array_item [String, Integer, DateTime] Primitive value from simple array
|
|
102
|
+
# @param index [Integer] Array index for error messages
|
|
103
|
+
# @raise [Treaty::Exceptions::Validation] If primitive value doesn't match defined type
|
|
104
|
+
# @return [void]
|
|
105
|
+
def validate_self_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
|
|
106
|
+
errors = []
|
|
107
|
+
|
|
108
|
+
validated = self_validators.any? do |validator|
|
|
109
|
+
validator.validate_value!(array_item)
|
|
110
|
+
true
|
|
111
|
+
rescue Treaty::Exceptions::Validation => e
|
|
112
|
+
errors << e.message
|
|
113
|
+
false
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
return if validated
|
|
117
|
+
|
|
118
|
+
raise Treaty::Exceptions::Validation,
|
|
119
|
+
I18n.t(
|
|
120
|
+
"treaty.attributes.validators.nested.array.element_validation_error",
|
|
121
|
+
attribute: @attribute.name,
|
|
122
|
+
index:,
|
|
123
|
+
errors: errors.join("; ")
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Validates array item for complex arrays (with regular attributes)
|
|
128
|
+
# Complex array contains hash objects with defined structure
|
|
129
|
+
# Example: [{ name: "Alice", email: "alice@example.com" }, ...] where each item is a Hash
|
|
130
|
+
#
|
|
131
|
+
# @param array_item [Hash] Hash object from complex array
|
|
132
|
+
# @param index [Integer] Array index for error messages
|
|
133
|
+
# @raise [Treaty::Exceptions::Validation] If item is not Hash or nested validation fails
|
|
134
|
+
# @return [void]
|
|
135
|
+
def validate_regular_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
|
|
136
|
+
unless array_item.is_a?(Hash)
|
|
137
|
+
raise Treaty::Exceptions::Validation,
|
|
138
|
+
I18n.t(
|
|
139
|
+
"treaty.attributes.validators.nested.array.element_type_error",
|
|
140
|
+
attribute: @attribute.name,
|
|
141
|
+
index:,
|
|
142
|
+
actual: array_item.class
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
regular_validators.each do |nested_attribute, validator|
|
|
147
|
+
nested_value = array_item.fetch(nested_attribute.name, nil)
|
|
148
|
+
validator.validate_value!(nested_value)
|
|
149
|
+
rescue Treaty::Exceptions::Validation => e
|
|
150
|
+
raise Treaty::Exceptions::Validation,
|
|
151
|
+
I18n.t(
|
|
152
|
+
"treaty.attributes.validators.nested.array.attribute_error",
|
|
153
|
+
attribute: @attribute.name,
|
|
154
|
+
index:,
|
|
155
|
+
message: e.message
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
########################################################################
|
|
161
|
+
|
|
162
|
+
# Gets cached self validators or builds them
|
|
163
|
+
#
|
|
164
|
+
# @return [Array<AttributeValidator>] Validators for :_self attributes
|
|
165
|
+
def self_validators
|
|
166
|
+
@self_validators ||= build_self_validators
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Gets cached regular validators or builds them
|
|
170
|
+
#
|
|
171
|
+
# @return [Hash] Hash of nested_attribute => validator
|
|
172
|
+
def regular_validators
|
|
173
|
+
@regular_validators ||= build_regular_validators
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
########################################################################
|
|
177
|
+
|
|
178
|
+
# Builds validators for :_self attributes (simple array elements)
|
|
179
|
+
#
|
|
180
|
+
# @return [Array<AttributeValidator>] Array of validators
|
|
181
|
+
def build_self_validators
|
|
182
|
+
@attribute.collection_of_attributes
|
|
183
|
+
.select { |attr| attr.name == :_self }
|
|
184
|
+
.map do |self_attribute|
|
|
185
|
+
validator = AttributeValidator.new(self_attribute)
|
|
186
|
+
validator.validate_schema!
|
|
187
|
+
validator
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Builds validators for regular attributes (complex array elements)
|
|
192
|
+
#
|
|
193
|
+
# @return [Hash] Hash of nested_attribute => validator
|
|
194
|
+
def build_regular_validators
|
|
195
|
+
@attribute.collection_of_attributes
|
|
196
|
+
.reject { |attr| attr.name == :_self }
|
|
197
|
+
.each_with_object({}) do |nested_attribute, cache|
|
|
198
|
+
validator = AttributeValidator.new(nested_attribute)
|
|
199
|
+
validator.validate_schema!
|
|
200
|
+
cache[nested_attribute] = validator
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Entity
|
|
5
|
+
module Attribute
|
|
6
|
+
module Validation
|
|
7
|
+
# Validates nested object (hash) attributes against their defined structure.
|
|
8
|
+
#
|
|
9
|
+
# ## Purpose
|
|
10
|
+
#
|
|
11
|
+
# Performs validation for nested object attributes during the validation phase.
|
|
12
|
+
# Ensures hash values conform to the nested attribute definitions.
|
|
13
|
+
#
|
|
14
|
+
# ## Responsibilities
|
|
15
|
+
#
|
|
16
|
+
# 1. **Structure Validation** - Validates hash structure matches definition
|
|
17
|
+
# 2. **Attribute Validation** - Validates each nested attribute's value
|
|
18
|
+
# 3. **Type Safety** - Ensures value is a Hash before validation
|
|
19
|
+
# 4. **Validator Caching** - Builds and caches validators for performance
|
|
20
|
+
#
|
|
21
|
+
# ## Usage
|
|
22
|
+
#
|
|
23
|
+
# Used for object-type attributes with nested definitions:
|
|
24
|
+
#
|
|
25
|
+
# ```ruby
|
|
26
|
+
# object :author do
|
|
27
|
+
# string :name, :required
|
|
28
|
+
# string :email
|
|
29
|
+
# integer :age
|
|
30
|
+
# end
|
|
31
|
+
# ```
|
|
32
|
+
#
|
|
33
|
+
# Validates: `{ name: "Alice", email: "alice@example.com", age: 30 }`
|
|
34
|
+
#
|
|
35
|
+
# ## Usage in Code
|
|
36
|
+
#
|
|
37
|
+
# Called by AttributeValidator for nested objects:
|
|
38
|
+
#
|
|
39
|
+
# validator = NestedObjectValidator.new(attribute)
|
|
40
|
+
# validator.validate!(hash_value)
|
|
41
|
+
#
|
|
42
|
+
# ## Validation Flow
|
|
43
|
+
#
|
|
44
|
+
# 1. Check if value is a Hash
|
|
45
|
+
# 2. Build validators for all nested attributes (cached)
|
|
46
|
+
# 3. For each nested attribute:
|
|
47
|
+
# - Extract value from hash
|
|
48
|
+
# - Validate using AttributeValidator
|
|
49
|
+
# 4. Raise exception if any validation fails
|
|
50
|
+
#
|
|
51
|
+
# ## Architecture
|
|
52
|
+
#
|
|
53
|
+
# Uses:
|
|
54
|
+
# - `AttributeValidator` - Validates individual nested attributes
|
|
55
|
+
# - Caches validators to avoid rebuilding on each validation
|
|
56
|
+
class NestedObjectValidator
|
|
57
|
+
attr_reader :attribute
|
|
58
|
+
|
|
59
|
+
# Creates a new nested object validator
|
|
60
|
+
#
|
|
61
|
+
# @param attribute [Attribute::Base] The object-type attribute with nested attributes
|
|
62
|
+
def initialize(attribute)
|
|
63
|
+
@attribute = attribute
|
|
64
|
+
@validators_cache = nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Validates all nested attributes in a hash
|
|
68
|
+
# Skips validation if value is not a Hash
|
|
69
|
+
#
|
|
70
|
+
# @param hash [Hash] The hash to validate
|
|
71
|
+
# @raise [Treaty::Exceptions::Validation] If any nested validation fails
|
|
72
|
+
# @return [void]
|
|
73
|
+
def validate!(hash)
|
|
74
|
+
return unless hash.is_a?(Hash)
|
|
75
|
+
|
|
76
|
+
validators.each do |nested_attribute, nested_validator|
|
|
77
|
+
nested_value = hash.fetch(nested_attribute.name, nil)
|
|
78
|
+
nested_validator.validate_value!(nested_value)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Gets cached validators or builds them
|
|
85
|
+
#
|
|
86
|
+
# @return [Hash] Hash of nested_attribute => validator
|
|
87
|
+
def validators
|
|
88
|
+
@validators ||= build_validators
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Builds validators for all nested attributes
|
|
92
|
+
#
|
|
93
|
+
# @return [Hash] Hash of nested_attribute => validator
|
|
94
|
+
def build_validators
|
|
95
|
+
attribute.collection_of_attributes.each_with_object({}) do |nested_attribute, cache|
|
|
96
|
+
validator = AttributeValidator.new(nested_attribute)
|
|
97
|
+
validator.validate_schema!
|
|
98
|
+
cache[nested_attribute] = validator
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|