treaty 0.17.0 → 0.18.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 +2 -2
- data/config/locales/en.yml +4 -0
- data/lib/treaty/attribute/builder/base.rb +131 -1
- data/lib/treaty/attribute/entity/builder.rb +23 -0
- data/lib/treaty/attribute/option/conditionals/if_conditional.rb +5 -5
- data/lib/treaty/attribute/option/conditionals/unless_conditional.rb +9 -9
- data/lib/treaty/attribute/option/modifiers/computed_modifier.rb +13 -13
- data/lib/treaty/entity.rb +15 -11
- data/lib/treaty/request/attribute/builder.rb +23 -0
- data/lib/treaty/request/factory.rb +1 -1
- data/lib/treaty/response/attribute/builder.rb +23 -0
- data/lib/treaty/response/factory.rb +1 -1
- data/lib/treaty/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed95ed37e55c61ca17604d5ef395f93405adbb8259064a08b9857b032d267c93
|
|
4
|
+
data.tar.gz: 48a97168517da5bbca25f73174e5caa666939505193d556b6612ea238f67b98a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ae24db235dc2b5127d42e4823d7a103dc6bb85f39bd6ef7ef2ab27cf406bf6adb555177bea649bc3231fb7f8741753c217d976d11b65c10ae5675757aeeca98
|
|
7
|
+
data.tar.gz: 6985a2a682ee06acf5ae091bef6c6c15469ede66d4eebd6bf510973a2e84d16aa3144e82429e9ebbaaa662628eec7054287d708a9917f67e8214987174aa2b03
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
15
|
> [!WARNING]
|
|
16
|
-
> **Development Status**: Treaty is currently under active development in the 0.x version series. Breaking changes may occur between minor versions (0.x) as we refine the API and add new features. The library will stabilize with the 1.0 release. We recommend pinning to specific patch versions in your Gemfile (e.g., `gem "treaty", "~> 0.
|
|
16
|
+
> **Development Status**: Treaty is currently under active development in the 0.x version series. Breaking changes may occur between minor versions (0.x) as we refine the API and add new features. The library will stabilize with the 1.0 release. We recommend pinning to specific patch versions in your Gemfile (e.g., `gem "treaty", "~> 0.18.0"`) until the 1.0 release.
|
|
17
17
|
|
|
18
18
|
## 📚 Documentation
|
|
19
19
|
|
|
@@ -33,7 +33,7 @@ Treaty provides a complete solution for building versioned APIs in Ruby on Rails
|
|
|
33
33
|
- **Type Safety** - Enforce strict type checking for request and response data
|
|
34
34
|
- **API Versioning** - Manage multiple concurrent API versions effortlessly
|
|
35
35
|
- **Unified Architecture** - Request blocks, response blocks, and Entity classes share the same validation system
|
|
36
|
-
- **Entity Classes
|
|
36
|
+
- **Entity Classes** - Define reusable entity classes for better code organization
|
|
37
37
|
- **Built-in Validation** - Validate incoming requests and outgoing responses automatically
|
|
38
38
|
- **Data Transformation** - Transform data seamlessly between different API versions
|
|
39
39
|
- **Inventory System** - Pass controller-specific data to services efficiently
|
data/config/locales/en.yml
CHANGED
|
@@ -83,6 +83,10 @@ en:
|
|
|
83
83
|
builder:
|
|
84
84
|
not_implemented: "%{class} must implement #create_attribute"
|
|
85
85
|
create_attribute_not_implemented: "Subclass %{class} must implement #create_attribute method"
|
|
86
|
+
deep_copy_not_implemented: "%{class} must implement #deep_copy_attribute"
|
|
87
|
+
invalid_entity_class: "use_entity expects a Treaty::Entity subclass, got %{type}: %{value}"
|
|
88
|
+
use_entity_after_attributes: "use_entity must be the only statement in the block. Cannot call use_entity after defining other attributes."
|
|
89
|
+
attributes_after_use_entity: "use_entity must be the only statement in the block. Cannot define attributes after calling use_entity."
|
|
86
90
|
|
|
87
91
|
# Attribute-level errors
|
|
88
92
|
errors:
|
|
@@ -16,6 +16,7 @@ module Treaty
|
|
|
16
16
|
# 2. **Method Dispatch** - Routes type methods (string, integer, etc.) to attribute creation
|
|
17
17
|
# 3. **Helper Support** - Handles helper symbols in various positions
|
|
18
18
|
# 4. **Nesting Tracking** - Tracks nesting level for nested attributes
|
|
19
|
+
# 5. **Entity Reuse** - Supports use_entity for copying attributes from Entity classes
|
|
19
20
|
#
|
|
20
21
|
# ## DSL Usage
|
|
21
22
|
#
|
|
@@ -33,6 +34,18 @@ module Treaty
|
|
|
33
34
|
# end
|
|
34
35
|
# ```
|
|
35
36
|
#
|
|
37
|
+
# ## Entity Reuse
|
|
38
|
+
#
|
|
39
|
+
# You can use `use_entity` to copy attributes from an Entity class:
|
|
40
|
+
#
|
|
41
|
+
# ```ruby
|
|
42
|
+
# object :author do
|
|
43
|
+
# use_entity(AuthorEntity)
|
|
44
|
+
# end
|
|
45
|
+
# ```
|
|
46
|
+
#
|
|
47
|
+
# Note: `use_entity` must be the only statement in the block.
|
|
48
|
+
#
|
|
36
49
|
# ## Method Dispatch
|
|
37
50
|
#
|
|
38
51
|
# ### Type-based Methods
|
|
@@ -63,12 +76,14 @@ module Treaty
|
|
|
63
76
|
#
|
|
64
77
|
# Subclasses must implement:
|
|
65
78
|
# - `create_attribute` - Creates the appropriate attribute type (Request/Response)
|
|
79
|
+
# - `deep_copy_attribute` - Deep copies an attribute with adjusted nesting level
|
|
66
80
|
#
|
|
67
81
|
# ## Architecture
|
|
68
82
|
#
|
|
69
83
|
# Used by:
|
|
70
84
|
# - Request::Builder - For request attribute definitions
|
|
71
85
|
# - Response::Builder - For response attribute definitions
|
|
86
|
+
# - Entity::Builder - For entity attribute definitions
|
|
72
87
|
class Base
|
|
73
88
|
attr_reader :nesting_level,
|
|
74
89
|
:collection_of_attributes
|
|
@@ -80,6 +95,34 @@ module Treaty
|
|
|
80
95
|
def initialize(collection_of_attributes, nesting_level)
|
|
81
96
|
@collection_of_attributes = collection_of_attributes
|
|
82
97
|
@nesting_level = nesting_level
|
|
98
|
+
@use_entity_called = false
|
|
99
|
+
@attributes_defined = false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Uses an Entity class to copy its attributes into this builder's collection.
|
|
103
|
+
# Must be the ONLY statement in the block - no other attributes allowed.
|
|
104
|
+
#
|
|
105
|
+
# @param entity_class [Class] Entity class (must be Treaty::Entity subclass)
|
|
106
|
+
# @raise [Treaty::Exceptions::Validation] if entity_class is invalid
|
|
107
|
+
# @raise [Treaty::Exceptions::Validation] if mixed with other attributes
|
|
108
|
+
# @return [void]
|
|
109
|
+
#
|
|
110
|
+
# @example Using an Entity in a nested object
|
|
111
|
+
# object :author do
|
|
112
|
+
# use_entity(AuthorEntity)
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
# @example Using an Entity in a nested array
|
|
116
|
+
# array :items, :optional do
|
|
117
|
+
# use_entity(ItemEntity)
|
|
118
|
+
# end
|
|
119
|
+
def use_entity(entity_class)
|
|
120
|
+
validate_use_entity_preconditions!
|
|
121
|
+
validate_entity_class!(entity_class)
|
|
122
|
+
|
|
123
|
+
@use_entity_called = true
|
|
124
|
+
|
|
125
|
+
copy_attributes_from_entity(entity_class)
|
|
83
126
|
end
|
|
84
127
|
|
|
85
128
|
# Defines an attribute with explicit type
|
|
@@ -91,6 +134,10 @@ module Treaty
|
|
|
91
134
|
# @param block [Proc] Block for nested attributes
|
|
92
135
|
# @return [void]
|
|
93
136
|
def attribute(name, type, *helpers, **options, &block)
|
|
137
|
+
validate_no_use_entity_called!
|
|
138
|
+
|
|
139
|
+
@attributes_defined = true
|
|
140
|
+
|
|
94
141
|
@collection_of_attributes << create_attribute(
|
|
95
142
|
name,
|
|
96
143
|
type,
|
|
@@ -133,10 +180,93 @@ module Treaty
|
|
|
133
180
|
# @raise [Treaty::Exceptions::NotImplemented] If subclass doesn't implement
|
|
134
181
|
# @return [Attribute::Base] Created attribute instance
|
|
135
182
|
def create_attribute(*)
|
|
136
|
-
# Must be implemented in subclasses
|
|
137
183
|
raise Treaty::Exceptions::NotImplemented,
|
|
138
184
|
I18n.t("treaty.attributes.builder.not_implemented", class: self.class)
|
|
139
185
|
end
|
|
186
|
+
|
|
187
|
+
# Validates that use_entity can be called (no attributes defined before)
|
|
188
|
+
#
|
|
189
|
+
# @raise [Treaty::Exceptions::Validation] if attributes were defined before use_entity
|
|
190
|
+
def validate_use_entity_preconditions!
|
|
191
|
+
return unless @attributes_defined
|
|
192
|
+
|
|
193
|
+
raise Treaty::Exceptions::Validation,
|
|
194
|
+
I18n.t("treaty.attributes.builder.use_entity_after_attributes")
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Validates that no use_entity was called before defining attributes
|
|
198
|
+
#
|
|
199
|
+
# @raise [Treaty::Exceptions::Validation] if use_entity was already called
|
|
200
|
+
def validate_no_use_entity_called!
|
|
201
|
+
return unless @use_entity_called
|
|
202
|
+
|
|
203
|
+
raise Treaty::Exceptions::Validation,
|
|
204
|
+
I18n.t("treaty.attributes.builder.attributes_after_use_entity")
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Validates that entity_class is a valid Treaty::Entity subclass
|
|
208
|
+
#
|
|
209
|
+
# @param entity_class [Class] Entity class to validate
|
|
210
|
+
# @raise [Treaty::Exceptions::Validation] if entity_class is not valid
|
|
211
|
+
def validate_entity_class!(entity_class)
|
|
212
|
+
return if entity_class.is_a?(Class) && entity_class < Treaty::Entity
|
|
213
|
+
|
|
214
|
+
raise Treaty::Exceptions::Validation,
|
|
215
|
+
I18n.t(
|
|
216
|
+
"treaty.attributes.builder.invalid_entity_class",
|
|
217
|
+
type: entity_class.class,
|
|
218
|
+
value: entity_class
|
|
219
|
+
)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Copies all attributes from entity_class to this builder's collection
|
|
223
|
+
# with adjusted nesting levels.
|
|
224
|
+
#
|
|
225
|
+
# @param entity_class [Class] Source entity class
|
|
226
|
+
def copy_attributes_from_entity(entity_class)
|
|
227
|
+
entity_class.collection_of_attributes.each do |source_attribute|
|
|
228
|
+
copied_attribute = deep_copy_attribute(source_attribute, @nesting_level)
|
|
229
|
+
@collection_of_attributes << copied_attribute
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Deep copies an attribute with adjusted nesting level.
|
|
234
|
+
# Must be implemented by subclasses to use proper attribute types.
|
|
235
|
+
#
|
|
236
|
+
# @param source_attribute [Attribute::Base] Attribute to copy
|
|
237
|
+
# @param new_nesting_level [Integer] New nesting level for copied attribute
|
|
238
|
+
# @return [Attribute::Base] Copied attribute with correct type
|
|
239
|
+
def deep_copy_attribute(_source_attribute, _new_nesting_level)
|
|
240
|
+
raise Treaty::Exceptions::NotImplemented,
|
|
241
|
+
I18n.t(
|
|
242
|
+
"treaty.attributes.builder.deep_copy_not_implemented",
|
|
243
|
+
class: self.class
|
|
244
|
+
)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Deep copies options hash, preserving Proc references
|
|
248
|
+
# and recursively handling nested Hash/Array structures.
|
|
249
|
+
#
|
|
250
|
+
# @param options [Hash] Options to copy
|
|
251
|
+
# @return [Hash] Copied options
|
|
252
|
+
def deep_copy_options(options)
|
|
253
|
+
options.transform_values { |value| deep_copy_value(value) }
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Deep copies a single value, handling nested structures.
|
|
257
|
+
# Immutable types (Proc, Symbol, Numeric, nil, true, false) are returned as-is.
|
|
258
|
+
# Hash and Array are recursively copied. Strings are duplicated if not frozen.
|
|
259
|
+
#
|
|
260
|
+
# @param value [Object] Value to copy
|
|
261
|
+
# @return [Object] Copied value
|
|
262
|
+
def deep_copy_value(value)
|
|
263
|
+
case value
|
|
264
|
+
when Hash then value.transform_values { |v| deep_copy_value(v) }
|
|
265
|
+
when Array then value.map { |v| deep_copy_value(v) }
|
|
266
|
+
when String then value.frozen? ? value : value.dup
|
|
267
|
+
else value
|
|
268
|
+
end
|
|
269
|
+
end
|
|
140
270
|
end
|
|
141
271
|
end
|
|
142
272
|
end
|
|
@@ -17,6 +17,29 @@ module Treaty
|
|
|
17
17
|
&block
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# Deep copies an attribute with adjusted nesting level for Entity context.
|
|
22
|
+
#
|
|
23
|
+
# @param source_attribute [Treaty::Attribute::Base] Attribute to copy
|
|
24
|
+
# @param new_nesting_level [Integer] New nesting level
|
|
25
|
+
# @return [Entity::Attribute] Copied attribute
|
|
26
|
+
def deep_copy_attribute(source_attribute, new_nesting_level) # rubocop:disable Metrics/MethodLength
|
|
27
|
+
copied = Attribute.new(
|
|
28
|
+
source_attribute.name,
|
|
29
|
+
source_attribute.type,
|
|
30
|
+
nesting_level: new_nesting_level,
|
|
31
|
+
**deep_copy_options(source_attribute.options)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return copied unless source_attribute.nested?
|
|
35
|
+
|
|
36
|
+
source_attribute.collection_of_attributes.each do |nested_source|
|
|
37
|
+
nested_copied = deep_copy_attribute(nested_source, new_nesting_level + 1)
|
|
38
|
+
copied.collection_of_attributes << nested_copied
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
copied
|
|
42
|
+
end
|
|
20
43
|
end
|
|
21
44
|
end
|
|
22
45
|
end
|
|
@@ -17,9 +17,9 @@ module Treaty
|
|
|
17
17
|
# integer :views, if: ->(post:) { post[:published_at].present? }
|
|
18
18
|
#
|
|
19
19
|
# Complex conditions:
|
|
20
|
-
# string :admin_note, if:
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# string :admin_note, if: (lambda do |**attributes|
|
|
21
|
+
# attributes.dig(:user, :role) == "admin" && attributes.dig(:post, :flagged)
|
|
22
|
+
# end)
|
|
23
23
|
#
|
|
24
24
|
# ## Use Cases
|
|
25
25
|
#
|
|
@@ -30,7 +30,7 @@ module Treaty
|
|
|
30
30
|
# string :id
|
|
31
31
|
# string :title
|
|
32
32
|
# datetime :published_at, :optional
|
|
33
|
-
# integer :rating, if: ->(**
|
|
33
|
+
# integer :rating, if: ->(**attributes) { attributes.dig(:post, :published_at).present? }
|
|
34
34
|
# end
|
|
35
35
|
# end
|
|
36
36
|
# # If published_at is nil → rating is excluded from response
|
|
@@ -80,7 +80,7 @@ module Treaty
|
|
|
80
80
|
#
|
|
81
81
|
# ```ruby
|
|
82
82
|
# # For response with { post: { title: "...", published_at: "..." } }
|
|
83
|
-
# integer :rating, if: ->(**
|
|
83
|
+
# integer :rating, if: ->(**attributes) { attributes.dig(:post, :published_at).present? }
|
|
84
84
|
#
|
|
85
85
|
# # Alternative: named argument pattern
|
|
86
86
|
# integer :rating, if: ->(post:) { post[:published_at].present? }
|
|
@@ -17,9 +17,9 @@ module Treaty
|
|
|
17
17
|
# integer :edit_count, unless: ->(post:) { post[:published_at].present? }
|
|
18
18
|
#
|
|
19
19
|
# Complex conditions:
|
|
20
|
-
# string :internal_note, unless:
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# string :internal_note, unless: (lambda do |**attributes|
|
|
21
|
+
# attributes.dig(:user, :role) == "admin" && attributes.dig(:post, :flagged)
|
|
22
|
+
# end)
|
|
23
23
|
#
|
|
24
24
|
# ## Use Cases
|
|
25
25
|
#
|
|
@@ -30,7 +30,7 @@ module Treaty
|
|
|
30
30
|
# string :id
|
|
31
31
|
# string :title
|
|
32
32
|
# datetime :published_at, :optional
|
|
33
|
-
# integer :draft_views, unless: ->(**
|
|
33
|
+
# integer :draft_views, unless: ->(**attributes) { attributes.dig(:post, :published_at).present? }
|
|
34
34
|
# end
|
|
35
35
|
# end
|
|
36
36
|
# # If published_at is nil → draft_views is included in response
|
|
@@ -74,12 +74,12 @@ module Treaty
|
|
|
74
74
|
#
|
|
75
75
|
# ```ruby
|
|
76
76
|
# # These are equivalent:
|
|
77
|
-
# integer :rating, if: ->(**
|
|
78
|
-
# integer :rating, unless: ->(**
|
|
77
|
+
# integer :rating, if: ->(**attributes) { attributes.dig(:post, :published_at).present? }
|
|
78
|
+
# integer :rating, unless: ->(**attributes) { attributes.dig(:post, :published_at).blank? }
|
|
79
79
|
#
|
|
80
80
|
# # These are also equivalent:
|
|
81
|
-
# integer :draft_views, unless: ->(**
|
|
82
|
-
# integer :draft_views, if: ->(**
|
|
81
|
+
# integer :draft_views, unless: ->(**attributes) { attributes.dig(:post, :published_at).present? }
|
|
82
|
+
# integer :draft_views, if: ->(**attributes) { attributes.dig(:post, :published_at).blank? }
|
|
83
83
|
# ```
|
|
84
84
|
#
|
|
85
85
|
# ## Error Handling
|
|
@@ -96,7 +96,7 @@ module Treaty
|
|
|
96
96
|
#
|
|
97
97
|
# ```ruby
|
|
98
98
|
# # For response with { post: { title: "...", published_at: "..." } }
|
|
99
|
-
# integer :draft_views, unless: ->(**
|
|
99
|
+
# integer :draft_views, unless: ->(**attributes) { attributes.dig(:post, :published_at).present? }
|
|
100
100
|
#
|
|
101
101
|
# # Alternative: named argument pattern
|
|
102
102
|
# integer :draft_views, unless: ->(post:) { post[:published_at].present? }
|
|
@@ -14,13 +14,13 @@ module Treaty
|
|
|
14
14
|
# ## Usage Examples
|
|
15
15
|
#
|
|
16
16
|
# Simple mode:
|
|
17
|
-
# string :full_name, computed:
|
|
18
|
-
# "#{
|
|
19
|
-
#
|
|
17
|
+
# string :full_name, computed: (lambda do |**attributes|
|
|
18
|
+
# "#{attributes.dig(:user, :first_name)} #{attributes.dig(:user, :last_name)}"
|
|
19
|
+
# end)
|
|
20
20
|
#
|
|
21
21
|
# Advanced mode with custom error message:
|
|
22
22
|
# string :full_name, computed: {
|
|
23
|
-
# is: ->(**
|
|
23
|
+
# is: ->(**attributes) { "#{attributes.dig(:user, :first_name)} #{attributes.dig(:user, :last_name)}" },
|
|
24
24
|
# message: "Failed to compute full name"
|
|
25
25
|
# }
|
|
26
26
|
#
|
|
@@ -32,9 +32,9 @@ module Treaty
|
|
|
32
32
|
# object :user do
|
|
33
33
|
# string :first_name
|
|
34
34
|
# string :last_name
|
|
35
|
-
# string :full_name, computed:
|
|
36
|
-
# "#{
|
|
37
|
-
#
|
|
35
|
+
# string :full_name, computed: (lambda do |**attributes|
|
|
36
|
+
# "#{attributes.dig(:user, :first_name)} #{attributes.dig(:user, :last_name)}"
|
|
37
|
+
# end)
|
|
38
38
|
# end
|
|
39
39
|
# end
|
|
40
40
|
# ```
|
|
@@ -44,9 +44,9 @@ module Treaty
|
|
|
44
44
|
# response 200 do
|
|
45
45
|
# object :post do
|
|
46
46
|
# string :content
|
|
47
|
-
# integer :word_count, computed:
|
|
48
|
-
#
|
|
49
|
-
#
|
|
47
|
+
# integer :word_count, computed: (lambda do |**attributes|
|
|
48
|
+
# attributes.dig(:post, :content).to_s.split.size
|
|
49
|
+
# end)
|
|
50
50
|
# end
|
|
51
51
|
# end
|
|
52
52
|
# ```
|
|
@@ -57,9 +57,9 @@ module Treaty
|
|
|
57
57
|
# object :order do
|
|
58
58
|
# integer :quantity
|
|
59
59
|
# integer :unit_price
|
|
60
|
-
# integer :total, computed:
|
|
61
|
-
#
|
|
62
|
-
#
|
|
60
|
+
# integer :total, computed: (lambda do |**attributes|
|
|
61
|
+
# attributes.dig(:order, :quantity).to_i * attributes.dig(:order, :unit_price).to_i
|
|
62
|
+
# end)
|
|
63
63
|
# end
|
|
64
64
|
# end
|
|
65
65
|
# ```
|
data/lib/treaty/entity.rb
CHANGED
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Treaty
|
|
4
|
-
# Base class for defining
|
|
4
|
+
# Base class for defining reusable entity classes in Treaty.
|
|
5
5
|
#
|
|
6
6
|
# ## Purpose
|
|
7
7
|
#
|
|
8
|
-
# Treaty::Entity provides a base class for creating reusable
|
|
8
|
+
# Treaty::Entity provides a base class for creating reusable entity classes
|
|
9
9
|
# that can be used in both request and response definitions. This allows
|
|
10
10
|
# for better code organization and reusability of common data structures.
|
|
11
11
|
#
|
|
12
12
|
# ## Usage
|
|
13
13
|
#
|
|
14
|
-
# Create
|
|
14
|
+
# Create an entity class by inheriting from Treaty::Entity:
|
|
15
15
|
#
|
|
16
16
|
# ```ruby
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
17
|
+
# module Posts
|
|
18
|
+
# module Create
|
|
19
|
+
# class ResponseEntity < Treaty::Entity
|
|
20
|
+
# string :id
|
|
21
|
+
# string :title
|
|
22
|
+
# string :content
|
|
23
|
+
# datetime :created_at
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
22
26
|
# end
|
|
23
27
|
# ```
|
|
24
28
|
#
|
|
25
29
|
# Then use it in your treaty definitions:
|
|
26
30
|
#
|
|
27
31
|
# ```ruby
|
|
28
|
-
# class CreateTreaty < ApplicationTreaty
|
|
32
|
+
# class Posts::CreateTreaty < ApplicationTreaty
|
|
29
33
|
# version 1 do
|
|
30
|
-
# request
|
|
31
|
-
# response 201,
|
|
34
|
+
# request Posts::Create::RequestEntity
|
|
35
|
+
# response 201, Posts::Create::ResponseEntity
|
|
32
36
|
# end
|
|
33
37
|
# end
|
|
34
38
|
# ```
|
|
@@ -17,6 +17,29 @@ module Treaty
|
|
|
17
17
|
&block
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# Deep copies an attribute with adjusted nesting level for Request context.
|
|
22
|
+
#
|
|
23
|
+
# @param source_attribute [Treaty::Attribute::Base] Attribute to copy
|
|
24
|
+
# @param new_nesting_level [Integer] New nesting level
|
|
25
|
+
# @return [Request::Attribute::Attribute] Copied attribute
|
|
26
|
+
def deep_copy_attribute(source_attribute, new_nesting_level) # rubocop:disable Metrics/MethodLength
|
|
27
|
+
copied = Attribute.new(
|
|
28
|
+
source_attribute.name,
|
|
29
|
+
source_attribute.type,
|
|
30
|
+
nesting_level: new_nesting_level,
|
|
31
|
+
**deep_copy_options(source_attribute.options)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return copied unless source_attribute.nested?
|
|
35
|
+
|
|
36
|
+
source_attribute.collection_of_attributes.each do |nested_source|
|
|
37
|
+
nested_copied = deep_copy_attribute(nested_source, new_nesting_level + 1)
|
|
38
|
+
copied.collection_of_attributes << nested_copied
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
copied
|
|
42
|
+
end
|
|
20
43
|
end
|
|
21
44
|
end
|
|
22
45
|
end
|
|
@@ -17,6 +17,29 @@ module Treaty
|
|
|
17
17
|
&block
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# Deep copies an attribute with adjusted nesting level for Response context.
|
|
22
|
+
#
|
|
23
|
+
# @param source_attribute [Treaty::Attribute::Base] Attribute to copy
|
|
24
|
+
# @param new_nesting_level [Integer] New nesting level
|
|
25
|
+
# @return [Response::Attribute::Attribute] Copied attribute
|
|
26
|
+
def deep_copy_attribute(source_attribute, new_nesting_level) # rubocop:disable Metrics/MethodLength
|
|
27
|
+
copied = Attribute.new(
|
|
28
|
+
source_attribute.name,
|
|
29
|
+
source_attribute.type,
|
|
30
|
+
nesting_level: new_nesting_level,
|
|
31
|
+
**deep_copy_options(source_attribute.options)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return copied unless source_attribute.nested?
|
|
35
|
+
|
|
36
|
+
source_attribute.collection_of_attributes.each do |nested_source|
|
|
37
|
+
nested_copied = deep_copy_attribute(nested_source, new_nesting_level + 1)
|
|
38
|
+
copied.collection_of_attributes << nested_copied
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
copied
|
|
42
|
+
end
|
|
20
43
|
end
|
|
21
44
|
end
|
|
22
45
|
end
|
data/lib/treaty/version.rb
CHANGED