treaty 0.7.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -0
  3. data/config/locales/en.yml +3 -0
  4. data/lib/treaty/attribute/base.rb +13 -5
  5. data/lib/treaty/attribute/dsl.rb +90 -0
  6. data/lib/treaty/attribute/entity/attribute.rb +25 -0
  7. data/lib/treaty/attribute/entity/builder.rb +23 -0
  8. data/lib/treaty/attribute/option/base.rb +17 -1
  9. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +5 -3
  10. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +20 -8
  11. data/lib/treaty/attribute/option/validators/required_validator.rb +8 -2
  12. data/lib/treaty/attribute/option/validators/type_validator.rb +51 -40
  13. data/lib/treaty/attribute/option_orchestrator.rb +7 -5
  14. data/lib/treaty/attribute/validation/nested_array_validator.rb +18 -12
  15. data/lib/treaty/attribute/validation/nested_transformer.rb +18 -12
  16. data/lib/treaty/base.rb +1 -1
  17. data/lib/treaty/controller/dsl.rb +4 -1
  18. data/lib/treaty/entity.rb +84 -0
  19. data/lib/treaty/info/entity/builder.rb +50 -0
  20. data/lib/treaty/info/entity/dsl.rb +28 -0
  21. data/lib/treaty/info/entity/result.rb +15 -0
  22. data/lib/treaty/info/rest/builder.rb +110 -0
  23. data/lib/treaty/info/rest/dsl.rb +28 -0
  24. data/lib/treaty/info/rest/result.rb +15 -0
  25. data/lib/treaty/request/attribute/attribute.rb +1 -0
  26. data/lib/treaty/request/attribute/builder.rb +1 -0
  27. data/lib/treaty/request/entity.rb +33 -0
  28. data/lib/treaty/request/factory.rb +61 -14
  29. data/lib/treaty/request/validator.rb +65 -0
  30. data/lib/treaty/response/attribute/attribute.rb +1 -0
  31. data/lib/treaty/response/attribute/builder.rb +1 -0
  32. data/lib/treaty/response/entity.rb +33 -0
  33. data/lib/treaty/response/factory.rb +61 -14
  34. data/lib/treaty/response/validator.rb +57 -0
  35. data/lib/treaty/version.rb +1 -1
  36. data/lib/treaty/versions/execution/request.rb +10 -5
  37. data/lib/treaty/versions/factory.rb +16 -5
  38. data/lib/treaty/versions/resolver.rb +8 -2
  39. data/lib/treaty/versions/workspace.rb +2 -2
  40. metadata +15 -8
  41. data/lib/treaty/info/builder.rb +0 -108
  42. data/lib/treaty/info/dsl.rb +0 -26
  43. data/lib/treaty/info/result.rb +0 -13
  44. data/lib/treaty/request/attribute/validation/orchestrator.rb +0 -19
  45. data/lib/treaty/request/attribute/validator.rb +0 -50
  46. data/lib/treaty/response/attribute/validation/orchestrator.rb +0 -19
  47. data/lib/treaty/response/attribute/validator.rb +0 -44
@@ -2,6 +2,27 @@
2
2
 
3
3
  module Treaty
4
4
  module Response
5
+ # Factory for creating response definitions.
6
+ #
7
+ # Supports two modes:
8
+ # 1. Block mode: Creates an anonymous Response::Entity class with the block
9
+ # 2. Entity mode: Uses a provided Entity class directly
10
+ #
11
+ # ## Block Mode
12
+ #
13
+ # ```ruby
14
+ # response 200 do
15
+ # object :post do
16
+ # string :id
17
+ # end
18
+ # end
19
+ # ```
20
+ #
21
+ # ## Entity Mode
22
+ #
23
+ # ```ruby
24
+ # response 200, PostResponseEntity
25
+ # ```
5
26
  class Factory
6
27
  attr_reader :status
7
28
 
@@ -9,32 +30,58 @@ module Treaty
9
30
  @status = status
10
31
  end
11
32
 
