treaty 0.0.1 → 0.1.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -18
  3. data/Rakefile +4 -2
  4. data/lib/treaty/attribute/base.rb +172 -0
  5. data/lib/treaty/attribute/builder/base.rb +142 -0
  6. data/lib/treaty/attribute/collection.rb +65 -0
  7. data/lib/treaty/attribute/helper_mapper.rb +72 -0
  8. data/lib/treaty/attribute/option/base.rb +159 -0
  9. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +87 -0
  10. data/lib/treaty/attribute/option/modifiers/default_modifier.rb +103 -0
  11. data/lib/treaty/attribute/option/registry.rb +128 -0
  12. data/lib/treaty/attribute/option/registry_initializer.rb +90 -0
  13. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +80 -0
  14. data/lib/treaty/attribute/option/validators/required_validator.rb +94 -0
  15. data/lib/treaty/attribute/option/validators/type_validator.rb +153 -0
  16. data/lib/treaty/attribute/option_normalizer.rb +150 -0
  17. data/lib/treaty/attribute/option_orchestrator.rb +186 -0
  18. data/lib/treaty/attribute/validation/attribute_validator.rb +144 -0
  19. data/lib/treaty/attribute/validation/base.rb +93 -0
  20. data/lib/treaty/attribute/validation/nested_array_validator.rb +194 -0
  21. data/lib/treaty/attribute/validation/nested_object_validator.rb +103 -0
  22. data/lib/treaty/attribute/validation/nested_transformer.rb +240 -0
  23. data/lib/treaty/attribute/validation/orchestrator/base.rb +196 -0
  24. data/lib/treaty/base.rb +9 -0
  25. data/lib/treaty/configuration.rb +17 -0
  26. data/lib/treaty/context/callable.rb +24 -0
  27. data/lib/treaty/context/dsl.rb +12 -0
  28. data/lib/treaty/context/workspace.rb +28 -0
  29. data/lib/treaty/controller/dsl.rb +38 -0
  30. data/lib/treaty/engine.rb +37 -0
  31. data/lib/treaty/exceptions/base.rb +8 -0
  32. data/lib/treaty/exceptions/class_name.rb +11 -0
  33. data/lib/treaty/exceptions/deprecated.rb +8 -0
  34. data/lib/treaty/exceptions/execution.rb +8 -0
  35. data/lib/treaty/exceptions/method_name.rb +8 -0
  36. data/lib/treaty/exceptions/nested_attributes.rb +8 -0
  37. data/lib/treaty/exceptions/strategy.rb +8 -0
  38. data/lib/treaty/exceptions/unexpected.rb +8 -0
  39. data/lib/treaty/exceptions/validation.rb +8 -0
  40. data/lib/treaty/info/builder.rb +122 -0
  41. data/lib/treaty/info/dsl.rb +26 -0
  42. data/lib/treaty/info/result.rb +13 -0
  43. data/lib/treaty/request/attribute/attribute.rb +24 -0
  44. data/lib/treaty/request/attribute/builder.rb +22 -0
  45. data/lib/treaty/request/attribute/validation/orchestrator.rb +27 -0
  46. data/lib/treaty/request/attribute/validator.rb +50 -0
  47. data/lib/treaty/request/factory.rb +32 -0
  48. data/lib/treaty/request/scope/collection.rb +21 -0
  49. data/lib/treaty/request/scope/factory.rb +42 -0
  50. data/lib/treaty/response/attribute/attribute.rb +24 -0
  51. data/lib/treaty/response/attribute/builder.rb +22 -0
  52. data/lib/treaty/response/attribute/validation/orchestrator.rb +27 -0
  53. data/lib/treaty/response/attribute/validator.rb +44 -0
  54. data/lib/treaty/response/factory.rb +38 -0
  55. data/lib/treaty/response/scope/collection.rb +21 -0
  56. data/lib/treaty/response/scope/factory.rb +42 -0
  57. data/lib/treaty/result.rb +22 -0
  58. data/lib/treaty/strategy.rb +31 -0
  59. data/lib/treaty/support/loader.rb +24 -0
  60. data/lib/treaty/version.rb +8 -1
  61. data/lib/treaty/versions/collection.rb +15 -0
  62. data/lib/treaty/versions/dsl.rb +30 -0
  63. data/lib/treaty/versions/execution/request.rb +151 -0
  64. data/lib/treaty/versions/executor.rb +14 -0
  65. data/lib/treaty/versions/factory.rb +93 -0
  66. data/lib/treaty/versions/resolver.rb +72 -0
  67. data/lib/treaty/versions/semantic.rb +22 -0
  68. data/lib/treaty/versions/workspace.rb +40 -0
  69. data/lib/treaty.rb +3 -3
  70. metadata +184 -27
  71. data/.standard.yml +0 -3
  72. data/CHANGELOG.md +0 -5
  73. data/CODE_OF_CONDUCT.md +0 -84
  74. data/LICENSE.txt +0 -21
  75. data/sig/treaty.rbs +0 -4
  76. data/treaty.gemspec +0 -35
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Validation
6
+ # Base class for validation strategies (adapter vs non-adapter).
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Provides common interface for validation strategies used in Treaty.
11
+ # Subclasses implement specific validation logic for different strategies.
12
+ #
13
+ # ## Responsibilities
14
+ #
15
+ # 1. **Strategy Interface** - Defines common validation interface
16
+ # 2. **Factory Pattern** - Provides class-level validate! method
17
+ # 3. **Strategy Detection** - Checks if adapter strategy is active
18
+ #
19
+ # ## Subclasses
20
+ #
21
+ # - Request::Validation - Validates request data (uses Orchestrator::Request)
22
+ # - Response::Validation - Validates response data (uses Orchestrator::Response)
23
+ #
24
+ # ## Usage
25
+ #
26
+ # Subclasses must implement:
27
+ # - `validate!` - Performs validation and returns transformed data
28
+ #
29
+ # Example usage:
30
+ # Request::Validation.validate!(version_factory: factory, data: params)
31
+ #
32
+ # ## Strategy Pattern
33
+ #
34
+ # The validation system supports two strategies:
35
+ # - **Adapter Strategy** - Adapts between different API versions
36
+ # - **Standard Strategy** - Direct version handling
37
+ #
38
+ # This base class provides `adapter_strategy?` helper to check current strategy.
39
+ #
40
+ # ## Factory Method
41
+ #
42
+ # The `self.validate!(...)` class method provides a convenient factory pattern:
43
+ # ```ruby
44
+ # Request::Validation.validate!(version_factory: factory, data: params)
45
+ # # Equivalent to:
46
+ # Request::Validation.new(version_factory: factory).validate!(data: params)
47
+ # ```
48
+ #
49
+ # ## Architecture
50
+ #
51
+ # Works with:
52
+ # - VersionFactory - Provides version and strategy information
53
+ # - Orchestrator::Base - Performs actual validation and transformation
54
+ class Base
55
+ # Class-level factory method for validation
56
+ # Creates instance and calls validate!
57
+ #
58
+ # @param args [Hash] Arguments passed to initialize and validate!
59
+ # @return [Hash] Validated and transformed data
60
+ def self.validate!(...)
61
+ new(...).validate!
62
+ end
63
+
64
+ # Creates a new validation instance
65
+ #
66
+ # @param version_factory [VersionFactory] Factory containing version and strategy
67
+ def initialize(version_factory:)
68
+ @version_factory = version_factory
69
+ end
70
+
71
+ # Performs validation and transformation
72
+ # Must be implemented in subclasses
73
+ #
74
+ # @raise [NotImplementedError] If subclass doesn't implement
75
+ # @return [Hash] Validated and transformed data
76
+ def validate!
77
+ # TODO: It is necessary to implement a translation system (I18n).
78
+ raise Treaty::Exceptions::Validation,
79
+ "Subclass must implement the validate! method"
80
+ end
81
+
82
+ private
83
+
84
+ # Checks if adapter strategy is active
85
+ #
86
+ # @return [Boolean] True if using adapter strategy
87
+ def adapter_strategy?
88
+ @version_factory.strategy_instance.adapter?
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Validation
6
+ # Validates array elements against nested attribute definitions.
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Performs validation for nested array attributes during the validation phase.
11
+ # Handles both simple arrays (with :_self scope) and complex arrays (objects).
12
+ #
13
+ # ## Responsibilities
14
+ #
15
+ # 1. **Simple Array Validation** - Validates primitive values in arrays
16
+ # 2. **Complex Array Validation** - Validates hash objects within arrays
17
+ # 3. **Error Context** - Provides clear error messages with array index
18
+ # 4. **Type Checking** - Ensures elements match expected types
19
+ #
20
+ # ## Array Types
21
+ #
22
+ # ### Simple Array (`:_self` scope)
23
+ # Array containing primitive values (strings, integers, etc.)
24
+ #
25
+ # ```ruby
26
+ # array :tags do
27
+ # string :_self # Array of strings
28
+ # end
29
+ # ```
30
+ #
31
+ # Validates: `["ruby", "rails", "api"]`
32
+ #
33
+ # ### Complex Array (regular attributes)
34
+ # Array containing hash objects with defined structure
35
+ #
36
+ # ```ruby
37
+ # array :authors do
38
+ # string :name, :required
39
+ # string :email
40
+ # end
41
+ # ```
42
+ #
43
+ # Validates: `[{ name: "Alice", email: "alice@example.com" }, ...]`
44
+ #
45
+ # ## Usage
46
+ #
47
+ # Called by AttributeValidator for nested arrays:
48
+ #
49
+ # validator = NestedArrayValidator.new(attribute)
50
+ # validator.validate!(array_value)
51
+ #
52
+ # ## Error Handling
53
+ #
54
+ # Provides contextual error messages including:
55
+ # - Array attribute name
56
+ # - Element index (0-based)
57
+ # - Specific validation error
58
+ #
59
+ # Example error:
60
+ # "Error in array 'tags' at index 2: Element must match one of the defined types"
61
+ #
62
+ # ## Architecture
63
+ #
64
+ # Uses:
65
+ # - `AttributeValidator` - Validates individual elements
66
+ # - Caches validators for performance
67
+ # - Separates self validators from regular validators
68
+ class NestedArrayValidator
69
+ # Creates a new nested array validator
70
+ #
71
+ # @param attribute [Attribute::Base] The array-type attribute with nested attributes
72
+ def initialize(attribute)
73
+ @attribute = attribute
74
+ @self_validators = nil
75
+ @regular_validators = nil
76
+ end
77
+
78
+ # Validates all items in an array
79
+ # Skips validation if value is not an Array
80
+ #
81
+ # @param array [Array] The array to validate
82
+ # @raise [Treaty::Exceptions::Validation] If any item validation fails
83
+ # @return [void]
84
+ def validate!(array)
85
+ return unless array.is_a?(Array)
86
+
87
+ array.each_with_index do |array_item, index|
88
+ validate_self_array_item!(array_item, index) if self_validators.any?
89
+
90
+ validate_regular_array_item!(array_item, index) if regular_validators.any?
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ # Validates array item for simple arrays (with :_self scope)
97
+ # Simple array contains primitive values: strings, integers, datetimes, etc.
98
+ # Example: ["ruby", "rails", "api"] where each item is a String
99
+ #
100
+ # @param array_item [String, Integer, DateTime] Primitive value from simple array
101
+ # @param index [Integer] Array index for error messages
102
+ # @raise [Treaty::Exceptions::Validation] If primitive value doesn't match defined type
103
+ # @return [void]
104
+ def validate_self_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
105
+ errors = []
106
+
107
+ validated = self_validators.any? do |validator|
108
+ validator.validate_value!(array_item)
109
+ true
110
+ rescue Treaty::Exceptions::Validation => e
111
+ errors << e.message
112
+ false
113
+ end
114
+
115
+ return if validated
116
+
117
+ # TODO: It is necessary to implement a translation system (I18n).
118
+ raise Treaty::Exceptions::Validation,
119
+ "Error in array '#{@attribute.name}' at index #{index}: " \
120
+ "Item must match one of the defined types. Errors: #{errors.join('; ')}"
121
+ end
122
+
123
+ # Validates array item for complex arrays (with regular attributes)
124
+ # Complex array contains hash objects with defined structure
125
+ # Example: [{ name: "Alice", email: "alice@example.com" }, ...] where each item is a Hash
126
+ #
127
+ # @param array_item [Hash] Hash object from complex array
128
+ # @param index [Integer] Array index for error messages
129
+ # @raise [Treaty::Exceptions::Validation] If item is not Hash or nested validation fails
130
+ # @return [void]
131
+ def validate_regular_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
132
+ unless array_item.is_a?(Hash)
133
+ # TODO: It is necessary to implement a translation system (I18n).
134
+ raise Treaty::Exceptions::Validation,
135
+ "Error in array '#{@attribute.name}' at index #{index}: Expected Hash but got #{array_item.class}"
136
+ end
137
+
138
+ regular_validators.each do |nested_attribute, validator|
139
+ nested_value = array_item.fetch(nested_attribute.name, nil)
140
+ validator.validate_value!(nested_value)
141
+ rescue Treaty::Exceptions::Validation => e
142
+ # TODO: It is necessary to implement a translation system (I18n).
143
+ raise Treaty::Exceptions::Validation,
144
+ "Error in array '#{@attribute.name}' at index #{index}: #{e.message}"
145
+ end
146
+ end
147
+
148
+ ########################################################################
149
+
150
+ # Gets cached self validators or builds them
151
+ #
152
+ # @return [Array<AttributeValidator>] Validators for :_self attributes
153
+ def self_validators
154
+ @self_validators ||= build_self_validators
155
+ end
156
+
157
+ # Gets cached regular validators or builds them
158
+ #
159
+ # @return [Hash] Hash of nested_attribute => validator
160
+ def regular_validators
161
+ @regular_validators ||= build_regular_validators
162
+ end
163
+
164
+ ########################################################################
165
+
166
+ # Builds validators for :_self attributes (simple array elements)
167
+ #
168
+ # @return [Array<AttributeValidator>] Array of validators
169
+ def build_self_validators
170
+ @attribute.collection_of_attributes
171
+ .select { |attr| attr.name == :_self }
172
+ .map do |self_attribute|
173
+ validator = AttributeValidator.new(self_attribute)
174
+ validator.validate_schema!
175
+ validator
176
+ end
177
+ end
178
+
179
+ # Builds validators for regular attributes (complex array elements)
180
+ #
181
+ # @return [Hash] Hash of nested_attribute => validator
182
+ def build_regular_validators
183
+ @attribute.collection_of_attributes
184
+ .reject { |attr| attr.name == :_self }
185
+ .each_with_object({}) do |nested_attribute, cache|
186
+ validator = AttributeValidator.new(nested_attribute)
187
+ validator.validate_schema!
188
+ cache[nested_attribute] = validator
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Validation
6
+ # Validates nested object (hash) attributes against their defined structure.
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Performs validation for nested object attributes during the validation phase.
11
+ # Ensures hash values conform to the nested attribute definitions.
12
+ #
13
+ # ## Responsibilities
14
+ #
15
+ # 1. **Structure Validation** - Validates hash structure matches definition
16
+ # 2. **Attribute Validation** - Validates each nested attribute's value
17
+ # 3. **Type Safety** - Ensures value is a Hash before validation
18
+ # 4. **Validator Caching** - Builds and caches validators for performance
19
+ #
20
+ # ## Usage
21
+ #
22
+ # Used for object-type attributes with nested definitions:
23
+ #
24
+ # ```ruby
25
+ # object :author do
26
+ # string :name, :required
27
+ # string :email
28
+ # integer :age
29
+ # end
30
+ # ```
31
+ #
32
+ # Validates: `{ name: "Alice", email: "alice@example.com", age: 30 }`
33
+ #
34
+ # ## Usage in Code
35
+ #
36
+ # Called by AttributeValidator for nested objects:
37
+ #
38
+ # validator = NestedObjectValidator.new(attribute)
39
+ # validator.validate!(hash_value)
40
+ #
41
+ # ## Validation Flow
42
+ #
43
+ # 1. Check if value is a Hash
44
+ # 2. Build validators for all nested attributes (cached)
45
+ # 3. For each nested attribute:
46
+ # - Extract value from hash
47
+ # - Validate using AttributeValidator
48
+ # 4. Raise exception if any validation fails
49
+ #
50
+ # ## Architecture
51
+ #
52
+ # Uses:
53
+ # - `AttributeValidator` - Validates individual nested attributes
54
+ # - Caches validators to avoid rebuilding on each validation
55
+ class NestedObjectValidator
56
+ attr_reader :attribute
57
+
58
+ # Creates a new nested object validator
59
+ #
60
+ # @param attribute [Attribute::Base] The object-type attribute with nested attributes
61
+ def initialize(attribute)
62
+ @attribute = attribute
63
+ @validators_cache = nil
64
+ end
65
+
66
+ # Validates all nested attributes in a hash
67
+ # Skips validation if value is not a Hash
68
+ #
69
+ # @param hash [Hash] The hash to validate
70
+ # @raise [Treaty::Exceptions::Validation] If any nested validation fails
71
+ # @return [void]
72
+ def validate!(hash)
73
+ return unless hash.is_a?(Hash)
74
+
75
+ validators.each do |nested_attribute, nested_validator|
76
+ nested_value = hash.fetch(nested_attribute.name, nil)
77
+ nested_validator.validate_value!(nested_value)
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ # Gets cached validators or builds them
84
+ #
85
+ # @return [Hash] Hash of nested_attribute => validator
86
+ def validators
87
+ @validators ||= build_validators
88
+ end
89
+
90
+ # Builds validators for all nested attributes
91
+ #
92
+ # @return [Hash] Hash of nested_attribute => validator
93
+ def build_validators
94
+ attribute.collection_of_attributes.each_with_object({}) do |nested_attribute, cache|
95
+ validator = AttributeValidator.new(nested_attribute)
96
+ validator.validate_schema!
97
+ cache[nested_attribute] = validator
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Validation
6
+ # Handles transformation of nested attributes (objects and arrays).
7
+ # Extracted from Orchestrator::Base to reduce complexity.
8
+ class NestedTransformer
9
+ SELF_SCOPE = :_self
10
+ private_constant :SELF_SCOPE
11
+
12
+ attr_reader :attribute
13
+
14
+ # Creates a new nested transformer
15
+ #
16
+ # @param attribute [Attribute::Base] The attribute with nested structure
17
+ def initialize(attribute)
18
+ @attribute = attribute
19
+ end
20
+
21
+ # Transforms nested attribute value (object or array)
22
+ # Returns original value if nil or not nested
23
+ #
24
+ # @param value [Object] The value to transform
25
+ # @return [Object] Transformed value
26
+ def transform(value)
27
+ return value if value.nil?
28
+
29
+ case attribute.type
30
+ when :object
31
+ transform_object(value)
32
+ when :array
33
+ transform_array(value)
34
+ else
35
+ value
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # Transforms object (hash) value
42
+ #
43
+ # @param value [Hash] The hash to transform
44
+ # @return [Hash] Transformed hash
45
+ def transform_object(value)
46
+ return value unless attribute.nested?
47
+
48
+ transformer = ObjectTransformer.new(attribute)
49
+ transformer.transform(value)
50
+ end
51
+
52
+ # Transforms array value
53
+ #
54
+ # @param value [Array] The array to transform
55
+ # @return [Array] Transformed array
56
+ def transform_array(value)
57
+ return value unless attribute.nested?
58
+
59
+ transformer = ArrayTransformer.new(attribute)
60
+ transformer.transform(value)
61
+ end
62
+
63
+ # Transforms object (hash) with nested attributes
64
+ class ObjectTransformer
65
+ attr_reader :attribute
66
+
67
+ # Creates a new object transformer
68
+ #
69
+ # @param attribute [Attribute::Base] The object-type attribute
70
+ def initialize(attribute)
71
+ @attribute = attribute
72
+ end
73
+
74
+ # Transforms hash by processing all nested attributes
75
+ #
76
+ # @param value [Hash] The source hash
77
+ # @return [Hash] Transformed hash with processed attributes
78
+ def transform(value)
79
+ transformed = {}
80
+
81
+ attribute.collection_of_attributes.each do |nested_attribute|
82
+ process_attribute(nested_attribute, value, transformed)
83
+ end
84
+
85
+ transformed
86
+ end
87
+
88
+ private
89
+
90
+ # Processes a single nested attribute
91
+ # Validates, transforms, and adds to target hash
92
+ #
93
+ # @param nested_attribute [Attribute::Base] Attribute to process
94
+ # @param source_hash [Hash] Source data
95
+ # @param target_hash [Hash] Target hash to populate
96
+ # @return [void]
97
+ def process_attribute(nested_attribute, source_hash, target_hash) # rubocop:disable Metrics/MethodLength
98
+ source_name = nested_attribute.name
99
+ nested_value = source_hash.fetch(source_name, nil)
100
+
101
+ validator = AttributeValidator.new(nested_attribute)
102
+ validator.validate_schema!
103
+
104
+ transformed_value = if nested_attribute.nested?
105
+ nested_transformer = NestedTransformer.new(nested_attribute)
106
+ validator.validate_type!(nested_value) unless nested_value.nil?
107
+ validator.validate_required!(nested_value)
108
+ nested_transformer.transform(nested_value)
109
+ else
110
+ validator.validate_value!(nested_value)
111
+ validator.transform_value(nested_value)
112
+ end
113
+
114
+ target_name = validator.target_name
115
+ target_hash[target_name] = transformed_value
116
+ end
117
+ end
118
+
119
+ # Transforms array with nested attributes
120
+ class ArrayTransformer
121
+ SELF_SCOPE = :_self
122
+ private_constant :SELF_SCOPE
123
+
124
+ attr_reader :attribute
125
+
126
+ # Creates a new array transformer
127
+ #
128
+ # @param attribute [Attribute::Base] The array-type attribute
129
+ def initialize(attribute)
130
+ @attribute = attribute
131
+ end
132
+
133
+ # Transforms array by processing each element
134
+ # Handles both simple arrays (:_self) and complex arrays (objects)
135
+ #
136
+ # @param value [Array] The source array
137
+ # @return [Array] Transformed array
138
+ def transform(value)
139
+ value.each_with_index.map do |item, index|
140
+ if simple_array?
141
+ validate_simple_element(item, index)
142
+ item
143
+ else
144
+ transform_array_item(item, index)
145
+ end
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ # Checks if this is a simple array (primitive values)
152
+ #
153
+ # @return [Boolean] True if array contains primitive values with :_self scope
154
+ def simple_array?
155
+ attribute.collection_of_attributes.size == 1 &&
156
+ attribute.collection_of_attributes.first.name == SELF_SCOPE
157
+ end
158
+
159
+ # Validates a simple array element (primitive value)
160
+ #
161
+ # @param item [Object] Array element to validate
162
+ # @param index [Integer] Element index for error messages
163
+ # @raise [Treaty::Exceptions::Validation] If validation fails
164
+ # @return [void]
165
+ def validate_simple_element(item, index) # rubocop:disable Metrics/MethodLength
166
+ self_attr = attribute.collection_of_attributes.first
167
+ validator = AttributeValidator.new(self_attr)
168
+ validator.validate_schema!
169
+
170
+ begin
171
+ validator.validate_value!(item)
172
+ rescue Treaty::Exceptions::Validation => e
173
+ raise Treaty::Exceptions::Validation,
174
+ "Error in array '#{attribute.name}' at index #{index}: " \
175
+ "Element must match one of the defined types. " \
176
+ "Errors: #{e.message}"
177
+ end
178
+ end
179
+
180
+ # Transforms a complex array element (hash object)
181
+ #
182
+ # @param item [Hash] Array element to transform
183
+ # @param index [Integer] Element index for error messages
184
+ # @raise [Treaty::Exceptions::Validation] If item is not a Hash
185
+ # @return [Hash] Transformed hash
186
+ def transform_array_item(item, index)
187
+ unless item.is_a?(Hash)
188
+ raise Treaty::Exceptions::Validation,
189
+ "Error in array '#{attribute.name}' at index #{index}: " \
190
+ "Expected Hash but got #{item.class}"
191
+ end
192
+
193
+ transformed = {}
194
+
195
+ attribute.collection_of_attributes.each do |nested_attribute|
196
+ process_attribute(nested_attribute, item, transformed, index)
197
+ end
198
+
199
+ transformed
200
+ end
201
+
202
+ # Processes a single nested attribute in array element
203
+ # Validates, transforms, and adds to target hash
204
+ #
205
+ # @param nested_attribute [Attribute::Base] Attribute to process
206
+ # @param source_hash [Hash] Source data
207
+ # @param target_hash [Hash] Target hash to populate
208
+ # @param index [Integer] Array index for error messages
209
+ # @raise [Treaty::Exceptions::Validation] If validation fails
210
+ # @return [void]
211
+ def process_attribute(nested_attribute, source_hash, target_hash, index) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
212
+ source_name = nested_attribute.name
213
+ nested_value = source_hash.fetch(source_name, nil)
214
+
215
+ validator = AttributeValidator.new(nested_attribute)
216
+ validator.validate_schema!
217
+
218
+ begin
219
+ transformed_value = if nested_attribute.nested?
220
+ nested_transformer = NestedTransformer.new(nested_attribute)
221
+ validator.validate_type!(nested_value) unless nested_value.nil?
222
+ validator.validate_required!(nested_value)
223
+ nested_transformer.transform(nested_value)
224
+ else
225
+ validator.validate_value!(nested_value)
226
+ validator.transform_value(nested_value)
227
+ end
228
+ rescue Treaty::Exceptions::Validation => e
229
+ raise Treaty::Exceptions::Validation,
230
+ "Error in array '#{attribute.name}' at index #{index}: #{e.message}"
231
+ end
232
+
233
+ target_name = validator.target_name
234
+ target_hash[target_name] = transformed_value
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end