treaty 0.1.0 → 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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -16
  3. data/config/locales/en.yml +96 -0
  4. data/lib/treaty/attribute/base.rb +10 -8
  5. data/lib/treaty/attribute/builder/base.rb +3 -2
  6. data/lib/treaty/attribute/option/base.rb +1 -0
  7. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +3 -2
  8. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +6 -6
  9. data/lib/treaty/attribute/option/validators/required_validator.rb +2 -4
  10. data/lib/treaty/attribute/option/validators/type_validator.rb +19 -13
  11. data/lib/treaty/attribute/option_normalizer.rb +2 -1
  12. data/lib/treaty/attribute/option_orchestrator.rb +4 -3
  13. data/lib/treaty/attribute/validation/base.rb +2 -3
  14. data/lib/treaty/attribute/validation/nested_array_validator.rb +12 -7
  15. data/lib/treaty/attribute/validation/nested_transformer.rb +13 -7
  16. data/lib/treaty/attribute/validation/orchestrator/base.rb +2 -4
  17. data/lib/treaty/controller/dsl.rb +2 -2
  18. data/lib/treaty/exceptions/base.rb +39 -0
  19. data/lib/treaty/exceptions/class_name.rb +39 -0
  20. data/lib/treaty/exceptions/deprecated.rb +46 -0
  21. data/lib/treaty/exceptions/execution.rb +58 -0
  22. data/lib/treaty/exceptions/method_name.rb +47 -0
  23. data/lib/treaty/exceptions/nested_attributes.rb +57 -0
  24. data/lib/treaty/exceptions/not_implemented.rb +32 -0
  25. data/lib/treaty/exceptions/strategy.rb +55 -0
  26. data/lib/treaty/exceptions/unexpected.rb +62 -0
  27. data/lib/treaty/exceptions/validation.rb +89 -0
  28. data/lib/treaty/info/builder.rb +3 -3
  29. data/lib/treaty/request/attribute/attribute.rb +1 -1
  30. data/lib/treaty/response/attribute/attribute.rb +1 -1
  31. data/lib/treaty/strategy.rb +2 -2
  32. data/lib/treaty/version.rb +1 -1
  33. data/lib/treaty/versions/execution/request.rb +24 -28
  34. data/lib/treaty/versions/factory.rb +3 -4
  35. data/lib/treaty/versions/resolver.rb +3 -6
  36. metadata +21 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d99db2724f9dd4fd84a7f315d1acf070dcf70a7f3669dab9980334dc2c179076
4
- data.tar.gz: d02611a96da26130b8504818fabd9adbc10fa72b8984fc8330e7b63aec79c7ea
3
+ metadata.gz: 2697d0627eb77c0671a3b71a5aacefc47acd90339214badcc68ab30ad61b96b0
4
+ data.tar.gz: 32dfce4473730165c99715bbc83d2c822148fb6392bc47a098bcd7771131b748
5
5
  SHA512:
6
- metadata.gz: 492777e146c0042d9c78df8e9783a17d4aa705c629705425d7cd0db0ce2eaf5d8a2cf58b817c95fe1bc9025739044eb89959e91b04eb7e8dd205c6da9ab68251
7
- data.tar.gz: 6133706b11654af81a88b32489d88d78f86a66df79a80a072c29fde23e2ea6130e0bee4ffe401def62f52a80ce293954228dd746bc57cf01d38e17833179f350
6
+ metadata.gz: b6681d643573b27f56102619b50ef8f48e332b834e580f4d0dc164f0c5e298f221da2877db78d2c88853857f682b5f183b58c86e0b2d72c540a7462670d150d7
7
+ data.tar.gz: 70f69a8794ff010e115fdb80c81fc954ed1bd2881509cbcbf9d714957714c298775291e03f2268633d4fc8e293820a3a3cdd93cc2cb249b3beb51ffd64029037
data/README.md CHANGED
@@ -1,32 +1,120 @@
1
- # Treaty
1
+ <div align="center">
2
+ <h1>Treaty</h1>
3
+ <p>A Ruby library for defining and managing REST API contracts with versioning support.</p>
4
+ </div>
2
5
 
