treaty 0.6.0 → 0.8.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 +9 -4
- data/config/locales/en.yml +6 -4
- data/lib/treaty/attribute/base.rb +15 -7
- data/lib/treaty/attribute/builder/base.rb +4 -4
- data/lib/treaty/attribute/collection.rb +1 -1
- data/lib/treaty/attribute/dsl.rb +90 -0
- data/lib/treaty/attribute/entity/attribute.rb +25 -0
- data/lib/treaty/attribute/entity/builder.rb +23 -0
- data/lib/treaty/attribute/option/base.rb +17 -1
- data/lib/treaty/attribute/option/modifiers/as_modifier.rb +5 -3
- data/lib/treaty/attribute/option/modifiers/default_modifier.rb +1 -1
- data/lib/treaty/attribute/option/registry.rb +4 -4
- data/lib/treaty/attribute/option/validators/inclusion_validator.rb +20 -8
- data/lib/treaty/attribute/option/validators/required_validator.rb +8 -2
- data/lib/treaty/attribute/option/validators/type_validator.rb +51 -40
- data/lib/treaty/attribute/option_orchestrator.rb +8 -6
- data/lib/treaty/attribute/validation/nested_array_validator.rb +21 -15
- data/lib/treaty/attribute/validation/nested_transformer.rb +24 -18
- data/lib/treaty/attribute/validation/orchestrator/base.rb +67 -83
- data/lib/treaty/base.rb +1 -1
- data/lib/treaty/controller/dsl.rb +4 -1
- data/lib/treaty/entity.rb +84 -0
- data/lib/treaty/exceptions/nested_attributes.rb +2 -2
- data/lib/treaty/info/entity/builder.rb +50 -0
- data/lib/treaty/info/entity/dsl.rb +28 -0
- data/lib/treaty/info/entity/result.rb +15 -0
- data/lib/treaty/info/rest/builder.rb +110 -0
- data/lib/treaty/info/rest/dsl.rb +28 -0
- data/lib/treaty/info/rest/result.rb +15 -0
- data/lib/treaty/request/attribute/attribute.rb +1 -0
- data/lib/treaty/request/attribute/builder.rb +1 -0
- data/lib/treaty/request/entity.rb +33 -0
- data/lib/treaty/request/factory.rb +63 -14
- data/lib/treaty/request/validator.rb +65 -0
- data/lib/treaty/response/attribute/attribute.rb +1 -0
- data/lib/treaty/response/attribute/builder.rb +1 -0
- data/lib/treaty/response/entity.rb +33 -0
- data/lib/treaty/response/factory.rb +63 -14
- data/lib/treaty/response/validator.rb +57 -0
- data/lib/treaty/version.rb +1 -1
- data/lib/treaty/versions/execution/request.rb +10 -5
- data/lib/treaty/versions/factory.rb +16 -5
- data/lib/treaty/versions/resolver.rb +8 -2
- data/lib/treaty/versions/workspace.rb +2 -2
- metadata +15 -12
- data/lib/treaty/info/builder.rb +0 -122
- data/lib/treaty/info/dsl.rb +0 -26
- data/lib/treaty/info/result.rb +0 -13
- data/lib/treaty/request/attribute/validation/orchestrator.rb +0 -27
- data/lib/treaty/request/attribute/validator.rb +0 -50
- data/lib/treaty/request/scope/collection.rb +0 -21
- data/lib/treaty/request/scope/factory.rb +0 -42
- data/lib/treaty/response/attribute/validation/orchestrator.rb +0 -27
- data/lib/treaty/response/attribute/validator.rb +0 -44
- data/lib/treaty/response/scope/collection.rb +0 -21
- data/lib/treaty/response/scope/factory.rb +0 -42
|
@@ -8,7 +8,7 @@ module Treaty
|
|
|
8
8
|
# ## Purpose
|
|
9
9
|
#
|
|
10
10
|
# Performs validation for nested array attributes during the validation phase.
|
|
11
|
-
# Handles both simple arrays (with :_self
|
|
11
|
+
# Handles both simple arrays (with :_self attribute) and complex arrays (objects).
|
|
12
12
|
#
|
|
13
13
|
# ## Responsibilities
|
|
14
14
|
#
|
|
@@ -19,7 +19,7 @@ module Treaty
|
|
|
19
19
|
#
|
|
20
20
|
# ## Array Types
|
|
21
21
|
#
|
|
22
|
-
# ### Simple Array (`:_self`
|
|
22
|
+
# ### Simple Array (`:_self` attribute)
|
|
23
23
|
# Array containing primitive values (strings, integers, etc.)
|
|
24
24
|
#
|
|
25
25
|
# ```ruby
|
|
@@ -93,7 +93,7 @@ module Treaty
|
|
|
93
93
|
|
|
94
94
|
private
|
|
95
95
|
|
|
96
|
-
# Validates array item for simple arrays (with :_self
|
|
96
|
+
# Validates array item for simple arrays (with :_self attribute)
|
|
97
97
|
# Simple array contains primitive values: strings, integers, datetimes, etc.
|
|
98
98
|
# Example: ["ruby", "rails", "api"] where each item is a String
|
|
99
99
|
#
|
|
@@ -115,10 +115,12 @@ module Treaty
|
|
|
115
115
|
return if validated
|
|
116
116
|
|
|
117
117
|
raise Treaty::Exceptions::Validation,
|
|
118
|
-
I18n.t(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
I18n.t(
|
|
119
|
+
"treaty.attributes.validators.nested.array.element_validation_error",
|
|
120
|
+
attribute: @attribute.name,
|
|
121
|
+
index:,
|
|
122
|
+
errors: errors.join("; ")
|
|
123
|
+
)
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
# Validates array item for complex arrays (with regular attributes)
|
|
@@ -132,10 +134,12 @@ module Treaty
|
|
|
132
134
|
def validate_regular_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
|
|
133
135
|
unless array_item.is_a?(Hash)
|
|
134
136
|
raise Treaty::Exceptions::Validation,
|
|
135
|
-
I18n.t(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
I18n.t(
|
|
138
|
+
"treaty.attributes.validators.nested.array.element_type_error",
|
|
139
|
+
attribute: @attribute.name,
|
|
140
|
+
index:,
|
|
141
|
+
actual: array_item.class
|
|
142
|
+
)
|
|
139
143
|
end
|
|
140
144
|
|
|
141
145
|
regular_validators.each do |nested_attribute, validator|
|
|
@@ -143,10 +147,12 @@ module Treaty
|
|
|
143
147
|
validator.validate_value!(nested_value)
|
|
144
148
|
rescue Treaty::Exceptions::Validation => e
|
|
145
149
|
raise Treaty::Exceptions::Validation,
|
|
146
|
-
I18n.t(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
I18n.t(
|
|
151
|
+
"treaty.attributes.validators.nested.array.attribute_error",
|
|
152
|
+
attribute: @attribute.name,
|
|
153
|
+
index:,
|
|
154
|
+
message: e.message
|
|
155
|
+
)
|
|
150
156
|
end
|
|
151
157
|
end
|
|
152
158
|
|
|
@@ -6,8 +6,8 @@ module Treaty
|
|
|
6
6
|
# Handles transformation of nested attributes (objects and arrays).
|
|
7
7
|
# Extracted from Orchestrator::Base to reduce complexity.
|
|
8
8
|
class NestedTransformer
|
|
9
|
-
|
|
10
|
-
private_constant :
|
|
9
|
+
SELF_OBJECT = :_self
|
|
10
|
+
private_constant :SELF_OBJECT
|
|
11
11
|
|
|
12
12
|
attr_reader :attribute
|
|
13
13
|
|
|
@@ -118,8 +118,8 @@ module Treaty
|
|
|
118
118
|
|
|
119
119
|
# Transforms array with nested attributes
|
|
120
120
|
class ArrayTransformer
|
|
121
|
-
|
|
122
|
-
private_constant :
|
|
121
|
+
SELF_OBJECT = :_self
|
|
122
|
+
private_constant :SELF_OBJECT
|
|
123
123
|
|
|
124
124
|
attr_reader :attribute
|
|
125
125
|
|
|
@@ -150,10 +150,10 @@ module Treaty
|
|
|
150
150
|
|
|
151
151
|
# Checks if this is a simple array (primitive values)
|
|
152
152
|
#
|
|
153
|
-
# @return [Boolean] True if array contains primitive values with :_self
|
|
153
|
+
# @return [Boolean] True if array contains primitive values with :_self attribute
|
|
154
154
|
def simple_array?
|
|
155
155
|
attribute.collection_of_attributes.size == 1 &&
|
|
156
|
-
attribute.collection_of_attributes.first.name ==
|
|
156
|
+
attribute.collection_of_attributes.first.name == SELF_OBJECT
|
|
157
157
|
end
|
|
158
158
|
|
|
159
159
|
# Validates a simple array element (primitive value)
|
|
@@ -171,10 +171,12 @@ module Treaty
|
|
|
171
171
|
validator.validate_value!(item)
|
|
172
172
|
rescue Treaty::Exceptions::Validation => e
|
|
173
173
|
raise Treaty::Exceptions::Validation,
|
|
174
|
-
I18n.t(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
I18n.t(
|
|
175
|
+
"treaty.attributes.validators.nested.array.element_validation_error",
|
|
176
|
+
attribute: attribute.name,
|
|
177
|
+
index:,
|
|
178
|
+
errors: e.message
|
|
179
|
+
)
|
|
178
180
|
end
|
|
179
181
|
end
|
|
180
182
|
|
|
@@ -187,10 +189,12 @@ module Treaty
|
|
|
187
189
|
def transform_array_item(item, index) # rubocop:disable Metrics/MethodLength
|
|
188
190
|
unless item.is_a?(Hash)
|
|
189
191
|
raise Treaty::Exceptions::Validation,
|
|
190
|
-
I18n.t(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
I18n.t(
|
|
193
|
+
"treaty.attributes.validators.nested.array.element_type_error",
|
|
194
|
+
attribute: attribute.name,
|
|
195
|
+
index:,
|
|
196
|
+
actual: item.class
|
|
197
|
+
)
|
|
194
198
|
end
|
|
195
199
|
|
|
196
200
|
transformed = {}
|
|
@@ -230,10 +234,12 @@ module Treaty
|
|
|
230
234
|
end
|
|
231
235
|
rescue Treaty::Exceptions::Validation => e
|
|
232
236
|
raise Treaty::Exceptions::Validation,
|
|
233
|
-
I18n.t(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
I18n.t(
|
|
238
|
+
"treaty.attributes.validators.nested.array.attribute_error",
|
|
239
|
+
attribute: attribute.name,
|
|
240
|
+
index:,
|
|
241
|
+
message: e.message
|
|
242
|
+
)
|
|
237
243
|
end
|
|
238
244
|
|
|
239
245
|
target_name = validator.target_name
|
|
@@ -9,12 +9,12 @@ module Treaty
|
|
|
9
9
|
# ## Purpose
|
|
10
10
|
#
|
|
11
11
|
# Coordinates the validation and transformation of request/response data for a specific
|
|
12
|
-
# API version. Processes all
|
|
13
|
-
#
|
|
12
|
+
# API version. Processes all attributes, applying validations and transformations
|
|
13
|
+
# defined in the treaty DSL.
|
|
14
14
|
#
|
|
15
15
|
# ## Responsibilities
|
|
16
16
|
#
|
|
17
|
-
# 1. **
|
|
17
|
+
# 1. **Attribute Processing** - Iterates through all defined attributes
|
|
18
18
|
# 2. **Attribute Validation** - Validates each attribute's value
|
|
19
19
|
# 3. **Data Transformation** - Transforms values (defaults, renaming)
|
|
20
20
|
# 4. **Nested Handling** - Delegates nested structures to NestedTransformer
|
|
@@ -23,17 +23,16 @@ module Treaty
|
|
|
23
23
|
# ## Usage
|
|
24
24
|
#
|
|
25
25
|
# Subclasses must implement:
|
|
26
|
-
# - `
|
|
27
|
-
# - `scope_data_for(name)` - Extracts data for a specific scope
|
|
26
|
+
# - `collection_of_attributes` - Returns attributes for this context (request/response)
|
|
28
27
|
#
|
|
29
28
|
# Example:
|
|
30
29
|
# orchestrator = Request::Orchestrator.new(version_factory: factory, data: params)
|
|
31
30
|
# validated_data = orchestrator.validate!
|
|
32
31
|
#
|
|
33
|
-
# ## Special
|
|
32
|
+
# ## Special Case: object :_self
|
|
34
33
|
#
|
|
35
|
-
# - Normal
|
|
36
|
-
# - Self
|
|
34
|
+
# - Normal object: `{ object_name: { ... } }`
|
|
35
|
+
# - Self object (`:_self`): Attributes merged directly into parent
|
|
37
36
|
#
|
|
38
37
|
# ## Architecture
|
|
39
38
|
#
|
|
@@ -41,13 +40,13 @@ module Treaty
|
|
|
41
40
|
# - `AttributeValidator` - Validates individual attributes
|
|
42
41
|
# - `NestedTransformer` - Handles nested objects and arrays
|
|
43
42
|
#
|
|
44
|
-
# The
|
|
45
|
-
# - Orchestrator: High-level flow and
|
|
43
|
+
# The design separates concerns:
|
|
44
|
+
# - Orchestrator: High-level flow and attribute iteration
|
|
46
45
|
# - Validator: Individual attribute validation
|
|
47
46
|
# - Transformer: Nested structure transformation
|
|
48
47
|
class Base
|
|
49
|
-
|
|
50
|
-
private_constant :
|
|
48
|
+
SELF_OBJECT = :_self
|
|
49
|
+
private_constant :SELF_OBJECT
|
|
51
50
|
|
|
52
51
|
attr_reader :version_factory, :data
|
|
53
52
|
|
|
@@ -69,17 +68,22 @@ module Treaty
|
|
|
69
68
|
@data = data
|
|
70
69
|
end
|
|
71
70
|
|
|
72
|
-
# Validates and transforms all
|
|
73
|
-
# Iterates through
|
|
71
|
+
# Validates and transforms all attributes
|
|
72
|
+
# Iterates through attributes, processes them, handles :_self objects
|
|
74
73
|
#
|
|
75
|
-
# @return [Hash] Transformed data with all
|
|
74
|
+
# @return [Hash] Transformed data with all attributes processed
|
|
76
75
|
def validate!
|
|
77
76
|
transformed_data = {}
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
collection_of_attributes.each do |attribute|
|
|
79
|
+
transformed_value = validate_and_transform_attribute!(attribute)
|
|
80
|
+
|
|
81
|
+
if attribute.name == SELF_OBJECT && attribute.type == :object
|
|
82
|
+
# For object :_self, merge nested attributes to root
|
|
83
|
+
transformed_data.merge!(transformed_value)
|
|
84
|
+
else
|
|
85
|
+
transformed_data[attribute.name] = transformed_value
|
|
86
|
+
end
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
transformed_data
|
|
@@ -87,103 +91,83 @@ module Treaty
|
|
|
87
91
|
|
|
88
92
|
private
|
|
89
93
|
|
|
90
|
-
# Returns collection of
|
|
94
|
+
# Returns collection of attributes for this context
|
|
91
95
|
# Must be implemented in subclasses
|
|
92
96
|
#
|
|
93
97
|
# @raise [Treaty::Exceptions::Validation] If not implemented
|
|
94
|
-
# @return [
|
|
95
|
-
def
|
|
98
|
+
# @return [Treaty::Attribute::Collection] Collection of attributes
|
|
99
|
+
def collection_of_attributes
|
|
96
100
|
raise Treaty::Exceptions::Validation,
|
|
97
101
|
I18n.t("treaty.attributes.validators.nested.orchestrator.collection_not_implemented")
|
|
98
102
|
end
|
|
99
103
|
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
# @param scope_factory [ScopeFactory] The scope to validate
|
|
103
|
-
# @return [void]
|
|
104
|
-
def validate_scope!(scope_factory)
|
|
105
|
-
scope_data = scope_data_for(scope_factory.name)
|
|
106
|
-
|
|
107
|
-
validators_for_scope(scope_factory).each do |attribute, validator|
|
|
108
|
-
value = scope_data.fetch(attribute.name, nil)
|
|
109
|
-
validator.validate_value!(value)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Gets cached validators for scope or builds them
|
|
104
|
+
# Gets cached validators for attributes or builds them
|
|
114
105
|
#
|
|
115
|
-
# @param scope_factory [ScopeFactory] The scope factory
|
|
116
106
|
# @return [Hash] Hash of attribute => validator
|
|
117
|
-
def
|
|
118
|
-
@
|
|
119
|
-
@validators_cache[scope_factory] ||= build_validators_for_scope(scope_factory)
|
|
107
|
+
def validators_for_attributes
|
|
108
|
+
@validators_for_attributes ||= build_validators_for_attributes
|
|
120
109
|
end
|
|
121
110
|
|
|
122
|
-
# Builds validators for all attributes
|
|
111
|
+
# Builds validators for all attributes
|
|
123
112
|
#
|
|
124
|
-
# @param scope_factory [ScopeFactory] The scope factory
|
|
125
113
|
# @return [Hash] Hash of attribute => validator
|
|
126
|
-
def
|
|
127
|
-
|
|
114
|
+
def build_validators_for_attributes
|
|
115
|
+
collection_of_attributes.each_with_object({}) do |attribute, cache|
|
|
128
116
|
validator = AttributeValidator.new(attribute)
|
|
129
117
|
validator.validate_schema!
|
|
130
118
|
cache[attribute] = validator
|
|
131
119
|
end
|
|
132
120
|
end
|
|
133
121
|
|
|
134
|
-
#
|
|
135
|
-
# Must be implemented in subclasses
|
|
136
|
-
#
|
|
137
|
-
# @param _name [Symbol] The scope name
|
|
138
|
-
# @raise [Treaty::Exceptions::Validation] If not implemented
|
|
139
|
-
# @return [Hash] Scope data
|
|
140
|
-
def scope_data_for(_name)
|
|
141
|
-
raise Treaty::Exceptions::Validation,
|
|
142
|
-
I18n.t("treaty.attributes.validators.nested.orchestrator.scope_data_not_implemented")
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Validates and transforms all attributes in a scope
|
|
122
|
+
# Validates and transforms a single attribute
|
|
146
123
|
# Handles both nested and regular attributes
|
|
147
124
|
#
|
|
148
|
-
# @param
|
|
149
|
-
# @return [
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
transformed_value = validator.transform_value(value)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
target_name = validator.target_name
|
|
169
|
-
|
|
170
|
-
transformed_scope_data[target_name] = transformed_value
|
|
125
|
+
# @param attribute [Attribute] The attribute to process
|
|
126
|
+
# @return [Object] Transformed attribute value
|
|
127
|
+
def validate_and_transform_attribute!(attribute) # rubocop:disable Metrics/MethodLength
|
|
128
|
+
validator = validators_for_attributes.fetch(attribute)
|
|
129
|
+
|
|
130
|
+
# For :_self object, get data from root; otherwise from attribute key
|
|
131
|
+
value = if attribute.name == SELF_OBJECT && attribute.type == :object
|
|
132
|
+
data
|
|
133
|
+
else
|
|
134
|
+
data.fetch(attribute.name, nil)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
if attribute.nested?
|
|
138
|
+
validate_and_transform_nested(attribute, value, validator)
|
|
139
|
+
else
|
|
140
|
+
validator.validate_value!(value)
|
|
141
|
+
validator.transform_value(value)
|
|
171
142
|
end
|
|
172
|
-
|
|
173
|
-
transformed_scope_data
|
|
174
143
|
end
|
|
175
144
|
|
|
176
145
|
# Validates and transforms nested attribute (object/array)
|
|
177
146
|
# Delegates transformation to NestedTransformer
|
|
178
147
|
#
|
|
179
148
|
# @param attribute [Attribute::Base] The nested attribute
|
|
180
|
-
# @param value [Object] The value to validate and transform
|
|
149
|
+
# @param value [Object, nil] The value to validate and transform
|
|
181
150
|
# @param validator [AttributeValidator] The validator instance
|
|
182
|
-
# @return [Object] Transformed nested value
|
|
151
|
+
# @return [Object, nil] Transformed nested value or nil
|
|
152
|
+
#
|
|
153
|
+
# @note Flow control:
|
|
154
|
+
# - If value is nil and attribute is required → validate_required! raises exception
|
|
155
|
+
# - If value is nil and attribute is optional → validate_required! does nothing, returns nil
|
|
156
|
+
# - If value is not nil → proceeds to transformation (value guaranteed non-nil)
|
|
183
157
|
def validate_and_transform_nested(attribute, value, validator)
|
|
158
|
+
# Step 1: Validate type if value is present
|
|
184
159
|
validator.validate_type!(value) unless value.nil?
|
|
160
|
+
|
|
161
|
+
# Step 2: Validate required constraint
|
|
162
|
+
# This will raise an exception if attribute is required and value is nil
|
|
185
163
|
validator.validate_required!(value)
|
|
186
164
|
|
|
165
|
+
# Step 3: Early return for nil values
|
|
166
|
+
# Only reaches here if attribute is optional and value is nil
|
|
167
|
+
return nil if value.nil?
|
|
168
|
+
|
|
169
|
+
# Step 4: Transform non-nil value
|
|
170
|
+
# At this point, value is guaranteed to be non-nil
|
|
187
171
|
transformer = NestedTransformer.new(attribute)
|
|
188
172
|
transformer.transform(value)
|
|
189
173
|
end
|
data/lib/treaty/base.rb
CHANGED
|
@@ -25,7 +25,10 @@ module Treaty
|
|
|
25
25
|
treaty_class_name.constantize
|
|
26
26
|
rescue NameError
|
|
27
27
|
raise Treaty::Exceptions::ClassName,
|
|
28
|
-
I18n.t(
|
|
28
|
+
I18n.t(
|
|
29
|
+
"treaty.controller.treaty_class_not_found",
|
|
30
|
+
class_name: treaty_class_name
|
|
31
|
+
)
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def treaty_class_name
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
# Base class for defining DTO (Data Transfer Object) entities in Treaty.
|
|
5
|
+
#
|
|
6
|
+
# ## Purpose
|
|
7
|
+
#
|
|
8
|
+
# Treaty::Entity provides a base class for creating reusable DTO classes
|
|
9
|
+
# that can be used in both request and response definitions. This allows
|
|
10
|
+
# for better code organization and reusability of common data structures.
|
|
11
|
+
#
|
|
12
|
+
# ## Usage
|
|
13
|
+
#
|
|
14
|
+
# Create a DTO class by inheriting from Treaty::Entity:
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# class PostEntity < Treaty::Entity
|
|
18
|
+
# string :id
|
|
19
|
+
# string :title
|
|
20
|
+
# string :content
|
|
21
|
+
# datetime :created_at
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
# Then use it in your treaty definitions:
|
|
26
|
+
#
|
|
27
|
+
# ```ruby
|
|
28
|
+
# class CreateTreaty < ApplicationTreaty
|
|
29
|
+
# version 1 do
|
|
30
|
+
# request PostEntity
|
|
31
|
+
# response 201, PostEntity
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
# ```
|
|
35
|
+
#
|
|
36
|
+
# ## Attribute Defaults
|
|
37
|
+
#
|
|
38
|
+
# Unlike request/response blocks, Entity attributes are required by default:
|
|
39
|
+
# - All attributes have `required: true` unless explicitly marked as `:optional`
|
|
40
|
+
# - Use `:optional` helper to make attributes optional:
|
|
41
|
+
# ```ruby
|
|
42
|
+
# string :title # required by default
|
|
43
|
+
# string :summary, :optional # optional
|
|
44
|
+
# ```
|
|
45
|
+
#
|
|
46
|
+
# ## Features
|
|
47
|
+
#
|
|
48
|
+
# - **Type Safety** - Enforce strict type checking for all attributes
|
|
49
|
+
# - **Nested Structures** - Support for nested objects and arrays
|
|
50
|
+
# - **Validation** - Built-in validation for all attribute types
|
|
51
|
+
# - **Reusability** - Define once, use in multiple treaties
|
|
52
|
+
# - **Options** - Full support for attribute options (required, default, as, etc.)
|
|
53
|
+
#
|
|
54
|
+
# ## Supported Types
|
|
55
|
+
#
|
|
56
|
+
# - `string` - String values
|
|
57
|
+
# - `integer` - Integer values
|
|
58
|
+
# - `boolean` - Boolean values (true/false)
|
|
59
|
+
# - `datetime` - DateTime values
|
|
60
|
+
# - `array` - Array values (with nested type definition)
|
|
61
|
+
# - `object` - Object values (with nested attributes)
|
|
62
|
+
class Entity
|
|
63
|
+
include Info::Entity::DSL
|
|
64
|
+
include Attribute::DSL
|
|
65
|
+
|
|
66
|
+
class << self
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Creates an Attribute::Entity::Attribute for this Entity class
|
|
70
|
+
#
|
|
71
|
+
# @return [Attribute::Entity::Attribute] Created attribute instance
|
|
72
|
+
def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
|
|
73
|
+
Attribute::Entity::Attribute.new(
|
|
74
|
+
name,
|
|
75
|
+
type,
|
|
76
|
+
*helpers,
|
|
77
|
+
nesting_level:,
|
|
78
|
+
**options,
|
|
79
|
+
&block
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -18,7 +18,7 @@ module Treaty
|
|
|
18
18
|
#
|
|
19
19
|
# ```ruby
|
|
20
20
|
# request do
|
|
21
|
-
#
|
|
21
|
+
# object :user do
|
|
22
22
|
# object :profile do
|
|
23
23
|
# object :settings do
|
|
24
24
|
# object :preferences do
|
|
@@ -57,7 +57,7 @@ module Treaty
|
|
|
57
57
|
#
|
|
58
58
|
# - Keep nesting shallow (2-3 levels maximum)
|
|
59
59
|
# - Consider flattening deeply nested structures
|
|
60
|
-
# - Use separate
|
|
60
|
+
# - Use separate objects instead of deep nesting
|
|
61
61
|
# - Refactor complex structures into simpler ones
|
|
62
62
|
class NestedAttributes < Base
|
|
63
63
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Info
|
|
5
|
+
module Entity
|
|
6
|
+
class Builder
|
|
7
|
+
attr_reader :attributes
|
|
8
|
+
|
|
9
|
+
def self.build(...)
|
|
10
|
+
new.build(...)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def build(collection_of_attributes:)
|
|
14
|
+
build_all(
|
|
15
|
+
attributes: collection_of_attributes
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def build_all(attributes:)
|
|
24
|
+
@attributes = build_versions_with(attributes)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
##########################################################################
|
|
28
|
+
|
|
29
|
+
def build_versions_with(collection, current_level = 0)
|
|
30
|
+
collection.to_h do |attribute|
|
|
31
|
+
[
|
|
32
|
+
attribute.name,
|
|
33
|
+
{
|
|
34
|
+
type: attribute.type,
|
|
35
|
+
options: attribute.options,
|
|
36
|
+
attributes: build_nested_attributes(attribute, current_level)
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def build_nested_attributes(attribute, current_level)
|
|
43
|
+
return {} unless attribute.nested?
|
|
44
|
+
|
|
45
|
+
build_versions_with(attribute.collection_of_attributes, current_level + 1)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Info
|
|
5
|
+
module Entity
|
|
6
|
+
module DSL
|
|
7
|
+
def self.included(base)
|
|
8
|
+
base.extend(ClassMethods)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def info
|
|
13
|
+
builder = Builder.build(
|
|
14
|
+
collection_of_attributes:
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
Result.new(builder)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# API: Treaty Web
|
|
21
|
+
def treaty?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|