12
- def attribute(name, type, *helpers, **options, &block)
13
- collection_of_attributes << Attribute::Attribute.new(
14
- name,
15
- type,
16
- *helpers,
17
- nesting_level: 0,
18
- **options,
19
- &block
20
- )
33
+ # Uses a provided Entity class
34
+ #
35
+ # @param entity_class [Class] Entity class to use
36
+ # @return [void]
37
+ # @raise [Treaty::Exceptions::Validation] if entity_class is not a valid Treaty::Entity subclass
38
+ def use_entity(entity_class)
39
+ validate_entity_class!(entity_class)
40
+ @entity_class = entity_class
21
41
  end
22
42
 
43
+ # Returns collection of attributes from the entity class
44
+ #
45
+ # @return [Collection] Collection of attributes
23
46
  def collection_of_attributes
24
- @collection_of_attributes ||= Treaty::Attribute::Collection.new
25
- end
47
+ return Treaty::Attribute::Collection.new if @entity_class.nil?
26
48
 
27
- ##########################################################################
49
+ @entity_class.collection_of_attributes
50
+ end
28
51
 
52
+ # Handles DSL methods for defining attributes
53
+ #
54
+ # This allows the factory to be used with method_missing
55
+ # for backwards compatibility with direct method calls.
56
+ # Creates an anonymous Response::Entity class on first use.
29
57
  def method_missing(type, *helpers, **options, &block)
30
- name = helpers.shift
58
+ # If no entity class yet, create one
59
+ @entity_class ||= Class.new(Entity)
31
60
 
32
- attribute(name, type, *helpers, **options, &block)
61
+ # Call the method on the entity class
62
+ @entity_class.public_send(type, *helpers, **options, &block)
33
63
  end
34
64
 
35
65
  def respond_to_missing?(name, *)
36
66
  super
37
67
  end
68
+
69
+ private
70
+
71
+ # Validates that the provided entity_class is a valid Treaty::Entity subclass
72
+ #
73
+ # @param entity_class [Class] Entity class to validate
74
+ # @raise [Treaty::Exceptions::Validation] if entity_class is not a valid Treaty::Entity subclass
75
+ def validate_entity_class!(entity_class)
76
+ return if entity_class.is_a?(Class) && entity_class < Treaty::Entity
77
+
78
+ raise Treaty::Exceptions::Validation,
79
+ I18n.t(
80
+ "treaty.response.factory.invalid_entity_class",
81
+ type: entity_class.class,
82
+ value: entity_class
83
+ )
84
+ end
38
85
  end
39
86
  end
40
87
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Response
5
+ # Validator for response data
6
+ class Validator
7
+ class << self
8
+ # Validates response data against the response definition
9
+ #
10
+ # @param response_data [Hash] Response data to validate
11
+ # @param version_factory [Versions::Factory] Version factory with response definition
12
+ # @return [Hash] Validated and transformed response data
13
+ def validate!(version_factory:, response_data: {})
14
+ new(version_factory:, response_data:).validate!
15
+ end
16
+ end
17
+
18
+ def initialize(version_factory:, response_data: {})
19
+ @version_factory = version_factory
20
+ @response_data = response_data
21
+ end
22
+
23
+ def validate!
24
+ validate_response_attributes!
25
+ end
26
+
27
+ private
28
+
29
+ def validate_response_attributes!
30
+ return @response_data unless response_attributes_exist?
31
+
32
+ # Create orchestrator for both DIRECT and ADAPTER strategies
33
+ # Orchestrator filters data by attributes and performs transformation
34
+ orchestrator_class = Class.new(Treaty::Attribute::Validation::Orchestrator::Base) do
35
+ define_method(:collection_of_attributes) do
36
+ @version_factory.response_factory.collection_of_attributes
37
+ end
38
+ end
39
+
40
+ orchestrator_class.validate!(
41
+ version_factory: @version_factory,
42
+ data: @response_data
43
+ )
44
+ end
45
+
46
+ def adapter_strategy?
47
+ !@version_factory.strategy_instance.direct?
48
+ end
49
+
50
+ def response_attributes_exist?
51
+ return false if @version_factory.response_factory&.collection_of_attributes&.empty?
52
+
53
+ @version_factory.response_factory.collection_of_attributes.exists?
54
+ end
55
+ end
56
+ end
57
+ end
@@ -3,7 +3,7 @@
3
3
  module Treaty