3
- > [!WARNING]
4
- > This project is currently under development.
6
+ <div align="center">
5
7
 
6
- ## Quick Start
8
+ [![Gem Version](https://img.shields.io/gem/v/treaty.svg)](https://rubygems.org/gems/treaty)
9
+ [![Release Date](https://img.shields.io/github/release-date/servactory/treaty)](https://github.com/servactory/servactory/releases)
10
+ [![Gem Downloads](https://img.shields.io/gem/dt/treaty.svg)](https://rubygems.org/gems/treaty)
11
+ ![Ruby Version](https://img.shields.io/badge/Ruby-3.2%2B-red)
12
+
13
+ </div>
14
+
15
+ ## 📚 Documentation
16
+
17
+ Explore comprehensive guides and documentation at [docs](./docs):
18
+
19
+ - [Getting Started](./docs/getting-started.md) - installation and configuration
20
+ - [Core Concepts](./docs/core-concepts.md) - understand fundamental concepts
21
+ - [API Reference](./docs/api-reference.md) - complete API documentation
22
+ - [Examples](./docs/examples.md) - practical real-world examples
23
+ - [Internationalization](./docs/internationalization.md) - I18n and multilingual support
24
+ - [Full Documentation Index](./docs/README.md) - all documentation topics
25
+
26
+ ## 💡 Why Treaty?
27
+
28
+ Treaty provides a complete solution for building versioned APIs in Ruby on Rails:
29
+
30
+ - **Type Safety** - Enforce strict type checking for request and response data
31
+ - **API Versioning** - Manage multiple concurrent API versions effortlessly
32
+ - **Built-in Validation** - Validate incoming requests and outgoing responses automatically
33
+ - **Data Transformation** - Transform data seamlessly between different API versions
34
+ - **Deprecation Management** - Mark versions as deprecated with flexible conditions
35
+ - **Internationalization** - Full I18n support for multilingual error messages
36
+ - **Well-documented** - Comprehensive guides and examples for every feature
37
+
38
+ ## 🚀 Quick Start
7
39
 
8
40
  ### Installation
9
41
 
42
+ Add Treaty to your Gemfile:
43
+
10
44
  ```ruby
11
45
  gem "treaty"
12
46
  ```
13
47
 
14
- ## Documentation
48
+ Run:
15
49
 
16
- Complete documentation is available in the [docs](./docs) directory:
50
+ ```bash
51
+ bundle install
52
+ ```
17
53
 
18
- - [Getting Started](./docs/getting-started.md) - installation and first steps
19
- - [Core Concepts](./docs/core-concepts.md) - fundamental concepts
20
- - [API Reference](./docs/api-reference.md) - complete API documentation
21
- - [Examples](./docs/examples.md) - practical examples
22
- - [Full Documentation Index](./docs/README.md) - all documentation topics
54
+ ### Define Treaty
55
+
56
+ Create your first API contract in `app/treaties/posts/create_treaty.rb`:
57
+
58
+ ```ruby
59
+ module Posts
60
+ class CreateTreaty < ApplicationTreaty
61
+ version 1, default: true do
62
+ strategy Treaty::Strategy::ADAPTER
63
+
64
+ request do
65
+ scope :post do
66
+ string :title, :required
67
+ string :content, :required
68
+ string :summary, :optional
69
+ end
70
+ end
71
+
72
+ response 201 do
73
+ scope :post do
74
+ string :id
75
+ string :title
76
+ string :content
77
+ string :summary
78
+ datetime :created_at
79
+ end
80
+ end
81
+
82
+ delegate_to Posts::CreateService
83
+ end
84
+ end
85
+ end
86
+ ```
87
+
88
+ ### Use in Controller
89
+
90
+ Define the treaty in your controller `app/controllers/posts_controller.rb`:
91
+
92
+ ```ruby
93
+ class PostsController < ApplicationController
94
+ # Treaty automatically:
95
+ # 1. Validates incoming parameters according to request definition
96
+ # 2. Calls Posts::CreateService with validated data
97
+ # 3. Validates service response according to response definition
98
+ # 4. Returns transformed data to client
99
+ treaty :create
100
+ end
101
+ ```
102
+
103
+ ## 🤝 Contributing
104
+
105
+ We welcome contributions! You can help by:
106
+
107
+ - Reporting bugs and suggesting features
108
+ - Writing code and improving documentation
109
+ - Reviewing pull requests
110
+ - Sharing your experience with Treaty
111
+
112
+ Please read our [Contributing Guide](./CONTRIBUTING.md) before submitting a pull request.
23
113
 
24
- ## Contributing
114
+ ## 🙏 Acknowledgments
25
115
 
26
- This project is intended to be a safe, welcoming space for collaboration.
27
- Contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
28
- We recommend reading the [contributing guide](./CONTRIBUTING.md) as well.
116
+ Thank you to all [contributors](https://github.com/servactory/treaty/graphs/contributors) who have helped make Treaty better!
29
117
 
30
- ## License
118
+ ## 📄 License
31
119
 
32
120
  Treaty is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,96 @@
1
+ en:
2
+ treaty:
3
+ # ============================================================================
4
+ # Attributes: Definition, validation, and processing
5
+ # ============================================================================
6
+ attributes:
7
+ # Attribute value validators
8
+ validators:
9
+ required:
10
+ blank: "Attribute '%{attribute}' is required but was not provided or is empty"
11
+
12
+ type:
13
+ unknown_type: "Unknown type '%{type}' for attribute '%{attribute}'. Allowed types: %{allowed}"
14
+ mismatch:
15
+ integer: "Attribute '%{attribute}' must be an Integer, got %{actual}"
16
+ string: "Attribute '%{attribute}' must be a String, got %{actual}"
17
+ object: "Attribute '%{attribute}' must be a Hash (object), got %{actual}"
18
+ array: "Attribute '%{attribute}' must be an Array, got %{actual}"
19
+ datetime: "Attribute '%{attribute}' must be a DateTime/Time/Date, got %{actual}"
20
+
21
+ inclusion:
22
+ invalid_schema: "Option 'inclusion' for attribute '%{attribute}' must have a non-empty array of allowed values"
23
+ not_included: "Attribute '%{attribute}' must be one of: %{allowed}. Got: '%{value}'"
24
+
25
+ # Nested structures validation
26
+ nested:
27
+ # Orchestrator errors
28
+ orchestrator:
29
+ collection_not_implemented: "Subclass must implement the collection_of_scopes method"
30
+ scope_data_not_implemented: "Subclass must implement the scope_data_for method"
31
+
32
+ # Array validation errors
33
+ array:
34
+ element_validation_error: "Error in array '%{attribute}' at index %{index}: Element must match one of the defined types. Errors: %{errors}"
35
+ element_type_error: "Error in array '%{attribute}' at index %{index}: Expected Hash but got %{actual}"
36
+ attribute_error: "Error in array '%{attribute}' at index %{index}: %{message}"
37
+
38
+ # Attribute options
39
+ options:
40
+ unknown: "Unknown options for attribute '%{attribute}': %{unknown}. Known options: %{known}"
41
+
42
+ # Attribute modifiers
43
+ modifiers:
44
+ as:
45
+ invalid_type: "Option 'as' for attribute '%{attribute}' must be a Symbol. Got: %{type}"
46
+
47
+ # Attribute builder DSL
48
+ builder:
49
+ not_implemented: "%{class} must implement #create_attribute"
50
+
51
+ # Attribute-level errors
52
+ errors:
53
+ nesting_level_exceeded: "Nesting level %{level} exceeds maximum allowed level of %{max_level}"
54
+ apply_defaults_not_implemented: "%{class} must implement #apply_defaults!"
55
+ process_nested_not_implemented: "%{class} must implement #process_nested_attributes"
56
+
57
+ # ============================================================================
58
+ # Versioning: API version management and resolution
59
+ # ============================================================================
60
+ versioning:
61
+ # Version resolver
62
+ resolver:
63
+ current_version_required: "Current version is required for validation"
64
+ version_not_found: "Version %{version} not found in treaty definition"
65
+ version_deprecated: "Version %{version} is deprecated and cannot be used"
66
+
67
+ # Version factory
68
+ factory:
69
+ invalid_default_option: "Default option for version must be true, false, or a Proc, got: %{type}"
70
+ unknown_method: "Unknown method: %{method}"
71
+
72
+ # Strategy validation
73
+ strategy:
74
+ unknown: "Unknown strategy: %{strategy}"
75
+
76
+ # ============================================================================
77
+ # Execution: Service and executor invocation
78
+ # ============================================================================
79
+ execution:
80
+ executor_missing: "Executor is not defined for version %{version}"
81
+ executor_empty: "Executor cannot be an empty string"
82
+ executor_not_found: "Executor class `%{class_name}` not found"
83
+ executor_invalid_type: "Invalid executor type: %{type}. Expected Proc, Class, String, or Symbol"
84
+ method_not_found: "Method '%{method}' not found in class '%{class_name}'"
85
+ proc_error: "%{message}"
86
+ servactory_input_error: "%{message}"
87
+ servactory_internal_error: "%{message}"
88
+ servactory_output_error: "%{message}"
89
+ servactory_failure_error: "%{message}"
90
+ regular_service_error: "%{message}"
91
+
92
+ # ============================================================================
93
+ # Controller DSL: Rails controller integration
94
+ # ============================================================================
95
+ controller:
96
+ treaty_class_not_found: "%{class_name}"
@@ -121,10 +121,10 @@ module Treaty
121
121
  def validate_nesting_level!
122
122
  return unless @nesting_level > Treaty::Engine.config.treaty.attribute_nesting_level
123
123
 
124
- # TODO: It is necessary to implement a translation system (I18n).
125
124
  raise Treaty::Exceptions::NestedAttributes,
126
- "Nesting level #{@nesting_level} exceeds maximum allowed level of " \
127
- "#{Treaty::Engine.config.treaty.attribute_nesting_level}"
125
+ I18n.t("treaty.attributes.errors.nesting_level_exceeded",
126
+ level: @nesting_level,
127
+ max_level: Treaty::Engine.config.treaty.attribute_nesting_level)
128
128
  end
129
129
 
130
130
  # Extracts helper symbols from arguments
@@ -150,22 +150,24 @@ module Treaty
150
150
  # Applies default values for options based on context (request/response)
151
151
  # Must be implemented in subclasses
152
152
  #
153
- # @raise [NotImplementedError] If subclass doesn't implement
153
+ # @raise [Treaty::Exceptions::NotImplemented] If subclass doesn't implement
154
154
  # @return [void]
155
155
  def apply_defaults!
156
156
  # Must be implemented in subclasses
157
- raise NotImplementedError, "#{self.class} must implement #apply_defaults!"
157
+ raise Treaty::Exceptions::NotImplemented,
158
+ I18n.t("treaty.attributes.errors.apply_defaults_not_implemented", class: self.class)
158
159
  end
159
160
 
160
161
  # Processes nested attributes block for object/array types
161
162
  # Must be implemented in subclasses
162
163
  #
163
164
  # @param block [Proc] Block containing nested attribute definitions
164
- # @raise [NotImplementedError] If subclass doesn't implement
165
+ # @raise [Treaty::Exceptions::NotImplemented] If subclass doesn't implement
165
166
  # @return [void]
166
- def process_nested_attributes(&block)
167
+ def process_nested_attributes
167
168
  # Must be implemented in subclasses
168
- raise NotImplementedError, "#{self.class} must implement #process_nested_attributes"
169
+ raise Treaty::Exceptions::NotImplemented,
170
+ I18n.t("treaty.attributes.errors.process_nested_not_implemented", class: self.class)
169
171
  end
170
172
  end
171
173
  end
@@ -130,11 +130,12 @@ module Treaty
130
130
 
131
131
  # Creates an attribute instance (must be implemented in subclasses)
132
132
  #
133
- # @raise [NotImplementedError] If subclass doesn't implement
133
+ # @raise [Treaty::Exceptions::NotImplemented] If subclass doesn't implement
134
134
  # @return [Attribute::Base] Created attribute instance
135
135
  def create_attribute(*)
136
136
  # Must be implemented in subclasses
137
- raise NotImplementedError, "#{self.class} must implement #create_attribute"
137
+ raise Treaty::Exceptions::NotImplemented,
138
+ I18n.t("treaty.attributes.builder.not_implemented", class: self.class)
138
139
  end
139
140
  end
140
141
  end
@@ -132,6 +132,7 @@ module Treaty
132
132
  end
133
133
 
134
134
  # Gets custom error message from advanced mode schema
135
+ # Returns nil if no custom message, which triggers I18n default message
135
136
  #
136
137
  # @return [String, nil] Custom error message or nil for default message
137
138
  def custom_message
@@ -53,9 +53,10 @@ module Treaty
53
53
 
54
54
  return if target.is_a?(Symbol)
55
55
 
56
- # TODO: It is necessary to implement a translation system (I18n).
57
56
  raise Treaty::Exceptions::Validation,
58
- "Option 'as' for attribute '#{@attribute_name}' must be a Symbol. Got: #{target.class}"
57
+ I18n.t("treaty.attributes.modifiers.as.invalid_type",
58
+ attribute: @attribute_name,
59
+ type: target.class)
59
60
  end
60
61
 
61
62
  # Indicates that AsModifier transforms attribute names
@@ -28,9 +28,8 @@ module Treaty
28
28
 
29
29
  return if allowed_values.is_a?(Array) && !allowed_values.empty?
30
30
 
31
- # TODO: It is necessary to implement a translation system (I18n).
32
31
  raise Treaty::Exceptions::Validation,
33
- "Option 'inclusion' for attribute '#{@attribute_name}' must have a non-empty array of allowed values"
32
+ I18n.t("treaty.attributes.validators.inclusion.invalid_schema", attribute: @attribute_name)
34
33
  end
35
34
 
36
35
  # Validates that value is included in allowed set
@@ -48,7 +47,6 @@ module Treaty
48
47
 
49
48
  message = custom_message || default_message(allowed_values, value)
50
49
 
51
- # TODO: It is necessary to implement a translation system (I18n).
52
50
  raise Treaty::Exceptions::Validation, message
53
51
  end
54
52
 
@@ -64,14 +62,16 @@ module Treaty
64
62
 
65
63
  private
66
64
 
67
- # Generates default error message with allowed values
65
+ # Generates default error message with allowed values using I18n
68
66
  #
69
67
  # @param allowed_values [Array] Array of allowed values
70
68
  # @param value [Object] The actual value that failed validation
71
69
  # @return [String] Default error message
72
70
  def default_message(allowed_values, value)
73
- # TODO: It is necessary to implement a translation system (I18n).
74
- "Attribute '#{@attribute_name}' must be one of: #{allowed_values.join(', ')}. Got: '#{value}'"
71
+ I18n.t("treaty.attributes.validators.inclusion.not_included",
72
+ attribute: @attribute_name,
73
+ allowed: allowed_values.join(", "),
74
+ value:)
75
75
  end
76
76
  end
77
77
  end
@@ -53,7 +53,6 @@ module Treaty
53
53
 
54
54
  message = custom_message || default_message
55
55
 
56
- # TODO: It is necessary to implement a translation system (I18n).
57
56
  raise Treaty::Exceptions::Validation, message
58
57
  end
59
58
 
@@ -80,12 +79,11 @@ module Treaty
80
79
  true
81
80
  end
82
81
 
83
- # Generates default error message
82
+ # Generates default error message using I18n
84
83
  #
85
84
  # @return [String] Default error message
86
85
  def default_message
87
- # TODO: It is necessary to implement a translation system (I18n).
88
- "Attribute '#{@attribute_name}' is required but was not provided or is empty"
86
+ I18n.t("treaty.attributes.validators.required.blank", attribute: @attribute_name)
89
87
  end
90
88
  end
91
89
  end
@@ -50,10 +50,11 @@ module Treaty
50
50
  def validate_schema!
51
51
  return if ALLOWED_TYPES.include?(@attribute_type)
52
52
 
53
- # TODO: It is necessary to implement a translation system (I18n).
54
53
  raise Treaty::Exceptions::Validation,
55
- "Unknown type '#{@attribute_type}' for attribute '#{@attribute_name}'. " \
56
- "Allowed types: #{ALLOWED_TYPES.join(', ')}"
54
+ I18n.t("treaty.attributes.validators.type.unknown_type",
55
+ type: @attribute_type,
56
+ attribute: @attribute_name,
57
+ allowed: ALLOWED_TYPES.join(", "))
57
58
  end
58
59
 
59
60
  # Validates that the value matches the declared type
@@ -89,9 +90,10 @@ module Treaty
89
90
  def validate_integer!(value)
90
91
  return if value.is_a?(Integer)
91
92
 
92
- # TODO: It is necessary to implement a translation system (I18n).
93
93
  raise Treaty::Exceptions::Validation,
94
- "Attribute '#{@attribute_name}' must be an Integer, got #{value.class}"
94
+ I18n.t("treaty.attributes.validators.type.mismatch.integer",
95
+ attribute: @attribute_name,
96
+ actual: value.class)
95
97
  end
96
98
 
97
99
  # Validates that value is a String
@@ -102,9 +104,10 @@ module Treaty
102
104
  def validate_string!(value)
103
105
  return if value.is_a?(String)
104
106
 
105
- # TODO: It is necessary to implement a translation system (I18n).
106
107
  raise Treaty::Exceptions::Validation,
107
- "Attribute '#{@attribute_name}' must be a String, got #{value.class}"
108
+ I18n.t("treaty.attributes.validators.type.mismatch.string",
109
+ attribute: @attribute_name,
110
+ actual: value.class)
108
111
  end
109
112
 
110
113
  # Validates that value is a Hash (object type)
@@ -115,9 +118,10 @@ module Treaty
115
118
  def validate_object!(value)
116
119
  return if value.is_a?(Hash)
117
120
 
118
- # TODO: It is necessary to implement a translation system (I18n).
119
121
  raise Treaty::Exceptions::Validation,
120
- "Attribute '#{@attribute_name}' must be a Hash (object), got #{value.class}"
122
+ I18n.t("treaty.attributes.validators.type.mismatch.object",
123
+ attribute: @attribute_name,
124
+ actual: value.class)
121
125
  end
122
126
 
123
127
  # Validates that value is an Array
@@ -128,9 +132,10 @@ module Treaty
128
132
  def validate_array!(value)
129
133
  return if value.is_a?(Array)
130
134
 
131
- # TODO: It is necessary to implement a translation system (I18n).
132
135
  raise Treaty::Exceptions::Validation,
133
- "Attribute '#{@attribute_name}' must be an Array, got #{value.class}"
136
+ I18n.t("treaty.attributes.validators.type.mismatch.array",
137
+ attribute: @attribute_name,
138
+ actual: value.class)
134
139
  end
135
140
 
136
141
  # Validates that value is a DateTime, Time, or Date
@@ -142,9 +147,10 @@ module Treaty
142
147
  # TODO: It is better to divide it into different methods for each class.
143
148
  return if value.is_a?(DateTime) || value.is_a?(Time) || value.is_a?(Date)
144
149
 
145
- # TODO: It is necessary to implement a translation system (I18n).
146
150
  raise Treaty::Exceptions::Validation,
147
- "Attribute '#{@attribute_name}' must be a DateTime/Time/Date, got #{value.class}"
151
+ I18n.t("treaty.attributes.validators.type.mismatch.datetime",
152
+ attribute: @attribute_name,
153
+ actual: value.class)
148
154
  end
149
155
  end
150
156
  end
@@ -128,10 +128,11 @@ module Treaty
128
128
  def normalize_value(value, value_key)
129
129
  if advanced_mode?(value, value_key)
130
130
  # Already in advanced mode, ensure it has both keys.
131
+ # message: nil means use I18n default message from validators
131
132
  { value_key => value.fetch(value_key), message: value.fetch(:message, nil) }
132
133
  else
133
134
  # Simple mode, convert to advanced.
134
- # TODO: It is necessary to implement a translation system (I18n).
135
+ # message: nil means use I18n default message from validators
135
136
  { value_key => value, message: nil }
136
137
  end
137
138
  end
@@ -176,10 +176,11 @@ module Treaty
176
176
 
177
177
  return if unknown_options.empty?
178
178
 
179
- # TODO: It is necessary to implement a translation system (I18n).
180
179
  raise Treaty::Exceptions::Validation,
181
- "Unknown options for attribute '#{@attribute.name}': #{unknown_options.join(', ')}. " \
182
- "Known options: #{Option::Registry.all_options.join(', ')}"
180
+ I18n.t("treaty.attributes.options.unknown",
181
+ attribute: @attribute.name,
182
+ unknown: unknown_options.join(", "),
183
+ known: Option::Registry.all_options.join(", "))
183
184
  end
184
185
  end
185
186
  end
@@ -71,12 +71,11 @@ module Treaty
71
71
  # Performs validation and transformation
72
72
  # Must be implemented in subclasses
73
73
  #
74
- # @raise [NotImplementedError] If subclass doesn't implement
74
+ # @raise [Treaty::Exceptions::NotImplemented] If subclass doesn't implement
75
75
  # @return [Hash] Validated and transformed data
76
76
  def validate!
77
- # TODO: It is necessary to implement a translation system (I18n).
78
77
  raise Treaty::Exceptions::Validation,
79
- "Subclass must implement the validate! method"
78
+ I18n.t("treaty.attributes.validators.nested.orchestrator.collection_not_implemented")
80
79
  end
81
80
 
82
81
  private
@@ -114,10 +114,11 @@ module Treaty
114
114
 
115
115
  return if validated
116
116
 
117
- # TODO: It is necessary to implement a translation system (I18n).
118
117
  raise Treaty::Exceptions::Validation,
119
- "Error in array '#{@attribute.name}' at index #{index}: " \
120
- "Item must match one of the defined types. Errors: #{errors.join('; ')}"
118
+ I18n.t("treaty.attributes.validators.nested.array.element_validation_error",
119
+ attribute: @attribute.name,
120
+ index:,
121
+ errors: errors.join("; "))
121
122
  end
122
123
 
123
124
  # Validates array item for complex arrays (with regular attributes)
@@ -130,18 +131,22 @@ module Treaty
130
131
  # @return [void]
131
132
  def validate_regular_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
132
133
  unless array_item.is_a?(Hash)
133
- # TODO: It is necessary to implement a translation system (I18n).
134
134
  raise Treaty::Exceptions::Validation,
135
- "Error in array '#{@attribute.name}' at index #{index}: Expected Hash but got #{array_item.class}"
135
+ I18n.t("treaty.attributes.validators.nested.array.element_type_error",
136
+ attribute: @attribute.name,
137
+ index:,
138
+ actual: array_item.class)
136
139
  end
137
140
 
138
141
  regular_validators.each do |nested_attribute, validator|
139
142
  nested_value = array_item.fetch(nested_attribute.name, nil)
140
143
  validator.validate_value!(nested_value)
141
144
  rescue Treaty::Exceptions::Validation => e
142
- # TODO: It is necessary to implement a translation system (I18n).
143
145
  raise Treaty::Exceptions::Validation,
144
- "Error in array '#{@attribute.name}' at index #{index}: #{e.message}"
146
+ I18n.t("treaty.attributes.validators.nested.array.attribute_error",
147
+ attribute: @attribute.name,
148
+ index:,
149
+ message: e.message)
145
150
  end
146
151
  end
147
152
 
@@ -171,9 +171,10 @@ module Treaty
171
171
  validator.validate_value!(item)
172
172
  rescue Treaty::Exceptions::Validation => e
173
173
  raise Treaty::Exceptions::Validation,
174
- "Error in array '#{attribute.name}' at index #{index}: " \
175
- "Element must match one of the defined types. " \
176
- "Errors: #{e.message}"
174
+ I18n.t("treaty.attributes.validators.nested.array.element_validation_error",
175
+ attribute: attribute.name,
176
+ index:,
177
+ errors: e.message)
177
178
  end
178
179
  end
179
180
 
@@ -183,11 +184,13 @@ module Treaty
183
184
  # @param index [Integer] Element index for error messages
184
185
  # @raise [Treaty::Exceptions::Validation] If item is not a Hash
185
186
  # @return [Hash] Transformed hash
186
- def transform_array_item(item, index)
187
+ def transform_array_item(item, index) # rubocop:disable Metrics/MethodLength
187
188
  unless item.is_a?(Hash)
188
189
  raise Treaty::Exceptions::Validation,
189
- "Error in array '#{attribute.name}' at index #{index}: " \
190
- "Expected Hash but got #{item.class}"
190
+ I18n.t("treaty.attributes.validators.nested.array.element_type_error",
191
+ attribute: attribute.name,
192
+ index:,
193
+ actual: item.class)
191
194
  end
192
195
 
193
196
  transformed = {}
@@ -227,7 +230,10 @@ module Treaty
227
230
  end
228
231
  rescue Treaty::Exceptions::Validation => e
229
232
  raise Treaty::Exceptions::Validation,
230
- "Error in array '#{attribute.name}' at index #{index}: #{e.message}"
233
+ I18n.t("treaty.attributes.validators.nested.array.attribute_error",
234
+ attribute: attribute.name,
235
+ index:,
236
+ message: e.message)
231
237
  end
232
238
 
233
239
  target_name = validator.target_name
@@ -93,9 +93,8 @@ module Treaty
93
93
  # @raise [Treaty::Exceptions::Validation] If not implemented
94
94
  # @return [Array<ScopeFactory>] Collection of scope factories
95
95
  def collection_of_scopes
96
- # TODO: It is necessary to implement a translation system (I18n).
97
96
  raise Treaty::Exceptions::Validation,
98
- "Subclass must implement the collection_of_scopes method"
97
+ I18n.t("treaty.attributes.validators.nested.orchestrator.collection_not_implemented")
99
98
  end
100
99
 
101
100
  # Validates all attributes in a scope (deprecated, not used)
@@ -139,9 +138,8 @@ module Treaty
139
138
  # @raise [Treaty::Exceptions::Validation] If not implemented
140
139
  # @return [Hash] Scope data
141
140
  def scope_data_for(_name)
142
- # TODO: It is necessary to implement a translation system (I18n).
143
141
  raise Treaty::Exceptions::Validation,
144
- "Subclass must implement the scope_data_for method"
142
+ I18n.t("treaty.attributes.validators.nested.orchestrator.scope_data_not_implemented")
145
143
  end
146
144
 
147
145
  # Validates and transforms all attributes in a scope
@@ -24,8 +24,8 @@ module Treaty
24
24
  def treaty_class
25
25
  treaty_class_name.constantize
26
26
  rescue NameError
27
- # TODO: It is necessary to implement a translation system (I18n).
28
- raise Treaty::Exceptions::ClassName, treaty_class_name
27
+ raise Treaty::Exceptions::ClassName,
28
+ I18n.t("treaty.controller.treaty_class_not_found", class_name: treaty_class_name)
29
29
  end
30
30
 
31
31
  def treaty_class_name