treaty 0.18.0 → 0.20.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 +1 -1
- data/config/locales/en.yml +3 -3
- data/lib/treaty/action/base.rb +11 -0
- data/lib/treaty/action/context/callable.rb +90 -0
- data/lib/treaty/action/context/dsl.rb +56 -0
- data/lib/treaty/action/context/workspace.rb +92 -0
- data/lib/treaty/action/executor/inventory.rb +136 -0
- data/lib/treaty/{info/rest → action/info}/builder.rb +2 -2
- data/lib/treaty/{info/rest → action/info}/dsl.rb +2 -2
- data/lib/treaty/{info/rest → action/info}/result.rb +2 -2
- data/lib/treaty/action/inventory/collection.rb +77 -0
- data/lib/treaty/action/inventory/factory.rb +108 -0
- data/lib/treaty/action/inventory/inventory.rb +146 -0
- data/lib/treaty/action/request/attribute/attribute.rb +76 -0
- data/lib/treaty/action/request/attribute/builder.rb +98 -0
- data/lib/treaty/action/request/entity.rb +78 -0
- data/lib/treaty/action/request/factory.rb +116 -0
- data/lib/treaty/action/request/validator.rb +120 -0
- data/lib/treaty/action/response/attribute/attribute.rb +79 -0
- data/lib/treaty/action/response/attribute/builder.rb +96 -0
- data/lib/treaty/action/response/entity.rb +79 -0
- data/lib/treaty/action/response/factory.rb +129 -0
- data/lib/treaty/action/response/validator.rb +111 -0
- data/lib/treaty/action/result.rb +81 -0
- data/lib/treaty/action/versions/collection.rb +47 -0
- data/lib/treaty/action/versions/dsl.rb +116 -0
- data/lib/treaty/action/versions/execution/request.rb +287 -0
- data/lib/treaty/action/versions/executor.rb +61 -0
- data/lib/treaty/action/versions/factory.rb +253 -0
- data/lib/treaty/action/versions/resolver.rb +150 -0
- data/lib/treaty/action/versions/semantic.rb +64 -0
- data/lib/treaty/action/versions/workspace.rb +106 -0
- data/lib/treaty/action.rb +31 -0
- data/lib/treaty/controller/dsl.rb +1 -1
- data/lib/treaty/engine.rb +1 -1
- data/lib/treaty/{attribute/entity → entity/attribute}/attribute.rb +4 -4
- data/lib/treaty/entity/attribute/base.rb +184 -0
- data/lib/treaty/entity/attribute/builder/base.rb +275 -0
- data/lib/treaty/entity/attribute/collection.rb +67 -0
- data/lib/treaty/entity/attribute/dsl.rb +92 -0
- data/lib/treaty/entity/attribute/helper_mapper.rb +74 -0
- data/lib/treaty/entity/attribute/option/base.rb +190 -0
- data/lib/treaty/entity/attribute/option/conditionals/base.rb +92 -0
- data/lib/treaty/entity/attribute/option/conditionals/if_conditional.rb +136 -0
- data/lib/treaty/entity/attribute/option/conditionals/unless_conditional.rb +153 -0
- data/lib/treaty/entity/attribute/option/modifiers/as_modifier.rb +93 -0
- data/lib/treaty/entity/attribute/option/modifiers/cast_modifier.rb +285 -0
- data/lib/treaty/entity/attribute/option/modifiers/computed_modifier.rb +128 -0
- data/lib/treaty/entity/attribute/option/modifiers/default_modifier.rb +105 -0
- data/lib/treaty/entity/attribute/option/modifiers/transform_modifier.rb +114 -0
- data/lib/treaty/entity/attribute/option/registry.rb +157 -0
- data/lib/treaty/entity/attribute/option/registry_initializer.rb +117 -0
- data/lib/treaty/entity/attribute/option/validators/format_validator.rb +222 -0
- data/lib/treaty/entity/attribute/option/validators/inclusion_validator.rb +94 -0
- data/lib/treaty/entity/attribute/option/validators/required_validator.rb +100 -0
- data/lib/treaty/entity/attribute/option/validators/type_validator.rb +219 -0
- data/lib/treaty/entity/attribute/option_normalizer.rb +168 -0
- data/lib/treaty/entity/attribute/option_orchestrator.rb +192 -0
- data/lib/treaty/entity/attribute/validation/attribute_validator.rb +147 -0
- data/lib/treaty/entity/attribute/validation/base.rb +76 -0
- data/lib/treaty/entity/attribute/validation/nested_array_validator.rb +207 -0
- data/lib/treaty/entity/attribute/validation/nested_object_validator.rb +105 -0
- data/lib/treaty/entity/attribute/validation/nested_transformer.rb +432 -0
- data/lib/treaty/entity/attribute/validation/orchestrator/base.rb +262 -0
- data/lib/treaty/entity/base.rb +90 -0
- data/lib/treaty/entity/builder.rb +101 -0
- data/lib/treaty/{info/entity → entity/info}/builder.rb +8 -8
- data/lib/treaty/{info/entity → entity/info}/dsl.rb +2 -2
- data/lib/treaty/{info/entity → entity/info}/result.rb +2 -2
- data/lib/treaty/entity.rb +7 -79
- data/lib/treaty/version.rb +1 -1
- metadata +66 -64
- data/lib/treaty/attribute/base.rb +0 -182
- data/lib/treaty/attribute/builder/base.rb +0 -273
- data/lib/treaty/attribute/collection.rb +0 -65
- data/lib/treaty/attribute/dsl.rb +0 -90
- data/lib/treaty/attribute/entity/builder.rb +0 -46
- data/lib/treaty/attribute/helper_mapper.rb +0 -72
- data/lib/treaty/attribute/option/base.rb +0 -188
- data/lib/treaty/attribute/option/conditionals/base.rb +0 -90
- data/lib/treaty/attribute/option/conditionals/if_conditional.rb +0 -134
- data/lib/treaty/attribute/option/conditionals/unless_conditional.rb +0 -151
- data/lib/treaty/attribute/option/modifiers/as_modifier.rb +0 -91
- data/lib/treaty/attribute/option/modifiers/cast_modifier.rb +0 -283
- data/lib/treaty/attribute/option/modifiers/computed_modifier.rb +0 -126
- data/lib/treaty/attribute/option/modifiers/default_modifier.rb +0 -103
- data/lib/treaty/attribute/option/modifiers/transform_modifier.rb +0 -112
- data/lib/treaty/attribute/option/registry.rb +0 -155
- data/lib/treaty/attribute/option/registry_initializer.rb +0 -115
- data/lib/treaty/attribute/option/validators/format_validator.rb +0 -220
- data/lib/treaty/attribute/option/validators/inclusion_validator.rb +0 -92
- data/lib/treaty/attribute/option/validators/required_validator.rb +0 -98
- data/lib/treaty/attribute/option/validators/type_validator.rb +0 -217
- data/lib/treaty/attribute/option_normalizer.rb +0 -166
- data/lib/treaty/attribute/option_orchestrator.rb +0 -190
- data/lib/treaty/attribute/validation/attribute_validator.rb +0 -145
- data/lib/treaty/attribute/validation/base.rb +0 -74
- data/lib/treaty/attribute/validation/nested_array_validator.rb +0 -205
- data/lib/treaty/attribute/validation/nested_object_validator.rb +0 -103
- data/lib/treaty/attribute/validation/nested_transformer.rb +0 -430
- data/lib/treaty/attribute/validation/orchestrator/base.rb +0 -260
- data/lib/treaty/base.rb +0 -9
- data/lib/treaty/context/callable.rb +0 -26
- data/lib/treaty/context/dsl.rb +0 -12
- data/lib/treaty/context/workspace.rb +0 -32
- data/lib/treaty/executor/inventory.rb +0 -122
- data/lib/treaty/inventory/collection.rb +0 -71
- data/lib/treaty/inventory/factory.rb +0 -91
- data/lib/treaty/inventory/inventory.rb +0 -92
- data/lib/treaty/request/attribute/attribute.rb +0 -25
- data/lib/treaty/request/attribute/builder.rb +0 -46
- data/lib/treaty/request/entity.rb +0 -33
- data/lib/treaty/request/factory.rb +0 -81
- data/lib/treaty/request/validator.rb +0 -60
- data/lib/treaty/response/attribute/attribute.rb +0 -25
- data/lib/treaty/response/attribute/builder.rb +0 -46
- data/lib/treaty/response/entity.rb +0 -33
- data/lib/treaty/response/factory.rb +0 -87
- data/lib/treaty/response/validator.rb +0 -53
- data/lib/treaty/result.rb +0 -23
- data/lib/treaty/versions/collection.rb +0 -15
- data/lib/treaty/versions/dsl.rb +0 -42
- data/lib/treaty/versions/execution/request.rb +0 -177
- data/lib/treaty/versions/executor.rb +0 -14
- data/lib/treaty/versions/factory.rb +0 -112
- data/lib/treaty/versions/resolver.rb +0 -70
- data/lib/treaty/versions/semantic.rb +0 -22
- data/lib/treaty/versions/workspace.rb +0 -43
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Action
|
|
5
|
+
module Versions
|
|
6
|
+
# Factory for version configuration and DSL.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Provides the DSL interface within `version` blocks. Captures all
|
|
11
|
+
# version configuration: summary, deprecation, request schema,
|
|
12
|
+
# response schema, and executor delegation.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Created by:
|
|
17
|
+
# - Versions::DSL (when `version` is called)
|
|
18
|
+
#
|
|
19
|
+
# Consumed by:
|
|
20
|
+
# - Versions::Resolver (to find matching version)
|
|
21
|
+
# - Versions::Execution::Request (to execute the service)
|
|
22
|
+
# - Request::Validator / Response::Validator (to get schemas)
|
|
23
|
+
# - Info::Builder (to build version information)
|
|
24
|
+
#
|
|
25
|
+
# ## DSL Methods
|
|
26
|
+
#
|
|
27
|
+
# | Method | Purpose |
|
|
28
|
+
# |--------|---------|
|
|
29
|
+
# | `summary` | Set version description text |
|
|
30
|
+
# | `deprecated` | Mark version as deprecated (bool or proc) |
|
|
31
|
+
# | `request` | Define request schema (block or entity class) |
|
|
32
|
+
# | `response` | Define response schema with status code |
|
|
33
|
+
# | `delegate_to` | Set service class/proc to execute |
|
|
34
|
+
#
|
|
35
|
+
# ## Example
|
|
36
|
+
#
|
|
37
|
+
# version 1, default: true do
|
|
38
|
+
# summary "Initial API version"
|
|
39
|
+
#
|
|
40
|
+
# deprecated { ENV["V1_DEPRECATED"] == "true" }
|
|
41
|
+
#
|
|
42
|
+
# request do
|
|
43
|
+
# object :post do
|
|
44
|
+
# string :title, :required
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# response 201 do
|
|
49
|
+
# object :post do
|
|
50
|
+
# string :id
|
|
51
|
+
# end
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
# delegate_to Posts::CreateService
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# ## Validation
|
|
58
|
+
#
|
|
59
|
+
# - `default` must be boolean or Proc
|
|
60
|
+
# - Cannot be both default and deprecated
|
|
61
|
+
class Factory
|
|
62
|
+
# @return [Treaty::Action::Versions::Semantic] Semantic version wrapper
|
|
63
|
+
attr_reader :version
|
|
64
|
+
|
|
65
|
+
# @return [Boolean] Whether this is the default version
|
|
66
|
+
attr_reader :default_result
|
|
67
|
+
|
|
68
|
+
# @return [String, nil] Version summary/description
|
|
69
|
+
attr_reader :summary_text
|
|
70
|
+
|
|
71
|
+
# @return [Boolean] Whether version is deprecated
|
|
72
|
+
attr_reader :deprecated_result
|
|
73
|
+
|
|
74
|
+
# @return [Treaty::Action::Versions::Executor, nil] Executor configuration
|
|
75
|
+
attr_reader :executor
|
|
76
|
+
|
|
77
|
+
# @return [Treaty::Action::Request::Factory, nil] Request schema factory
|
|
78
|
+
attr_reader :request_factory
|
|
79
|
+
|
|
80
|
+
# @return [Treaty::Action::Response::Factory, nil] Response schema factory
|
|
81
|
+
attr_reader :response_factory
|
|
82
|
+
|
|
83
|
+
# Creates a new version factory
|
|
84
|
+
#
|
|
85
|
+
# @param version [Integer, String, Array] Version identifier
|
|
86
|
+
# @param default [Boolean, Proc] Whether this is the default version
|
|
87
|
+
def initialize(version:, default:)
|
|
88
|
+
@version = Semantic.new(version)
|
|
89
|
+
@default_result = default.is_a?(Proc) ? default.call : default
|
|
90
|
+
@summary_text = nil
|
|
91
|
+
@deprecated_result = false
|
|
92
|
+
@executor = nil
|
|
93
|
+
|
|
94
|
+
validate!
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Validates configuration on creation
|
|
98
|
+
#
|
|
99
|
+
# @return [void]
|
|
100
|
+
def validate!
|
|
101
|
+
validate_default_option!
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Validates configuration after block evaluation
|
|
105
|
+
#
|
|
106
|
+
# @raise [Treaty::Exceptions::VersionDefaultDeprecatedConflict]
|
|
107
|
+
# @return [void]
|
|
108
|
+
def validate_after_block!
|
|
109
|
+
validate_default_deprecated_conflict!
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Sets version summary text
|
|
113
|
+
#
|
|
114
|
+
# @param text [String] Version description
|
|
115
|
+
# @return [void]
|
|
116
|
+
def summary(text)
|
|
117
|
+
@summary_text = text
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Marks version as deprecated
|
|
121
|
+
#
|
|
122
|
+
# Accepts boolean, Proc, or block that evaluates to boolean.
|
|
123
|
+
# Deprecated versions raise error when accessed.
|
|
124
|
+
#
|
|
125
|
+
# @param condition [Boolean, Proc, nil] Deprecation condition
|
|
126
|
+
# @yield Block that returns boolean
|
|
127
|
+
# @return [void]
|
|
128
|
+
def deprecated(condition = nil)
|
|
129
|
+
result =
|
|
130
|
+
if condition.is_a?(Proc)
|
|
131
|
+
condition.call
|
|
132
|
+
elsif condition.is_a?(TrueClass) || condition.is_a?(FalseClass)
|
|
133
|
+
condition
|
|
134
|
+
else
|
|
135
|
+
yield
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
@deprecated_result = result
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Defines request schema
|
|
142
|
+
#
|
|
143
|
+
# Accepts either an Entity class or a block with attribute definitions.
|
|
144
|
+
#
|
|
145
|
+
# @param entity_class [Class, nil] Entity class for request schema
|
|
146
|
+
# @yield Block with request attribute definitions
|
|
147
|
+
# @return [void]
|
|
148
|
+
def request(entity_class = nil, &block)
|
|
149
|
+
@request_factory ||= Request::Factory.new
|
|
150
|
+
|
|
151
|
+
if entity_class.present?
|
|
152
|
+
@request_factory.use_entity(entity_class)
|
|
153
|
+
elsif block_given?
|
|
154
|
+
@request_factory.instance_eval(&block)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Defines response schema with HTTP status
|
|
159
|
+
#
|
|
160
|
+
# Accepts status code and either Entity class or block.
|
|
161
|
+
#
|
|
162
|
+
# @param status [Integer] HTTP status code (200, 201, 404, etc.)
|
|
163
|
+
# @param entity_class [Class, nil] Entity class for response schema
|
|
164
|
+
# @yield Block with response attribute definitions
|
|
165
|
+
# @return [void]
|
|
166
|
+
def response(status, entity_class = nil, &block)
|
|
167
|
+
@response_factory ||= Response::Factory.new(status)
|
|
168
|
+
|
|
169
|
+
if entity_class.present?
|
|
170
|
+
@response_factory.use_entity(entity_class)
|
|
171
|
+
elsif block_given?
|
|
172
|
+
@response_factory.instance_eval(&block)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Configures service delegation
|
|
177
|
+
#
|
|
178
|
+
# Sets the executor (class, string, or proc) and method to call.
|
|
179
|
+
#
|
|
180
|
+
# @param executor [Class, String, Proc, Hash] Service reference
|
|
181
|
+
# @param method [Symbol] Method to call (default: :call)
|
|
182
|
+
# @return [void]
|
|
183
|
+
#
|
|
184
|
+
# @example Class reference
|
|
185
|
+
# delegate_to Posts::CreateService
|
|
186
|
+
#
|
|
187
|
+
# @example With custom method
|
|
188
|
+
# delegate_to Posts::CreateService => :call!
|
|
189
|
+
#
|
|
190
|
+
# @example String path
|
|
191
|
+
# delegate_to "posts/create_service"
|
|
192
|
+
#
|
|
193
|
+
# @example Lambda
|
|
194
|
+
# delegate_to ->(params:) { { id: SecureRandom.uuid } }
|
|
195
|
+
def delegate_to(executor, method = :call)
|
|
196
|
+
@executor = Executor.new(executor, method)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
##########################################################################
|
|
200
|
+
|
|
201
|
+
private
|
|
202
|
+
|
|
203
|
+
# Validates default option is boolean or Proc
|
|
204
|
+
#
|
|
205
|
+
# @raise [Treaty::Exceptions::Validation] If invalid type
|
|
206
|
+
# @return [Boolean, Proc]
|
|
207
|
+
def validate_default_option!
|
|
208
|
+
if @default_result.is_a?(TrueClass) || @default_result.is_a?(FalseClass) || @default_result.is_a?(Proc)
|
|
209
|
+
return @default_result
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
raise Treaty::Exceptions::Validation,
|
|
213
|
+
I18n.t(
|
|
214
|
+
"treaty.versioning.factory.invalid_default_option",
|
|
215
|
+
type: @default_result.class
|
|
216
|
+
)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Validates version is not both default and deprecated
|
|
220
|
+
#
|
|
221
|
+
# @raise [Treaty::Exceptions::VersionDefaultDeprecatedConflict]
|
|
222
|
+
# @return [void]
|
|
223
|
+
def validate_default_deprecated_conflict!
|
|
224
|
+
return unless @default_result == true
|
|
225
|
+
return unless @deprecated_result == true
|
|
226
|
+
|
|
227
|
+
raise Treaty::Exceptions::VersionDefaultDeprecatedConflict,
|
|
228
|
+
I18n.t(
|
|
229
|
+
"treaty.versioning.factory.default_deprecated_conflict",
|
|
230
|
+
version: @version.version
|
|
231
|
+
)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
##########################################################################
|
|
235
|
+
|
|
236
|
+
# Catches unknown DSL methods with helpful error
|
|
237
|
+
#
|
|
238
|
+
# @raise [Treaty::Exceptions::MethodName]
|
|
239
|
+
def method_missing(name, *, &_block)
|
|
240
|
+
raise Treaty::Exceptions::MethodName,
|
|
241
|
+
I18n.t("treaty.versioning.factory.unknown_method", method: name)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Required for method_missing
|
|
245
|
+
#
|
|
246
|
+
# @return [Boolean]
|
|
247
|
+
def respond_to_missing?(name, *)
|
|
248
|
+
super
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Action
|
|
5
|
+
module Versions
|
|
6
|
+
# Resolves version factory based on specified or default version.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Finds the appropriate version factory from the collection based on:
|
|
11
|
+
# - Specified version (from request header or parameter)
|
|
12
|
+
# - Default version (if no version specified)
|
|
13
|
+
#
|
|
14
|
+
# Also enforces deprecation by raising error for deprecated versions.
|
|
15
|
+
#
|
|
16
|
+
# ## Usage
|
|
17
|
+
#
|
|
18
|
+
# Called internally by:
|
|
19
|
+
# - Versions::Workspace (at the start of treaty execution)
|
|
20
|
+
#
|
|
21
|
+
# ## Resolution Logic
|
|
22
|
+
#
|
|
23
|
+
# 1. If version specified → find matching factory or raise VersionNotFound
|
|
24
|
+
# 2. If no version → use default factory or raise SpecifiedVersionNotFound
|
|
25
|
+
# 3. If resolved factory is deprecated → raise Deprecated
|
|
26
|
+
#
|
|
27
|
+
# ## Error Types
|
|
28
|
+
#
|
|
29
|
+
# | Error | Condition |
|
|
30
|
+
# |-------|-----------|
|
|
31
|
+
# | VersionNotFound | Specified version doesn't exist |
|
|
32
|
+
# | SpecifiedVersionNotFound | No version specified and no default |
|
|
33
|
+
# | Deprecated | Resolved version is marked deprecated |
|
|
34
|
+
#
|
|
35
|
+
# ## Example
|
|
36
|
+
#
|
|
37
|
+
# # Find specific version:
|
|
38
|
+
# factory = Resolver.resolve!(
|
|
39
|
+
# specified_version: "2",
|
|
40
|
+
# collection_of_versions: collection
|
|
41
|
+
# )
|
|
42
|
+
#
|
|
43
|
+
# # Use default version:
|
|
44
|
+
# factory = Resolver.resolve!(
|
|
45
|
+
# specified_version: nil,
|
|
46
|
+
# collection_of_versions: collection
|
|
47
|
+
# )
|
|
48
|
+
class Resolver
|
|
49
|
+
# Resolves version factory (class method shortcut)
|
|
50
|
+
#
|
|
51
|
+
# @param specified_version [String, nil] Requested version or nil for default
|
|
52
|
+
# @param collection_of_versions [Treaty::Action::Versions::Collection] Available versions
|
|
53
|
+
# @return [Treaty::Action::Versions::Factory] Resolved version factory
|
|
54
|
+
# @raise [Treaty::Exceptions::VersionNotFound] If version doesn't exist
|
|
55
|
+
# @raise [Treaty::Exceptions::SpecifiedVersionNotFound] If no default available
|
|
56
|
+
# @raise [Treaty::Exceptions::Deprecated] If version is deprecated
|
|
57
|
+
def self.resolve!(...)
|
|
58
|
+
new(...).resolve!
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Creates a new resolver instance
|
|
62
|
+
#
|
|
63
|
+
# @param specified_version [String, nil] Requested version
|
|
64
|
+
# @param collection_of_versions [Treaty::Action::Versions::Collection] Available versions
|
|
65
|
+
def initialize(specified_version:, collection_of_versions:)
|
|
66
|
+
@specified_version = specified_version
|
|
67
|
+
@collection_of_versions = collection_of_versions
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Resolves and returns the appropriate version factory
|
|
71
|
+
#
|
|
72
|
+
# @return [Treaty::Action::Versions::Factory] Resolved version factory
|
|
73
|
+
# @raise [Treaty::Exceptions::VersionNotFound] If version doesn't exist
|
|
74
|
+
# @raise [Treaty::Exceptions::SpecifiedVersionNotFound] If no default available
|
|
75
|
+
# @raise [Treaty::Exceptions::Deprecated] If version is deprecated
|
|
76
|
+
def resolve!
|
|
77
|
+
determined_factory =
|
|
78
|
+
if specified_version_blank?
|
|
79
|
+
default_version_factory || raise_specified_version_not_found!
|
|
80
|
+
else
|
|
81
|
+
version_factory || raise_version_not_found!
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
raise_version_deprecated! if determined_factory.deprecated_result
|
|
85
|
+
|
|
86
|
+
determined_factory
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Finds factory matching specified version
|
|
92
|
+
#
|
|
93
|
+
# @return [Factory, nil] Matching factory or nil
|
|
94
|
+
def version_factory
|
|
95
|
+
@version_factory ||=
|
|
96
|
+
@collection_of_versions.find do |factory|
|
|
97
|
+
factory.version.version == @specified_version
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Finds default version factory
|
|
102
|
+
#
|
|
103
|
+
# @return [Factory, nil] Default factory or nil
|
|
104
|
+
def default_version_factory
|
|
105
|
+
@default_version_factory ||=
|
|
106
|
+
@collection_of_versions.find(&:default_result)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Checks if specified version is blank
|
|
110
|
+
#
|
|
111
|
+
# @return [Boolean] True if version is nil or empty string
|
|
112
|
+
def specified_version_blank?
|
|
113
|
+
@specified_version.to_s.strip.empty?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
##########################################################################
|
|
117
|
+
|
|
118
|
+
# Raises error when no version specified and no default exists
|
|
119
|
+
#
|
|
120
|
+
# @raise [Treaty::Exceptions::SpecifiedVersionNotFound]
|
|
121
|
+
def raise_specified_version_not_found!
|
|
122
|
+
raise Treaty::Exceptions::SpecifiedVersionNotFound,
|
|
123
|
+
I18n.t("treaty.versioning.resolver.specified_version_required")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Raises error when specified version doesn't exist
|
|
127
|
+
#
|
|
128
|
+
# @raise [Treaty::Exceptions::VersionNotFound]
|
|
129
|
+
def raise_version_not_found!
|
|
130
|
+
raise Treaty::Exceptions::VersionNotFound,
|
|
131
|
+
I18n.t(
|
|
132
|
+
"treaty.versioning.resolver.version_not_found",
|
|
133
|
+
version: @specified_version
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Raises error when resolved version is deprecated
|
|
138
|
+
#
|
|
139
|
+
# @raise [Treaty::Exceptions::Deprecated]
|
|
140
|
+
def raise_version_deprecated!
|
|
141
|
+
raise Treaty::Exceptions::Deprecated,
|
|
142
|
+
I18n.t(
|
|
143
|
+
"treaty.versioning.resolver.version_deprecated",
|
|
144
|
+
version: @specified_version
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Action
|
|
5
|
+
module Versions
|
|
6
|
+
# Semantic version wrapper using Gem::Version.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Normalizes version inputs (Integer, String, Array) into a consistent
|
|
11
|
+
# Gem::Version object. This enables proper version comparison and
|
|
12
|
+
# semantic versioning support.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Created internally by:
|
|
17
|
+
# - Versions::Factory (to store version number)
|
|
18
|
+
#
|
|
19
|
+
# Consumed by:
|
|
20
|
+
# - Versions::Resolver (to match requested version)
|
|
21
|
+
# - Info::Builder (to display version info)
|
|
22
|
+
#
|
|
23
|
+
# ## Version Formats
|
|
24
|
+
#
|
|
25
|
+
# Accepts multiple input formats:
|
|
26
|
+
# - Integer: `1` → "1"
|
|
27
|
+
# - String: `"1.0.0"` → "1.0.0"
|
|
28
|
+
# - Array: `[1, 0, 0]` → "1.0.0"
|
|
29
|
+
#
|
|
30
|
+
# ## Underlying Implementation
|
|
31
|
+
#
|
|
32
|
+
# Uses Gem::Version internally which provides:
|
|
33
|
+
# - Semantic version comparison (1.0.0 < 1.0.1 < 1.1.0 < 2.0.0)
|
|
34
|
+
# - Proper handling of pre-release versions (1.0.0-alpha < 1.0.0)
|
|
35
|
+
# - String normalization
|
|
36
|
+
#
|
|
37
|
+
# ## Example
|
|
38
|
+
#
|
|
39
|
+
# Semantic.new(1).version # => Gem::Version.new("1")
|
|
40
|
+
# Semantic.new("1.2.3").version # => Gem::Version.new("1.2.3")
|
|
41
|
+
# Semantic.new([1, 2, 3]).version # => Gem::Version.new("1.2.3")
|
|
42
|
+
class Semantic
|
|
43
|
+
# @return [Gem::Version] Normalized semantic version
|
|
44
|
+
attr_reader :version
|
|
45
|
+
|
|
46
|
+
# Creates a new semantic version wrapper
|
|
47
|
+
#
|
|
48
|
+
# @param version [Integer, String, Array] Version in any supported format
|
|
49
|
+
def initialize(version)
|
|
50
|
+
version =
|
|
51
|
+
if version.is_a?(Array)
|
|
52
|
+
version.join(".")
|
|
53
|
+
# elsif version.is_a?(Integer)
|
|
54
|
+
# version.to_s
|
|
55
|
+
else
|
|
56
|
+
version # rubocop:disable Style/RedundantSelfAssignmentBranch
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@version = Gem::Version.new(version)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Action
|
|
5
|
+
module Versions
|
|
6
|
+
# Core execution module for treaty call! method.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Provides the `call!` instance method that orchestrates the complete
|
|
11
|
+
# treaty execution flow: version resolution, request validation,
|
|
12
|
+
# service execution, and response validation.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Included via:
|
|
17
|
+
# - Versions::DSL (when DSL is included in treaty class)
|
|
18
|
+
#
|
|
19
|
+
# Called by:
|
|
20
|
+
# - Controller integration (via Treaty::Action::Base.call!)
|
|
21
|
+
#
|
|
22
|
+
# ## Execution Flow
|
|
23
|
+
#
|
|
24
|
+
# ```
|
|
25
|
+
# call!(context:, inventory:, version:, params:)
|
|
26
|
+
# │
|
|
27
|
+
# ├─1─► Resolver.resolve! ──► Find version factory
|
|
28
|
+
# │
|
|
29
|
+
# ├─2─► Request::Validator.validate! ──► Validate params
|
|
30
|
+
# │
|
|
31
|
+
# ├─3─► Execution::Request.execute! ──► Run service
|
|
32
|
+
# │
|
|
33
|
+
# ├─4─► Response::Validator.validate! ──► Validate response
|
|
34
|
+
# │
|
|
35
|
+
# └─5─► Result.new ──► Return result object
|
|
36
|
+
# ```
|
|
37
|
+
#
|
|
38
|
+
# ## Parameters
|
|
39
|
+
#
|
|
40
|
+
# | Parameter | Description |
|
|
41
|
+
# |-----------|-------------|
|
|
42
|
+
# | context | Controller instance |
|
|
43
|
+
# | inventory | Inventory::Collection with controller data |
|
|
44
|
+
# | version | Requested version string (or nil for default) |
|
|
45
|
+
# | params | Request parameters |
|
|
46
|
+
#
|
|
47
|
+
# ## Return Value
|
|
48
|
+
#
|
|
49
|
+
# Returns `Treaty::Action::Result` with:
|
|
50
|
+
# - `data` - Validated response hash
|
|
51
|
+
# - `status` - HTTP status code
|
|
52
|
+
# - `version` - Resolved version string
|
|
53
|
+
module Workspace
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Executes the complete treaty flow
|
|
57
|
+
#
|
|
58
|
+
# Orchestrates version resolution, validation, execution,
|
|
59
|
+
# and returns a Result object.
|
|
60
|
+
#
|
|
61
|
+
# @param context [Object] Controller instance
|
|
62
|
+
# @param inventory [Treaty::Action::Inventory::Collection] Controller data
|
|
63
|
+
# @param version [String, nil] Requested version or nil for default
|
|
64
|
+
# @param params [Hash] Request parameters
|
|
65
|
+
# @return [Treaty::Action::Result] Execution result
|
|
66
|
+
# @raise [Treaty::Exceptions::VersionNotFound] If version not found
|
|
67
|
+
# @raise [Treaty::Exceptions::Deprecated] If version is deprecated
|
|
68
|
+
# @raise [Treaty::Exceptions::Validation] If validation fails
|
|
69
|
+
# @raise [Treaty::Exceptions::Execution] If service execution fails
|
|
70
|
+
def call!(context:, inventory:, version:, params:, **) # rubocop:disable Metrics/MethodLength
|
|
71
|
+
super
|
|
72
|
+
|
|
73
|
+
version_factory = Resolver.resolve!(
|
|
74
|
+
specified_version: version,
|
|
75
|
+
collection_of_versions: @collection_of_versions
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
validated_params = Request::Validator.validate!(
|
|
79
|
+
params:,
|
|
80
|
+
version_factory:
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
executor_result = Execution::Request.execute!(
|
|
84
|
+
context:,
|
|
85
|
+
inventory:,
|
|
86
|
+
version_factory:,
|
|
87
|
+
validated_params:
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
validated_response = Response::Validator.validate!(
|
|
91
|
+
version_factory:,
|
|
92
|
+
response_data: executor_result
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
status = version_factory.response_factory&.status || 200
|
|
96
|
+
|
|
97
|
+
Result.new(
|
|
98
|
+
data: validated_response,
|
|
99
|
+
status:,
|
|
100
|
+
version: version_factory.version.version
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
# Action namespace containing the base class and versioning system.
|
|
5
|
+
#
|
|
6
|
+
# Users should inherit from Treaty::Action::Base:
|
|
7
|
+
#
|
|
8
|
+
# class Posts::CreateTreaty < Treaty::Action::Base
|
|
9
|
+
# version 1, default: true do
|
|
10
|
+
# summary "Create a new post"
|
|
11
|
+
#
|
|
12
|
+
# request do
|
|
13
|
+
# object :post do
|
|
14
|
+
# string :title, :required
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# response 201 do
|
|
19
|
+
# object :post do
|
|
20
|
+
# string :id
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# delegate_to Posts::CreateService
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# @see Treaty::Action::Base for full documentation and examples
|
|
29
|
+
module Action
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -57,7 +57,7 @@ module Treaty
|
|
|
57
57
|
def treaty_build_inventory_for(action_name, block)
|
|
58
58
|
return nil unless block
|
|
59
59
|
|
|
60
|
-
factory = Treaty::Inventory::Factory.new(action_name)
|
|
60
|
+
factory = Treaty::Action::Inventory::Factory.new(action_name)
|
|
61
61
|
factory.instance_eval(&block)
|
|
62
62
|
factory.collection
|
|
63
63
|
end
|
data/lib/treaty/engine.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Treaty
|
|
|
12
12
|
|
|
13
13
|
initializer "treaty.register_option_processors", before: :load_config_initializers do
|
|
14
14
|
# Register all option processors (validators and modifiers)
|
|
15
|
-
require "treaty/attribute/option/registry_initializer"
|
|
15
|
+
require "treaty/entity/attribute/option/registry_initializer"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
initializer "treaty.validate_configuration" do
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Treaty
|
|
4
|
-
module
|
|
5
|
-
module
|
|
4
|
+
module Entity
|
|
5
|
+
module Attribute
|
|
6
6
|
# Entity-specific attribute that defaults to required: true
|
|
7
|
-
class Attribute <
|
|
7
|
+
class Attribute < Base
|
|
8
8
|
private
|
|
9
9
|
|
|
10
10
|
def apply_defaults!
|
|
@@ -16,7 +16,7 @@ module Treaty
|
|
|
16
16
|
def process_nested_attributes(&block)
|
|
17
17
|
return unless object_or_array?
|
|
18
18
|
|
|
19
|
-
builder = Builder.new(collection_of_attributes, @nesting_level + 1)
|
|
19
|
+
builder = Treaty::Entity::Builder.new(collection_of_attributes, @nesting_level + 1)
|
|
20
20
|
builder.instance_eval(&block)
|
|
21
21
|
end
|
|
22
22
|
end
|