4
4
  module VERSION
5
5
  MAJOR = 0
6
- MINOR = 7
6
+ MINOR = 8
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
@@ -3,7 +3,7 @@
3
3
  module Treaty
4
4
  module Versions
5
5
  module Execution
6
- class Request
6
+ class Request # rubocop:disable Metrics/ClassLength
7
7
  def self.execute!(...)
8
8
  new(...).execute!
9
9
  end
@@ -117,9 +117,11 @@ module Treaty
117
117
 
118
118
  unless executor.respond_to?(method_name)
119
119
  raise Treaty::Exceptions::Execution,
120
- I18n.t("treaty.execution.method_not_found",
121
- method: method_name,
122
- class_name: executor)
120
+ I18n.t(
121
+ "treaty.execution.method_not_found",
122
+ method: method_name,
123
+ class_name: executor
124
+ )
123
125
  end
124
126
 
125
127
  executor.public_send(method_name, params: @validated_params)
@@ -134,7 +136,10 @@ module Treaty
134
136
 
135
137
  def raise_executor_missing_error!
136
138
  raise Treaty::Exceptions::Execution,
137
- I18n.t("treaty.execution.executor_missing", version: @version_factory.version)
139
+ I18n.t(
140
+ "treaty.execution.executor_missing",
141
+ version: @version_factory.version
142
+ )
138
143
  end
139
144
 
140
145
  def servactory_service?
@@ -48,16 +48,24 @@ module Treaty
48
48
  @deprecated_result = result
49
49
  end
50
50
 
51
- def request(&block)
51
+ def request(entity_class = nil, &block)
52
52
  @request_factory ||= Request::Factory.new
53
53
 
54
- @request_factory.instance_eval(&block) if block_given?
54
+ if entity_class.present?
55
+ @request_factory.use_entity(entity_class)
56
+ elsif block_given?
57
+ @request_factory.instance_eval(&block)
58
+ end
55
59
  end
56
60
 
57
- def response(status, &block)
61
+ def response(status, entity_class = nil, &block)
58
62
  @response_factory ||= Response::Factory.new(status)
59
63
 
60
- @response_factory.instance_eval(&block) if block_given?
64
+ if entity_class.present?
65
+ @response_factory.use_entity(entity_class)
66
+ elsif block_given?
67
+ @response_factory.instance_eval(&block)
68
+ end
61
69
  end
62
70
 
63
71
  def delegate_to(executor, method = :call)
@@ -74,7 +82,10 @@ module Treaty
74
82
  end
75
83
 
76
84
  raise Treaty::Exceptions::Validation,
77
- I18n.t("treaty.versioning.factory.invalid_default_option", type: @default_result.class)
85
+ I18n.t(
86
+ "treaty.versioning.factory.invalid_default_option",
87
+ type: @default_result.class
88
+ )
78
89
  end
79
90
 
80
91
  ##########################################################################
@@ -52,12 +52,18 @@ module Treaty
52
52
 
53
53
  def raise_version_not_found!
54
54
  raise Treaty::Exceptions::Validation,
55
- I18n.t("treaty.versioning.resolver.version_not_found", version: @current_version)
55
+ I18n.t(
56
+ "treaty.versioning.resolver.version_not_found",
57
+ version: @current_version
58
+ )
56
59
  end
57
60
 
58
61
  def raise_version_deprecated!
59
62
  raise Treaty::Exceptions::Deprecated,
60
- I18n.t("treaty.versioning.resolver.version_deprecated", version: @current_version)
63
+ I18n.t(
64
+ "treaty.versioning.resolver.version_deprecated",
65
+ version: @current_version
66
+ )
61
67
  end
62
68
  end
