treaty 0.0.1 → 0.2.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -17
  3. data/Rakefile +4 -2
  4. data/config/locales/en.yml +96 -0
  5. data/lib/treaty/attribute/base.rb +174 -0
  6. data/lib/treaty/attribute/builder/base.rb +143 -0
  7. data/lib/treaty/attribute/collection.rb +65 -0
  8. data/lib/treaty/attribute/helper_mapper.rb +72 -0
  9. data/lib/treaty/attribute/option/base.rb +160 -0
  10. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +88 -0
  11. data/lib/treaty/attribute/option/modifiers/default_modifier.rb +103 -0
  12. data/lib/treaty/attribute/option/registry.rb +128 -0
  13. data/lib/treaty/attribute/option/registry_initializer.rb +90 -0
  14. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +80 -0
  15. data/lib/treaty/attribute/option/validators/required_validator.rb +92 -0
  16. data/lib/treaty/attribute/option/validators/type_validator.rb +159 -0
  17. data/lib/treaty/attribute/option_normalizer.rb +151 -0
  18. data/lib/treaty/attribute/option_orchestrator.rb +187 -0
  19. data/lib/treaty/attribute/validation/attribute_validator.rb +144 -0
  20. data/lib/treaty/attribute/validation/base.rb +92 -0
  21. data/lib/treaty/attribute/validation/nested_array_validator.rb +199 -0
  22. data/lib/treaty/attribute/validation/nested_object_validator.rb +103 -0
  23. data/lib/treaty/attribute/validation/nested_transformer.rb +246 -0
  24. data/lib/treaty/attribute/validation/orchestrator/base.rb +194 -0
  25. data/lib/treaty/base.rb +9 -0
  26. data/lib/treaty/configuration.rb +17 -0
  27. data/lib/treaty/context/callable.rb +24 -0
  28. data/lib/treaty/context/dsl.rb +12 -0
  29. data/lib/treaty/context/workspace.rb +28 -0
  30. data/lib/treaty/controller/dsl.rb +38 -0
  31. data/lib/treaty/engine.rb +37 -0
  32. data/lib/treaty/exceptions/base.rb +47 -0
  33. data/lib/treaty/exceptions/class_name.rb +50 -0
  34. data/lib/treaty/exceptions/deprecated.rb +54 -0
  35. data/lib/treaty/exceptions/execution.rb +66 -0
  36. data/lib/treaty/exceptions/method_name.rb +55 -0
  37. data/lib/treaty/exceptions/nested_attributes.rb +65 -0
  38. data/lib/treaty/exceptions/not_implemented.rb +32 -0
  39. data/lib/treaty/exceptions/strategy.rb +63 -0
  40. data/lib/treaty/exceptions/unexpected.rb +70 -0
  41. data/lib/treaty/exceptions/validation.rb +97 -0
  42. data/lib/treaty/info/builder.rb +122 -0
  43. data/lib/treaty/info/dsl.rb +26 -0
  44. data/lib/treaty/info/result.rb +13 -0
  45. data/lib/treaty/request/attribute/attribute.rb +24 -0
  46. data/lib/treaty/request/attribute/builder.rb +22 -0
  47. data/lib/treaty/request/attribute/validation/orchestrator.rb +27 -0
  48. data/lib/treaty/request/attribute/validator.rb +50 -0
  49. data/lib/treaty/request/factory.rb +32 -0
  50. data/lib/treaty/request/scope/collection.rb +21 -0
  51. data/lib/treaty/request/scope/factory.rb +42 -0
  52. data/lib/treaty/response/attribute/attribute.rb +24 -0
  53. data/lib/treaty/response/attribute/builder.rb +22 -0
  54. data/lib/treaty/response/attribute/validation/orchestrator.rb +27 -0
  55. data/lib/treaty/response/attribute/validator.rb +44 -0
  56. data/lib/treaty/response/factory.rb +38 -0
  57. data/lib/treaty/response/scope/collection.rb +21 -0
  58. data/lib/treaty/response/scope/factory.rb +42 -0
  59. data/lib/treaty/result.rb +22 -0
  60. data/lib/treaty/strategy.rb +31 -0
  61. data/lib/treaty/support/loader.rb +24 -0
  62. data/lib/treaty/version.rb +8 -1
  63. data/lib/treaty/versions/collection.rb +15 -0
  64. data/lib/treaty/versions/dsl.rb +30 -0
  65. data/lib/treaty/versions/execution/request.rb +147 -0
  66. data/lib/treaty/versions/executor.rb +14 -0
  67. data/lib/treaty/versions/factory.rb +92 -0
  68. data/lib/treaty/versions/resolver.rb +69 -0
  69. data/lib/treaty/versions/semantic.rb +22 -0
  70. data/lib/treaty/versions/workspace.rb +40 -0
  71. data/lib/treaty.rb +3 -3
  72. metadata +200 -27
  73. data/.standard.yml +0 -3
  74. data/CHANGELOG.md +0 -5
  75. data/CODE_OF_CONDUCT.md +0 -84
  76. data/LICENSE.txt +0 -21
  77. data/sig/treaty.rbs +0 -4
  78. data/treaty.gemspec +0 -35
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Validation
6
+ # Handles transformation of nested attributes (objects and arrays).
7
+ # Extracted from Orchestrator::Base to reduce complexity.
8
+ class NestedTransformer
9
+ SELF_SCOPE = :_self
10
+ private_constant :SELF_SCOPE
11
+
12
+ attr_reader :attribute
13
+
14
+ # Creates a new nested transformer
15
+ #
16
+ # @param attribute [Attribute::Base] The attribute with nested structure
17
+ def initialize(attribute)
18
+ @attribute = attribute
19
+ end
20
+
21
+ # Transforms nested attribute value (object or array)
22
+ # Returns original value if nil or not nested
23
+ #
24
+ # @param value [Object] The value to transform
25
+ # @return [Object] Transformed value
26
+ def transform(value)
27
+ return value if value.nil?
28
+
29
+ case attribute.type
30
+ when :object
31
+ transform_object(value)
32
+ when :array
33
+ transform_array(value)
34
+ else
35
+ value
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # Transforms object (hash) value
42
+ #
43
+ # @param value [Hash] The hash to transform
44
+ # @return [Hash] Transformed hash
45
+ def transform_object(value)
46
+ return value unless attribute.nested?
47
+
48
+ transformer = ObjectTransformer.new(attribute)
49
+ transformer.transform(value)
50
+ end
51
+
52
+ # Transforms array value
53
+ #
54
+ # @param value [Array] The array to transform
55
+ # @return [Array] Transformed array
56
+ def transform_array(value)
57
+ return value unless attribute.nested?
58
+
59
+ transformer = ArrayTransformer.new(attribute)
60
+ transformer.transform(value)
61
+ end
62
+
63
+ # Transforms object (hash) with nested attributes
64
+ class ObjectTransformer
65
+ attr_reader :attribute
66
+
67
+ # Creates a new object transformer
68
+ #
69
+ # @param attribute [Attribute::Base] The object-type attribute
70
+ def initialize(attribute)
71
+ @attribute = attribute
72
+ end
73
+
74
+ # Transforms hash by processing all nested attributes
75
+ #
76
+ # @param value [Hash] The source hash
77
+ # @return [Hash] Transformed hash with processed attributes
78
+ def transform(value)
79
+ transformed = {}
80
+
81
+ attribute.collection_of_attributes.each do |nested_attribute|
82
+ process_attribute(nested_attribute, value, transformed)
83
+ end
84
+
85
+ transformed
86
+ end
87
+
88
+ private
89
+
90
+ # Processes a single nested attribute
91
+ # Validates, transforms, and adds to target hash
92
+ #
93
+ # @param nested_attribute [Attribute::Base] Attribute to process
94
+ # @param source_hash [Hash] Source data
95
+ # @param target_hash [Hash] Target hash to populate
96
+ # @return [void]
97
+ def process_attribute(nested_attribute, source_hash, target_hash) # rubocop:disable Metrics/MethodLength
98
+ source_name = nested_attribute.name
99
+ nested_value = source_hash.fetch(source_name, nil)
100
+
101
+ validator = AttributeValidator.new(nested_attribute)
102
+ validator.validate_schema!
103
+
104
+ transformed_value = if nested_attribute.nested?
105
+ nested_transformer = NestedTransformer.new(nested_attribute)
106
+ validator.validate_type!(nested_value) unless nested_value.nil?
107
+ validator.validate_required!(nested_value)
108
+ nested_transformer.transform(nested_value)
109
+ else
110
+ validator.validate_value!(nested_value)
111
+ validator.transform_value(nested_value)
112
+ end
113
+
114
+ target_name = validator.target_name
115
+ target_hash[target_name] = transformed_value
116
+ end
117
+ end
118
+
119
+ # Transforms array with nested attributes
120
+ class ArrayTransformer
121
+ SELF_SCOPE = :_self
122
+ private_constant :SELF_SCOPE
123
+
124
+ attr_reader :attribute
125
+
126
+ # Creates a new array transformer
127
+ #
128
+ # @param attribute [Attribute::Base] The array-type attribute
129
+ def initialize(attribute)
130
+ @attribute = attribute
131
+ end
132
+
133
+ # Transforms array by processing each element
134
+ # Handles both simple arrays (:_self) and complex arrays (objects)
135
+ #
136
+ # @param value [Array] The source array
137
+ # @return [Array] Transformed array
138
+ def transform(value)
139
+ value.each_with_index.map do |item, index|
140
+ if simple_array?
141
+ validate_simple_element(item, index)
142
+ item
143
+ else
144
+ transform_array_item(item, index)
145
+ end
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ # Checks if this is a simple array (primitive values)
152
+ #
153
+ # @return [Boolean] True if array contains primitive values with :_self scope
154
+ def simple_array?
155
+ attribute.collection_of_attributes.size == 1 &&
156
+ attribute.collection_of_attributes.first.name == SELF_SCOPE
157
+ end
158
+
159
+ # Validates a simple array element (primitive value)
160
+ #
161
+ # @param item [Object] Array element to validate
162
+ # @param index [Integer] Element index for error messages
163
+ # @raise [Treaty::Exceptions::Validation] If validation fails
164
+ # @return [void]
165
+ def validate_simple_element(item, index) # rubocop:disable Metrics/MethodLength
166
+ self_attr = attribute.collection_of_attributes.first
167
+ validator = AttributeValidator.new(self_attr)
168
+ validator.validate_schema!
169
+
170
+ begin
171
+ validator.validate_value!(item)
172
+ rescue Treaty::Exceptions::Validation => e
173
+ raise Treaty::Exceptions::Validation,
174
+ I18n.t("treaty.attributes.validators.nested.array.element_validation_error",
175
+ attribute: attribute.name,
176
+ index:,
177
+ errors: e.message)
178
+ end
179
+ end
180
+
181
+ # Transforms a complex array element (hash object)
182
+ #
183
+ # @param item [Hash] Array element to transform
184
+ # @param index [Integer] Element index for error messages
185
+ # @raise [Treaty::Exceptions::Validation] If item is not a Hash
186
+ # @return [Hash] Transformed hash
187
+ def transform_array_item(item, index) # rubocop:disable Metrics/MethodLength
188
+ unless item.is_a?(Hash)
189
+ raise Treaty::Exceptions::Validation,
190
+ I18n.t("treaty.attributes.validators.nested.array.element_type_error",
191
+ attribute: attribute.name,
192
+ index:,
193
+ actual: item.class)
194
+ end
195
+
196
+ transformed = {}
197
+
198
+ attribute.collection_of_attributes.each do |nested_attribute|
199
+ process_attribute(nested_attribute, item, transformed, index)
200
+ end
201
+
202
+ transformed
203
+ end
204
+
205
+ # Processes a single nested attribute in array element
206
+ # Validates, transforms, and adds to target hash
207
+ #
208
+ # @param nested_attribute [Attribute::Base] Attribute to process
209
+ # @param source_hash [Hash] Source data
210
+ # @param target_hash [Hash] Target hash to populate
211
+ # @param index [Integer] Array index for error messages
212
+ # @raise [Treaty::Exceptions::Validation] If validation fails
213
+ # @return [void]
214
+ def process_attribute(nested_attribute, source_hash, target_hash, index) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
215
+ source_name = nested_attribute.name
216
+ nested_value = source_hash.fetch(source_name, nil)
217
+
218
+ validator = AttributeValidator.new(nested_attribute)
219
+ validator.validate_schema!
220
+
221
+ begin
222
+ transformed_value = if nested_attribute.nested?
223
+ nested_transformer = NestedTransformer.new(nested_attribute)
224
+ validator.validate_type!(nested_value) unless nested_value.nil?
225
+ validator.validate_required!(nested_value)
226
+ nested_transformer.transform(nested_value)
227
+ else
228
+ validator.validate_value!(nested_value)
229
+ validator.transform_value(nested_value)
230
+ end
231
+ rescue Treaty::Exceptions::Validation => e
232
+ raise Treaty::Exceptions::Validation,
233
+ I18n.t("treaty.attributes.validators.nested.array.attribute_error",
234
+ attribute: attribute.name,
235
+ index:,
236
+ message: e.message)
237
+ end
238
+
239
+ target_name = validator.target_name
240
+ target_hash[target_name] = transformed_value
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,194 @@
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
+ raise Treaty::Exceptions::Validation,
97
+ I18n.t("treaty.attributes.validators.nested.orchestrator.collection_not_implemented")
98
+ end
99
+
100
+ # Validates all attributes in a scope (deprecated, not used)
101
+ #
102
+ # @param scope_factory [ScopeFactory] The scope to validate
103
+ # @return [void]
104
+ def validate_scope!(scope_factory)
105
+ scope_data = scope_data_for(scope_factory.name)
106
+
107
+ validators_for_scope(scope_factory).each do |attribute, validator|
108
+ value = scope_data.fetch(attribute.name, nil)
109
+ validator.validate_value!(value)
110
+ end
111
+ end
112
+
113
+ # Gets cached validators for scope or builds them
114
+ #
115
+ # @param scope_factory [ScopeFactory] The scope factory
116
+ # @return [Hash] Hash of attribute => validator
117
+ def validators_for_scope(scope_factory)
118
+ @validators_cache ||= {}
119
+ @validators_cache[scope_factory] ||= build_validators_for_scope(scope_factory)
120
+ end
121
+
122
+ # Builds validators for all attributes in a scope
123
+ #
124
+ # @param scope_factory [ScopeFactory] The scope factory
125
+ # @return [Hash] Hash of attribute => validator
126
+ def build_validators_for_scope(scope_factory)
127
+ scope_factory.collection_of_attributes.each_with_object({}) do |attribute, cache|
128
+ validator = AttributeValidator.new(attribute)
129
+ validator.validate_schema!
130
+ cache[attribute] = validator
131
+ end
132
+ end
133
+
134
+ # Extracts data for a specific scope
135
+ # Must be implemented in subclasses
136
+ #
137
+ # @param _name [Symbol] The scope name
138
+ # @raise [Treaty::Exceptions::Validation] If not implemented
139
+ # @return [Hash] Scope data
140
+ def scope_data_for(_name)
141
+ raise Treaty::Exceptions::Validation,
142
+ I18n.t("treaty.attributes.validators.nested.orchestrator.scope_data_not_implemented")
143
+ end
144
+
145
+ # Validates and transforms all attributes in a scope
146
+ # Handles both nested and regular attributes
147
+ #
148
+ # @param scope_factory [ScopeFactory] The scope to process
149
+ # @return [Hash] Transformed scope data
150
+ def validate_and_transform_scope!(scope_factory) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
151
+ scope_data = scope_data_for(scope_factory.name)
152
+
153
+ return scope_data if scope_factory.collection_of_attributes.empty?
154
+
155
+ transformed_scope_data = {}
156
+
157
+ validators_for_scope(scope_factory).each do |attribute, validator|
158
+ source_name = attribute.name
159
+ value = scope_data.fetch(source_name, nil)
160
+
161
+ if attribute.nested?
162
+ transformed_value = validate_and_transform_nested(attribute, value, validator)
163
+ else
164
+ validator.validate_value!(value)
165
+ transformed_value = validator.transform_value(value)
166
+ end
167
+
168
+ target_name = validator.target_name
169
+
170
+ transformed_scope_data[target_name] = transformed_value
171
+ end
172
+
173
+ transformed_scope_data
174
+ end
175
+
176
+ # Validates and transforms nested attribute (object/array)
177
+ # Delegates transformation to NestedTransformer
178
+ #
179
+ # @param attribute [Attribute::Base] The nested attribute
180
+ # @param value [Object] The value to validate and transform
181
+ # @param validator [AttributeValidator] The validator instance
182
+ # @return [Object] Transformed nested value
183
+ def validate_and_transform_nested(attribute, value, validator)
184
+ validator.validate_type!(value) unless value.nil?
185
+ validator.validate_required!(value)
186
+
187
+ transformer = NestedTransformer.new(attribute)
188
+ transformer.transform(value)
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ class Base
5
+ include Info::DSL
6
+ include Context::DSL
7
+ include Versions::DSL
8
+ end
9
+ end
@@ -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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Context
5
+ module DSL
6
+ def self.included(base)
7
+ base.extend(Callable)
8
+ base.include(Workspace)
9
+ end
10
+ end
11
+ end
12
+ 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
+ raise Treaty::Exceptions::ClassName,
28
+ I18n.t("treaty.controller.treaty_class_not_found", class_name: 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,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Exceptions
5
+ # Base exception class for all Treaty-specific exceptions
6
+ #
7
+ # ## Purpose
8
+ #
9
+ # Serves as the parent class for all custom exceptions in the Treaty gem.
10
+ # Allows catching all Treaty-related exceptions with a single rescue clause.
11
+ #
12
+ # ## Usage
13
+ #
14
+ # All Treaty exceptions inherit from this base class:
15
+ #
16
+ # ```ruby
17
+ # begin
18
+ # Treaty::Base.call!(controller: self, params: params)
19
+ # rescue Treaty::Exceptions::Base => e
20
+ # # Catches any Treaty-specific exception
21
+ # handle_treaty_error(e)
22
+ # end
23
+ # ```
24
+ #
25
+ # ## Integration
26
+ #
27
+ # Can be used in application controllers for centralized error handling:
28
+ #
29
+ # ```ruby
30
+ # rescue_from Treaty::Exceptions::Base, with: :handle_treaty_error
31
+ # ```
32
+ #
33
+ # ## Subclasses
34
+ #
35
+ # - Validation - Attribute validation errors
36
+ # - Execution - Service execution errors
37
+ # - Deprecated - API version deprecation
38
+ # - Strategy - Invalid strategy specification
39
+ # - ClassName - Treaty class not found
40
+ # - MethodName - Unknown method in DSL
41
+ # - NestedAttributes - Nesting depth exceeded
42
+ # - NotImplemented - Abstract method not implemented
43
+ # - Unexpected - General unexpected errors
44
+ class Base < StandardError
45
+ end
46
+ end
47
+ end