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.
- checksums.yaml +4 -4
- data/README.md +19 -18
- data/Rakefile +4 -2
- data/lib/treaty/attribute/base.rb +172 -0
- data/lib/treaty/attribute/builder/base.rb +142 -0
- data/lib/treaty/attribute/collection.rb +65 -0
- data/lib/treaty/attribute/helper_mapper.rb +72 -0
- data/lib/treaty/attribute/option/base.rb +159 -0
- data/lib/treaty/attribute/option/modifiers/as_modifier.rb +87 -0
- data/lib/treaty/attribute/option/modifiers/default_modifier.rb +103 -0
- data/lib/treaty/attribute/option/registry.rb +128 -0
- data/lib/treaty/attribute/option/registry_initializer.rb +90 -0
- data/lib/treaty/attribute/option/validators/inclusion_validator.rb +80 -0
- data/lib/treaty/attribute/option/validators/required_validator.rb +94 -0
- data/lib/treaty/attribute/option/validators/type_validator.rb +153 -0
- data/lib/treaty/attribute/option_normalizer.rb +150 -0
- data/lib/treaty/attribute/option_orchestrator.rb +186 -0
- data/lib/treaty/attribute/validation/attribute_validator.rb +144 -0
- data/lib/treaty/attribute/validation/base.rb +93 -0
- data/lib/treaty/attribute/validation/nested_array_validator.rb +194 -0
- data/lib/treaty/attribute/validation/nested_object_validator.rb +103 -0
- data/lib/treaty/attribute/validation/nested_transformer.rb +240 -0
- data/lib/treaty/attribute/validation/orchestrator/base.rb +196 -0
- data/lib/treaty/base.rb +9 -0
- data/lib/treaty/configuration.rb +17 -0
- data/lib/treaty/context/callable.rb +24 -0
- data/lib/treaty/context/dsl.rb +12 -0
- data/lib/treaty/context/workspace.rb +28 -0
- data/lib/treaty/controller/dsl.rb +38 -0
- data/lib/treaty/engine.rb +37 -0
- data/lib/treaty/exceptions/base.rb +8 -0
- data/lib/treaty/exceptions/class_name.rb +11 -0
- data/lib/treaty/exceptions/deprecated.rb +8 -0
- data/lib/treaty/exceptions/execution.rb +8 -0
- data/lib/treaty/exceptions/method_name.rb +8 -0
- data/lib/treaty/exceptions/nested_attributes.rb +8 -0
- data/lib/treaty/exceptions/strategy.rb +8 -0
- data/lib/treaty/exceptions/unexpected.rb +8 -0
- data/lib/treaty/exceptions/validation.rb +8 -0
- data/lib/treaty/info/builder.rb +122 -0
- data/lib/treaty/info/dsl.rb +26 -0
- data/lib/treaty/info/result.rb +13 -0
- data/lib/treaty/request/attribute/attribute.rb +24 -0
- data/lib/treaty/request/attribute/builder.rb +22 -0
- data/lib/treaty/request/attribute/validation/orchestrator.rb +27 -0
- data/lib/treaty/request/attribute/validator.rb +50 -0
- data/lib/treaty/request/factory.rb +32 -0
- data/lib/treaty/request/scope/collection.rb +21 -0
- data/lib/treaty/request/scope/factory.rb +42 -0
- data/lib/treaty/response/attribute/attribute.rb +24 -0
- data/lib/treaty/response/attribute/builder.rb +22 -0
- data/lib/treaty/response/attribute/validation/orchestrator.rb +27 -0
- data/lib/treaty/response/attribute/validator.rb +44 -0
- data/lib/treaty/response/factory.rb +38 -0
- data/lib/treaty/response/scope/collection.rb +21 -0
- data/lib/treaty/response/scope/factory.rb +42 -0
- data/lib/treaty/result.rb +22 -0
- data/lib/treaty/strategy.rb +31 -0
- data/lib/treaty/support/loader.rb +24 -0
- data/lib/treaty/version.rb +8 -1
- data/lib/treaty/versions/collection.rb +15 -0
- data/lib/treaty/versions/dsl.rb +30 -0
- data/lib/treaty/versions/execution/request.rb +151 -0
- data/lib/treaty/versions/executor.rb +14 -0
- data/lib/treaty/versions/factory.rb +93 -0
- data/lib/treaty/versions/resolver.rb +72 -0
- data/lib/treaty/versions/semantic.rb +22 -0
- data/lib/treaty/versions/workspace.rb +40 -0
- data/lib/treaty.rb +3 -3
- metadata +184 -27
- data/.standard.yml +0 -3
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -84
- data/LICENSE.txt +0 -21
- data/sig/treaty.rbs +0 -4
- data/treaty.gemspec +0 -35
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Attribute
|
|
5
|
+
module Validation
|
|
6
|
+
module Orchestrator
|
|
7
|
+
# Base orchestrator for validating and transforming data according to treaty definitions.
|
|
8
|
+
#
|
|
9
|
+
# ## Purpose
|
|
10
|
+
#
|
|
11
|
+
# Coordinates the validation and transformation of request/response data for a specific
|
|
12
|
+
# API version. Processes all scopes and their attributes, applying validations and
|
|
13
|
+
# transformations defined in the treaty DSL.
|
|
14
|
+
#
|
|
15
|
+
# ## Responsibilities
|
|
16
|
+
#
|
|
17
|
+
# 1. **Scope Processing** - Iterates through all defined scopes
|
|
18
|
+
# 2. **Attribute Validation** - Validates each attribute's value
|
|
19
|
+
# 3. **Data Transformation** - Transforms values (defaults, renaming)
|
|
20
|
+
# 4. **Nested Handling** - Delegates nested structures to NestedTransformer
|
|
21
|
+
# 5. **Result Assembly** - Builds final transformed data structure
|
|
22
|
+
#
|
|
23
|
+
# ## Usage
|
|
24
|
+
#
|
|
25
|
+
# Subclasses must implement:
|
|
26
|
+
# - `collection_of_scopes` - Returns scopes for this context (request/response)
|
|
27
|
+
# - `scope_data_for(name)` - Extracts data for a specific scope
|
|
28
|
+
#
|
|
29
|
+
# Example:
|
|
30
|
+
# orchestrator = Request::Orchestrator.new(version_factory: factory, data: params)
|
|
31
|
+
# validated_data = orchestrator.validate!
|
|
32
|
+
#
|
|
33
|
+
# ## Special Scopes
|
|
34
|
+
#
|
|
35
|
+
# - Normal scope: `{ scope_name: { ... } }`
|
|
36
|
+
# - Self scope (`:_self`): Attributes merged directly into parent
|
|
37
|
+
#
|
|
38
|
+
# ## Architecture
|
|
39
|
+
#
|
|
40
|
+
# Uses:
|
|
41
|
+
# - `AttributeValidator` - Validates individual attributes
|
|
42
|
+
# - `NestedTransformer` - Handles nested objects and arrays
|
|
43
|
+
#
|
|
44
|
+
# The refactored design separates concerns:
|
|
45
|
+
# - Orchestrator: High-level flow and scope iteration
|
|
46
|
+
# - Validator: Individual attribute validation
|
|
47
|
+
# - Transformer: Nested structure transformation
|
|
48
|
+
class Base
|
|
49
|
+
SELF_SCOPE = :_self
|
|
50
|
+
private_constant :SELF_SCOPE
|
|
51
|
+
|
|
52
|
+
attr_reader :version_factory, :data
|
|
53
|
+
|
|
54
|
+
# Class-level factory method for validation
|
|
55
|
+
# Creates instance and calls validate!
|
|
56
|
+
#
|
|
57
|
+
# @param args [Hash] Arguments passed to initialize
|
|
58
|
+
# @return [Hash] Validated and transformed data
|
|
59
|
+
def self.validate!(...)
|
|
60
|
+
new(...).validate!
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Creates a new orchestrator instance
|
|
64
|
+
#
|
|
65
|
+
# @param version_factory [VersionFactory] Factory containing version info
|
|
66
|
+
# @param data [Hash] Data to validate and transform (default: {})
|
|
67
|
+
def initialize(version_factory:, data: {})
|
|
68
|
+
@version_factory = version_factory
|
|
69
|
+
@data = data
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Validates and transforms all scopes
|
|
73
|
+
# Iterates through scopes, processes attributes, handles :_self scope
|
|
74
|
+
#
|
|
75
|
+
# @return [Hash] Transformed data with all scopes processed
|
|
76
|
+
def validate!
|
|
77
|
+
transformed_data = {}
|
|
78
|
+
|
|
79
|
+
collection_of_scopes.each do |scope_factory|
|
|
80
|
+
transformed_scope_data = validate_and_transform_scope!(scope_factory)
|
|
81
|
+
transformed_data[scope_factory.name] = transformed_scope_data if scope_factory.name != SELF_SCOPE
|
|
82
|
+
transformed_data.merge!(transformed_scope_data) if scope_factory.name == SELF_SCOPE
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
transformed_data
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
# Returns collection of scopes for this context
|
|
91
|
+
# Must be implemented in subclasses
|
|
92
|
+
#
|
|
93
|
+
# @raise [Treaty::Exceptions::Validation] If not implemented
|
|
94
|
+
# @return [Array<ScopeFactory>] Collection of scope factories
|
|
95
|
+
def collection_of_scopes
|
|
96
|
+
# TODO: It is necessary to implement a translation system (I18n).
|
|
97
|
+
raise Treaty::Exceptions::Validation,
|
|
98
|
+
"Subclass must implement the collection_of_scopes method"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Validates all attributes in a scope (deprecated, not used)
|
|
102
|
+
#
|
|
103
|
+
# @param scope_factory [ScopeFactory] The scope to validate
|
|
104
|
+
# @return [void]
|
|
105
|
+
def validate_scope!(scope_factory)
|
|
106
|
+
scope_data = scope_data_for(scope_factory.name)
|
|
107
|
+
|
|
108
|
+
validators_for_scope(scope_factory).each do |attribute, validator|
|
|
109
|
+
value = scope_data.fetch(attribute.name, nil)
|
|
110
|
+
validator.validate_value!(value)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Gets cached validators for scope or builds them
|
|
115
|
+
#
|
|
116
|
+
# @param scope_factory [ScopeFactory] The scope factory
|
|
117
|
+
# @return [Hash] Hash of attribute => validator
|
|
118
|
+
def validators_for_scope(scope_factory)
|
|
119
|
+
@validators_cache ||= {}
|
|
120
|
+
@validators_cache[scope_factory] ||= build_validators_for_scope(scope_factory)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Builds validators for all attributes in a scope
|
|
124
|
+
#
|
|
125
|
+
# @param scope_factory [ScopeFactory] The scope factory
|
|
126
|
+
# @return [Hash] Hash of attribute => validator
|
|
127
|
+
def build_validators_for_scope(scope_factory)
|
|
128
|
+
scope_factory.collection_of_attributes.each_with_object({}) do |attribute, cache|
|
|
129
|
+
validator = AttributeValidator.new(attribute)
|
|
130
|
+
validator.validate_schema!
|
|
131
|
+
cache[attribute] = validator
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Extracts data for a specific scope
|
|
136
|
+
# Must be implemented in subclasses
|
|
137
|
+
#
|
|
138
|
+
# @param _name [Symbol] The scope name
|
|
139
|
+
# @raise [Treaty::Exceptions::Validation] If not implemented
|
|
140
|
+
# @return [Hash] Scope data
|
|
141
|
+
def scope_data_for(_name)
|
|
142
|
+
# TODO: It is necessary to implement a translation system (I18n).
|
|
143
|
+
raise Treaty::Exceptions::Validation,
|
|
144
|
+
"Subclass must implement the scope_data_for method"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Validates and transforms all attributes in a scope
|
|
148
|
+
# Handles both nested and regular attributes
|
|
149
|
+
#
|
|
150
|
+
# @param scope_factory [ScopeFactory] The scope to process
|
|
151
|
+
# @return [Hash] Transformed scope data
|
|
152
|
+
def validate_and_transform_scope!(scope_factory) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
153
|
+
scope_data = scope_data_for(scope_factory.name)
|
|
154
|
+
|
|
155
|
+
return scope_data if scope_factory.collection_of_attributes.empty?
|
|
156
|
+
|
|
157
|
+
transformed_scope_data = {}
|
|
158
|
+
|
|
159
|
+
validators_for_scope(scope_factory).each do |attribute, validator|
|
|
160
|
+
source_name = attribute.name
|
|
161
|
+
value = scope_data.fetch(source_name, nil)
|
|
162
|
+
|
|
163
|
+
if attribute.nested?
|
|
164
|
+
transformed_value = validate_and_transform_nested(attribute, value, validator)
|
|
165
|
+
else
|
|
166
|
+
validator.validate_value!(value)
|
|
167
|
+
transformed_value = validator.transform_value(value)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
target_name = validator.target_name
|
|
171
|
+
|
|
172
|
+
transformed_scope_data[target_name] = transformed_value
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
transformed_scope_data
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Validates and transforms nested attribute (object/array)
|
|
179
|
+
# Delegates transformation to NestedTransformer
|
|
180
|
+
#
|
|
181
|
+
# @param attribute [Attribute::Base] The nested attribute
|
|
182
|
+
# @param value [Object] The value to validate and transform
|
|
183
|
+
# @param validator [AttributeValidator] The validator instance
|
|
184
|
+
# @return [Object] Transformed nested value
|
|
185
|
+
def validate_and_transform_nested(attribute, value, validator)
|
|
186
|
+
validator.validate_type!(value) unless value.nil?
|
|
187
|
+
validator.validate_required!(value)
|
|
188
|
+
|
|
189
|
+
transformer = NestedTransformer.new(attribute)
|
|
190
|
+
transformer.transform(value)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
data/lib/treaty/base.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
class Configuration
|
|
5
|
+
include ::ActiveModel::Validations
|
|
6
|
+
|
|
7
|
+
attr_accessor :version
|
|
8
|
+
|
|
9
|
+
attr_reader :attribute_nesting_level
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@version = ->(context) { context }
|
|
13
|
+
|
|
14
|
+
@attribute_nesting_level = 5
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Context
|
|
5
|
+
module Callable
|
|
6
|
+
def call!(controller:, params:)
|
|
7
|
+
context = send(:new)
|
|
8
|
+
|
|
9
|
+
_call!(context, controller:, params:)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def _call!(context, controller:, params:)
|
|
15
|
+
context.send(
|
|
16
|
+
:_call!,
|
|
17
|
+
controller:,
|
|
18
|
+
params:,
|
|
19
|
+
collection_of_versions:
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Context
|
|
5
|
+
module Workspace
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def _call!(
|
|
9
|
+
controller:,
|
|
10
|
+
params:,
|
|
11
|
+
collection_of_versions:
|
|
12
|
+
)
|
|
13
|
+
call!(
|
|
14
|
+
controller:,
|
|
15
|
+
params:,
|
|
16
|
+
collection_of_versions:
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call!(
|
|
21
|
+
collection_of_versions:,
|
|
22
|
+
**
|
|
23
|
+
)
|
|
24
|
+
@collection_of_versions = collection_of_versions
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Controller
|
|
5
|
+
module DSL
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
base.include(InstanceMethods)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def treaty(action_name)
|
|
15
|
+
define_method(action_name) do
|
|
16
|
+
treaty = treaty_class.call!(controller: self, params:)
|
|
17
|
+
|
|
18
|
+
render json: treaty.data, status: treaty.status
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module InstanceMethods
|
|
24
|
+
def treaty_class
|
|
25
|
+
treaty_class_name.constantize
|
|
26
|
+
rescue NameError
|
|
27
|
+
# TODO: It is necessary to implement a translation system (I18n).
|
|
28
|
+
raise Treaty::Exceptions::ClassName, treaty_class_name
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def treaty_class_name
|
|
32
|
+
# TODO: Need to move `Treaty` to configuration.
|
|
33
|
+
self.class.name.sub(/Controller$/, "::#{action_name.to_s.classify}Treaty")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
isolate_namespace Treaty
|
|
6
|
+
|
|
7
|
+
config.treaty = Treaty::Configuration.new
|
|
8
|
+
|
|
9
|
+
def self.configure
|
|
10
|
+
yield(config.treaty) if block_given?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
initializer "treaty.register_option_processors", before: :load_config_initializers do
|
|
14
|
+
# Register all option processors (validators and modifiers)
|
|
15
|
+
require "treaty/attribute/option/registry_initializer"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
initializer "treaty.validate_configuration" do
|
|
19
|
+
config.after_initialize do
|
|
20
|
+
unless config.treaty.valid?
|
|
21
|
+
errors = config.treaty.errors.full_messages
|
|
22
|
+
raise "Invalid Treaty configuration: #{errors.join(', ')}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
initializer "treaty.controller_methods" do
|
|
28
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
29
|
+
include Treaty::Controller::DSL
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ActiveSupport.on_load(:action_controller_api) do
|
|
33
|
+
include Treaty::Controller::DSL
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Info
|
|
5
|
+
class Builder
|
|
6
|
+
attr_reader :versions
|
|
7
|
+
|
|
8
|
+
def self.build(...)
|
|
9
|
+
new.build(...)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build(collection_of_versions:)
|
|
13
|
+
build_all(
|
|
14
|
+
versions: collection_of_versions
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def build_all(versions:)
|
|
23
|
+
build_versions_with(
|
|
24
|
+
collection: versions
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##########################################################################
|
|
29
|
+
|
|
30
|
+
def build_versions_with(collection:) # rubocop:disable Metrics/MethodLength
|
|
31
|
+
@versions = collection.map do |version|
|
|
32
|
+
gem_version = version.version.version
|
|
33
|
+
{
|
|
34
|
+
version: gem_version.version,
|
|
35
|
+
segments: gem_version.segments,
|
|
36
|
+
default: version.default_result,
|
|
37
|
+
summary: version.summary_text,
|
|
38
|
+
strategy: version.strategy_instance.code,
|
|
39
|
+
deprecated: version.deprecated_result,
|
|
40
|
+
executor: build_executor_with(version),
|
|
41
|
+
request: build_request_with(version),
|
|
42
|
+
response: build_response_with(version)
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##########################################################################
|
|
48
|
+
|
|
49
|
+
def build_executor_with(version)
|
|
50
|
+
{
|
|
51
|
+
executor: version.executor.executor,
|
|
52
|
+
method: version.executor.method
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
##########################################################################
|
|
57
|
+
|
|
58
|
+
def build_request_with(version)
|
|
59
|
+
{
|
|
60
|
+
scopes: build_scopes_with(version.request_factory)
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def build_response_with(version)
|
|
65
|
+
response_factory = version.response_factory
|
|
66
|
+
{
|
|
67
|
+
status: response_factory.status,
|
|
68
|
+
scopes: build_scopes_with(response_factory)
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##########################################################################
|
|
73
|
+
|
|
74
|
+
def build_scopes_with(request_factory)
|
|
75
|
+
request_factory.collection_of_scopes.to_h do |scope|
|
|
76
|
+
[
|
|
77
|
+
scope.name,
|
|
78
|
+
build_attributes_with(scope.collection_of_attributes)
|
|
79
|
+
]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
##########################################################################
|
|
84
|
+
|
|
85
|
+
def build_attributes_with(collection, current_level = 0)
|
|
86
|
+
# validate_nesting_level!(current_level)
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
attributes: build_attributes_hash(collection, current_level)
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def build_attributes_hash(collection, current_level)
|
|
94
|
+
collection.to_h do |attribute|
|
|
95
|
+
[
|
|
96
|
+
attribute.name,
|
|
97
|
+
{
|
|
98
|
+
type: attribute.type,
|
|
99
|
+
options: attribute.options,
|
|
100
|
+
attributes: build_nested_attributes(attribute, current_level)
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def build_nested_attributes(attribute, current_level)
|
|
107
|
+
return {} unless attribute.nested?
|
|
108
|
+
|
|
109
|
+
build_attributes_hash(attribute.collection_of_attributes, current_level + 1)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# def validate_nesting_level!(level)
|
|
113
|
+
# return unless level > Treaty::Engine.config.treaty.attribute_nesting_level
|
|
114
|
+
#
|
|
115
|
+
# # TODO: It is necessary to implement a translation system (I18n).
|
|
116
|
+
# raise Treaty::Exceptions::NestedAttributes,
|
|
117
|
+
# "Nesting level #{level} exceeds maximum allowed level of " \
|
|
118
|
+
# "#{Treaty::Engine.config.treaty.attribute_nesting_level}"
|
|
119
|
+
# end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Info
|
|
5
|
+
module DSL
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def info
|
|
12
|
+
builder = Builder.build(
|
|
13
|
+
collection_of_versions:
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
Result.new(builder)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# API: Treaty Web
|
|
20
|
+
def treaty?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Request
|
|
5
|
+
module Attribute
|
|
6
|
+
class Attribute < Treaty::Attribute::Base
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def apply_defaults!
|
|
10
|
+
# For request: required by default (true).
|
|
11
|
+
# TODO: It is necessary to implement a translation system (I18n).
|
|
12
|
+
@options[:required] ||= { is: true, message: nil }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def process_nested_attributes(&block)
|
|
16
|
+
return unless object_or_array?
|
|
17
|
+
|
|
18
|
+
builder = Builder.new(collection_of_attributes, @nesting_level + 1)
|
|
19
|
+
builder.instance_eval(&block)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Request
|
|
5
|
+
module Attribute
|
|
6
|
+
class Builder < Treaty::Attribute::Builder::Base
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
|
|
10
|
+
Attribute.new(
|
|
11
|
+
name,
|
|
12
|
+
type,
|
|
13
|
+
*helpers,
|
|
14
|
+
nesting_level:,
|
|
15
|
+
**options,
|
|
16
|
+
&block
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|