63
69
  end
@@ -13,7 +13,7 @@ module Treaty
13
13
  collection_of_versions: @collection_of_versions
14
14
  )
15
15
 
16
- validated_params = Request::Attribute::Validator.validate!(
16
+ validated_params = Request::Validator.validate!(
17
17
  params:,
18
18
  version_factory:
19
19
  )
@@ -23,7 +23,7 @@ module Treaty
23
23
  validated_params:
24
24
  )
25
25
 
26
- validated_response = Response::Attribute::Validator.validate!(
26
+ validated_response = Response::Validator.validate!(
27
27
  version_factory:,
28
28
  response_data: executor_result
29
29
  )
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: treaty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
@@ -150,6 +150,9 @@ files:
150
150
  - lib/treaty/attribute/base.rb
151
151
  - lib/treaty/attribute/builder/base.rb
152
152
  - lib/treaty/attribute/collection.rb
153
+ - lib/treaty/attribute/dsl.rb
154
+ - lib/treaty/attribute/entity/attribute.rb
155
+ - lib/treaty/attribute/entity/builder.rb
153
156
  - lib/treaty/attribute/helper_mapper.rb
154
157
  - lib/treaty/attribute/option/base.rb
155
158
  - lib/treaty/attribute/option/modifiers/as_modifier.rb
@@ -174,6 +177,7 @@ files:
174
177
  - lib/treaty/context/workspace.rb
175
178
  - lib/treaty/controller/dsl.rb
176
179
  - lib/treaty/engine.rb
180
+ - lib/treaty/entity.rb
177
181
  - lib/treaty/exceptions/base.rb
178
182
  - lib/treaty/exceptions/class_name.rb
179
183
  - lib/treaty/exceptions/deprecated.rb
@@ -184,19 +188,22 @@ files:
184
188
  - lib/treaty/exceptions/strategy.rb
185
189
  - lib/treaty/exceptions/unexpected.rb
186
190
  - lib/treaty/exceptions/validation.rb
187
- - lib/treaty/info/builder.rb
188
- - lib/treaty/info/dsl.rb
189
- - lib/treaty/info/result.rb
191
+ - lib/treaty/info/entity/builder.rb
192
+ - lib/treaty/info/entity/dsl.rb
193
+ - lib/treaty/info/entity/result.rb
194
+ - lib/treaty/info/rest/builder.rb
195
+ - lib/treaty/info/rest/dsl.rb
196
+ - lib/treaty/info/rest/result.rb
190
197
  - lib/treaty/request/attribute/attribute.rb
191
198
  - lib/treaty/request/attribute/builder.rb
192
- - lib/treaty/request/attribute/validation/orchestrator.rb
193
- - lib/treaty/request/attribute/validator.rb
199
+ - lib/treaty/request/entity.rb
194
200
  - lib/treaty/request/factory.rb
201
+ - lib/treaty/request/validator.rb
195
202
  - lib/treaty/response/attribute/attribute.rb
196
203
  - lib/treaty/response/attribute/builder.rb
197
- - lib/treaty/response/attribute/validation/orchestrator.rb
198
- - lib/treaty/response/attribute/validator.rb
204
+ - lib/treaty/response/entity.rb
199
205
  - lib/treaty/response/factory.rb
206
+ - lib/treaty/response/validator.rb
200
207
  - lib/treaty/result.rb
201
208
  - lib/treaty/strategy.rb
202
209
  - lib/treaty/support/loader.rb
@@ -1,108 +0,0 @@
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
- build_attributes_structure(version.request_factory)
60
- end
61
-
62
- def build_response_with(version)
63
- response_factory = version.response_factory
64
- {
65
- status: response_factory.status
66
- }.merge(build_attributes_structure(response_factory))
67
- end
68
-
69
- ##########################################################################
70
-
71
- def build_attributes_structure(factory)
72
- {
73
- attributes: build_attributes_hash(factory.collection_of_attributes)
74
- }
75
- end
76
-
77
- def build_attributes_hash(collection, current_level = 0)
78
- # validate_nesting_level!(current_level)
79
-
80
- collection.to_h do |attribute|
81
- [
82
- attribute.name,
83
- {
84
- type: attribute.type,
85
- options: attribute.options,
86
- attributes: build_nested_attributes(attribute, current_level)
87
- }
88
- ]
89
- end
90
- end
91
-
92
- def build_nested_attributes(attribute, current_level)
93
- return {} unless attribute.nested?
94
-
95
- build_attributes_hash(attribute.collection_of_attributes, current_level + 1)
96
- end
97
-
98
- # def validate_nesting_level!(level)
99
- # return unless level > Treaty::Engine.config.treaty.attribute_nesting_level
100
- #
101
- # raise Treaty::Exceptions::NestedAttributes,
102
- # I18n.t("treaty.attributes.errors.nesting_level_exceeded",
103
- # level:,
104
- # max_level: Treaty::Engine.config.treaty.attribute_nesting_level)
105
- # end
106
- end
107
- end
108
- end
@@ -1,26 +0,0 @@
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
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Info
5
- class Result
6
- attr_reader :versions
7
-
8
- def initialize(builder)
9
- @versions = builder.versions
10
- end
11
- end
12
- end
13
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- module Attribute
6
- module Validation
7
- class Orchestrator < Treaty::Attribute::Validation::Orchestrator::Base
8
- private
9
-
10
- def collection_of_attributes
11
- return Treaty::Attribute::Collection.new if version_factory.request_factory.nil?
12
-
13
- version_factory.request_factory.collection_of_attributes
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- module Attribute
6
- class Validator < Treaty::Attribute::Validation::Base
7
- def self.validate!(...)
8
- new(...).validate!
9
- end
10
-
11
- def initialize(params:, version_factory:)
12
- super(version_factory:)
13
-
14
- @params = params
15
- end
16
-
17
- def validate!
18
- validate_request_attributes!
19
- end
20
-
21
- private
22
-
23
- def request_data
24
- @request_data ||= begin
25
- @params.to_unsafe_h
26
- rescue NoMethodError
27
- @params
28
- end
29
- end
30
-
31
- def validate_request_attributes!
32
- return request_data unless adapter_strategy?
33
- return request_data unless request_attributes_exist?
34
-
35
- # For adapter strategy:
36
- Validation::Orchestrator.validate!(
37
- version_factory: @version_factory,
38
- data: request_data
39
- )
40
- end
41
-
42
- def request_attributes_exist?
43
- return false if @version_factory.request_factory&.collection_of_attributes&.empty?
44
-
45
- @version_factory.request_factory.collection_of_attributes.exists?
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Response
5
- module Attribute
6
- module Validation
7
- class Orchestrator < Treaty::Attribute::Validation::Orchestrator::Base
8
- private
9
-
10
- def collection_of_attributes
11
- return Treaty::Attribute::Collection.new if version_factory.response_factory.nil?
12
-
13
- version_factory.response_factory.collection_of_attributes
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Response
5
- module Attribute
6
- class Validator < Treaty::Attribute::Validation::Base
7
- def self.validate!(version_factory:, response_data: {})
8
- new(
9
- version_factory:,
10
- response_data:
11
- ).validate!
12
- end
13
-
14
- def initialize(version_factory:, response_data: {})
15
- super(version_factory:)
16
-
17
- @response_data = response_data
18
- end
19
-
20
- def validate!
21
- validate_response_attributes!
22
- end
23
-
24
- private
25
-
26
- def validate_response_attributes!
27
- return @response_data unless response_attributes_exist?
28
-
29
- # For adapter strategy:
30
- Validation::Orchestrator.validate!(
31
- version_factory: @version_factory,
32
- data: @response_data
33
- )
34
- end
35
-
36
- def response_attributes_exist?
37
- return false if @version_factory.response_factory&.collection_of_attributes&.empty?
38
-
39
- @version_factory.response_factory.collection_of_attributes.exists?
40
- end
41
- end
42
- end
43
- end
44
- end