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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/locales/en.yml +3 -3
  4. data/lib/treaty/engine.rb +1 -1
  5. data/lib/treaty/{attribute/entity → entity/attribute}/attribute.rb +4 -4
  6. data/lib/treaty/entity/attribute/base.rb +184 -0
  7. data/lib/treaty/entity/attribute/builder/base.rb +275 -0
  8. data/lib/treaty/entity/attribute/collection.rb +67 -0
  9. data/lib/treaty/entity/attribute/dsl.rb +92 -0
  10. data/lib/treaty/entity/attribute/helper_mapper.rb +74 -0
  11. data/lib/treaty/entity/attribute/option/base.rb +190 -0
  12. data/lib/treaty/entity/attribute/option/conditionals/base.rb +92 -0
  13. data/lib/treaty/entity/attribute/option/conditionals/if_conditional.rb +136 -0
  14. data/lib/treaty/entity/attribute/option/conditionals/unless_conditional.rb +153 -0
  15. data/lib/treaty/entity/attribute/option/modifiers/as_modifier.rb +93 -0
  16. data/lib/treaty/entity/attribute/option/modifiers/cast_modifier.rb +285 -0
  17. data/lib/treaty/entity/attribute/option/modifiers/computed_modifier.rb +128 -0
  18. data/lib/treaty/entity/attribute/option/modifiers/default_modifier.rb +105 -0
  19. data/lib/treaty/entity/attribute/option/modifiers/transform_modifier.rb +114 -0
  20. data/lib/treaty/entity/attribute/option/registry.rb +157 -0
  21. data/lib/treaty/entity/attribute/option/registry_initializer.rb +117 -0
  22. data/lib/treaty/entity/attribute/option/validators/format_validator.rb +222 -0
  23. data/lib/treaty/entity/attribute/option/validators/inclusion_validator.rb +94 -0
  24. data/lib/treaty/entity/attribute/option/validators/required_validator.rb +100 -0
  25. data/lib/treaty/entity/attribute/option/validators/type_validator.rb +219 -0
  26. data/lib/treaty/entity/attribute/option_normalizer.rb +168 -0
  27. data/lib/treaty/entity/attribute/option_orchestrator.rb +192 -0
  28. data/lib/treaty/entity/attribute/validation/attribute_validator.rb +147 -0
  29. data/lib/treaty/entity/attribute/validation/base.rb +76 -0
  30. data/lib/treaty/entity/attribute/validation/nested_array_validator.rb +207 -0
  31. data/lib/treaty/entity/attribute/validation/nested_object_validator.rb +105 -0
  32. data/lib/treaty/entity/attribute/validation/nested_transformer.rb +432 -0
  33. data/lib/treaty/entity/attribute/validation/orchestrator/base.rb +262 -0
  34. data/lib/treaty/entity/base.rb +90 -0
  35. data/lib/treaty/entity/builder.rb +44 -0
  36. data/lib/treaty/{info/entity → entity/info}/builder.rb +8 -8
  37. data/lib/treaty/{info/entity → entity/info}/dsl.rb +2 -2
  38. data/lib/treaty/{info/entity → entity/info}/result.rb +2 -2
  39. data/lib/treaty/entity.rb +7 -79
  40. data/lib/treaty/request/attribute/attribute.rb +1 -1
  41. data/lib/treaty/request/attribute/builder.rb +2 -2
  42. data/lib/treaty/request/entity.rb +1 -1
  43. data/lib/treaty/request/factory.rb +5 -5
  44. data/lib/treaty/request/validator.rb +1 -1
  45. data/lib/treaty/response/attribute/attribute.rb +1 -1
  46. data/lib/treaty/response/attribute/builder.rb +2 -2
  47. data/lib/treaty/response/entity.rb +1 -1
  48. data/lib/treaty/response/factory.rb +5 -5
  49. data/lib/treaty/response/validator.rb +1 -1
  50. data/lib/treaty/version.rb +1 -1
  51. metadata +35 -34
  52. data/lib/treaty/attribute/base.rb +0 -182
  53. data/lib/treaty/attribute/builder/base.rb +0 -273
  54. data/lib/treaty/attribute/collection.rb +0 -65
  55. data/lib/treaty/attribute/dsl.rb +0 -90
  56. data/lib/treaty/attribute/entity/builder.rb +0 -46
  57. data/lib/treaty/attribute/helper_mapper.rb +0 -72
  58. data/lib/treaty/attribute/option/base.rb +0 -188
  59. data/lib/treaty/attribute/option/conditionals/base.rb +0 -90
  60. data/lib/treaty/attribute/option/conditionals/if_conditional.rb +0 -134
  61. data/lib/treaty/attribute/option/conditionals/unless_conditional.rb +0 -151
  62. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +0 -91
  63. data/lib/treaty/attribute/option/modifiers/cast_modifier.rb +0 -283
  64. data/lib/treaty/attribute/option/modifiers/computed_modifier.rb +0 -126
  65. data/lib/treaty/attribute/option/modifiers/default_modifier.rb +0 -103
  66. data/lib/treaty/attribute/option/modifiers/transform_modifier.rb +0 -112
  67. data/lib/treaty/attribute/option/registry.rb +0 -155
  68. data/lib/treaty/attribute/option/registry_initializer.rb +0 -115
  69. data/lib/treaty/attribute/option/validators/format_validator.rb +0 -220
  70. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +0 -92
  71. data/lib/treaty/attribute/option/validators/required_validator.rb +0 -98
  72. data/lib/treaty/attribute/option/validators/type_validator.rb +0 -217
  73. data/lib/treaty/attribute/option_normalizer.rb +0 -166
  74. data/lib/treaty/attribute/option_orchestrator.rb +0 -190
  75. data/lib/treaty/attribute/validation/attribute_validator.rb +0 -145
  76. data/lib/treaty/attribute/validation/base.rb +0 -74
  77. data/lib/treaty/attribute/validation/nested_array_validator.rb +0 -205
  78. data/lib/treaty/attribute/validation/nested_object_validator.rb +0 -103
  79. data/lib/treaty/attribute/validation/nested_transformer.rb +0 -430
  80. 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