treaty 0.11.0 → 0.13.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ec3d8b8448c1cb6634404040ed78864beb81a317b705a14d6f3684caaaf5952
4
- data.tar.gz: a21afccd9a25da7531ff561ed4baae2cc0011ed8c9c5b09b7ceb1370b79ba353
3
+ metadata.gz: 600a422c9ddcdff83b3ec4cde1c8124adb8ae52e1c1d88744d8474bc4afa8c68
4
+ data.tar.gz: dd425b19d451b34f46b07747586f35ba7df4dcac9c554a481ac5dd27e1fe2141
5
5
  SHA512:
6
- metadata.gz: 0a8281e2551007b9c6b514b3d8d664d484b27e757733dd6a94d191bfdf8cce1d50a5b4f3bdee8fb8e4cadb51b1b90802697f0cd06a929c8cf39482f6aef6c5c3
7
- data.tar.gz: 1855561545ee10307c8094377ed28a132a6a9ac8f9347801f9f1d80e7f6adbb6fddc812b34e2b7960336d8558d84e9d48691aeea525c6357962b66e2a0a56e7e
6
+ metadata.gz: 1739edd5d1c38fe70fc6ab892b06ec15df3a665724df68c95f351675dae4e8c6a00368d65489bbfde0900a67841b9aad1e7eb57f81b948248ae902755f485519
7
+ data.tar.gz: 627d321a706c846e3249afb9e97f4ceb48573af52fcf962459e38316ed76ed7d979c4fd0fcffa8146cff5e7cfeab36b4cf391f9bfb3d7c82ca8de3cd6c5ea439
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  </div>
14
14
 
15
15
  > [!WARNING]
16
- > **Development Status**: Treaty is currently under active development in the 0.x version series. Breaking changes may occur between minor versions (0.x) as we refine the API and add new features. The library will stabilize with the 1.0 release. We recommend pinning to specific patch versions in your Gemfile (e.g., `gem "treaty", "~> 0.9.0"`) until the 1.0 release.
16
+ > **Development Status**: Treaty is currently under active development in the 0.x version series. Breaking changes may occur between minor versions (0.x) as we refine the API and add new features. The library will stabilize with the 1.0 release. We recommend pinning to specific patch versions in your Gemfile (e.g., `gem "treaty", "~> 0.12.0"`) until the 1.0 release.
17
17
 
18
18
  ## 📚 Documentation
19
19
 
@@ -36,6 +36,7 @@ Treaty provides a complete solution for building versioned APIs in Ruby on Rails
36
36
  - **Entity Classes (DTOs)** - Define reusable data transfer objects for better code organization
37
37
  - **Built-in Validation** - Validate incoming requests and outgoing responses automatically
38
38
  - **Data Transformation** - Transform data seamlessly between different API versions
39
+ - **Inventory System** - Pass controller-specific data to services efficiently
39
40
  - **Deprecation Management** - Mark versions as deprecated with flexible conditions
40
41
  - **Internationalization** - Full I18n support for multilingual error messages
41
42
  - **Well-documented** - Comprehensive guides and examples for every feature
@@ -64,8 +65,6 @@ Create your first API contract in `app/treaties/posts/create_treaty.rb`:
64
65
  module Posts
65
66
  class CreateTreaty < ApplicationTreaty
66
67
  version 1, default: true do
67
- strategy Treaty::Strategy::ADAPTER
68
-
69
68
  request do
70
69
  object :post do
71
70
  string :title
@@ -102,6 +101,18 @@ class PostsController < ApplicationController
102
101
  # 3. Validates service response according to response definition
103
102
  # 4. Returns transformed data to client
104
103
  treaty :create
104
+
105
+ # Optional: Provide additional data from controller to service
106
+ treaty :index do
107
+ provide :current_user
108
+ provide :posts, from: :load_posts
109
+ end
110
+
111
+ private
112
+
113
+ def load_posts
114
+ Post.published.limit(10)
115
+ end
105
116
  end
106
117
  ```
107
118
 
@@ -49,6 +49,17 @@ en:
49
49
  as:
50
50
  invalid_type: "Option 'as' for attribute '%{attribute}' must be a Symbol. Got: %{type}"
51
51
 
52
+ transform:
53
+ invalid_type: "Option 'transform' for attribute '%{attribute}' must be a Proc or Lambda. Got: %{type}"
54
+ execution_error: "Transform failed for attribute '%{attribute}': %{error}"
55
+
56
+ cast:
57
+ invalid_type: "Option 'cast' for attribute '%{attribute}' must be a Symbol. Got: %{type}"
58
+ source_not_supported: "Option 'cast' for attribute '%{attribute}' cannot be used with type '%{source_type}'. Casting is only supported for: %{allowed}"
59
+ target_not_supported: "Option 'cast' for attribute '%{attribute}' cannot cast to '%{target_type}'. Supported target types: %{allowed}"
60
+ conversion_not_supported: "Option 'cast' for attribute '%{attribute}' does not support conversion from '%{from}' to '%{to}'"
61
+ conversion_error: "Cast failed for attribute '%{attribute}' from '%{from}' to '%{to}'. Value: '%{value}'. Error: %{error}"
62
+
52
63
  # Attribute builder DSL
53
64
  builder:
54
65
  not_implemented: "%{class} must implement #create_attribute"
@@ -91,14 +102,10 @@ en:
91
102
  # Version factory
92
103
  factory:
93
104
  invalid_default_option: "Default option for version must be true, false, or a Proc, got: %{type}"
94
- unknown_method: "Unknown method '%{method}' in version definition. Available methods: summary, strategy, deprecated, request, response, delegate_to"
105
+ unknown_method: "Unknown method '%{method}' in version definition. Available methods: summary, deprecated, request, response, delegate_to"
95
106
  default_deprecated_conflict: "Version %{version} cannot be both default and deprecated. A default version must be active and usable. Either remove 'default: true' or remove the 'deprecated' declaration."
96
107
  multiple_defaults: "Cannot have multiple versions marked as default. Only one version can be the default. Please review your treaty definition and ensure only one version has 'default: true'."
97
108
 
98
- # Strategy validation
99
- strategy:
100
- unknown: "Unknown strategy: %{strategy}"
101
-
102
109
  # ============================================================================
103
110
  # Execution: Service and executor invocation
104
111
  # ============================================================================
@@ -120,3 +127,23 @@ en:
120
127
  # ============================================================================
121
128
  controller:
122
129
  treaty_class_not_found: "%{class_name}"
130
+
131
+ # ============================================================================
132
+ # Inventory: Controller data provisioning system
133
+ # ============================================================================
134
+ inventory:
135
+ # Factory DSL errors
136
+ unknown_method: "Unknown method '%{method}' in treaty block for action '%{action}'. Only 'provide' method is supported. Use: provide :name, from: :source OR provide :name"
137
+ name_must_be_symbol: "Inventory name must be a Symbol, got %{name}. Use: provide :name, from: :source OR provide :name"
138
+
139
+ # Inventory validation errors
140
+ invalid_name: "Inventory name must be a non-empty Symbol, got %{name}"
141
+ source_required: "Inventory source cannot be nil. Provide a Symbol (method name), Proc/Lambda, or direct value"
142
+ evaluation_error: "Failed to evaluate inventory item '%{name}': %{error}"
143
+
144
+ # ============================================================================
145
+ # Executor: Inventory executor wrapper
146
+ # ============================================================================
147
+ executor:
148
+ inventory:
149
+ item_not_found: "Inventory item '%{name}' not found. Available items: %{available}"
@@ -144,14 +144,14 @@ module Treaty
144
144
  # Resolves custom message with lambda support
145
145
  # If message is a lambda, calls it with provided named arguments
146
146
  #
147
- # @param context [Hash] Named arguments to pass to lambda
147
+ # @param attributes [Hash] Named arguments to pass to lambda
148
148
  # @return [String, nil] Resolved message string or nil
149
- def resolve_custom_message(**context)
149
+ def resolve_custom_message(**attributes)
150
150
  message = custom_message
151
151
  return nil if message.nil?
152
152
 
153
153
  if message.respond_to?(:call)
154
- message.call(**context)
154
+ message.call(**attributes)
155
155
  else
156
156
  message
157
157
  end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Option
6
+ module Modifiers
7
+ # Converts attribute values between different types automatically.
8
+ #
9
+ # ## Usage Examples
10
+ #
11
+ # Simple mode:
12
+ # string :created_at, cast: :datetime
13
+ # datetime :timestamp, cast: :string
14
+ # integer :active, cast: :boolean
15
+ #
16
+ # Advanced mode with custom error message:
17
+ # string :created_at, cast: {
18
+ # to: :datetime,
19
+ # message: "Invalid date format"
20
+ # }
21
+ #
22
+ # ## Use Cases
23
+ #
24
+ # 1. **Request type conversion**:
25
+ # ```ruby
26
+ # request do
27
+ # string :created_at, cast: :datetime
28
+ # end
29
+ # # Input: { created_at: "2024-01-15T10:30:00Z" }
30
+ # # Service receives: { created_at: DateTime object }
31
+ # ```
32
+ #
33
+ # 2. **Response type conversion**:
34
+ # ```ruby
35
+ # response 200 do
36
+ # datetime :created_at, cast: :string
37
+ # end
38
+ # # Service returns: { created_at: DateTime object }
39
+ # # Output: { created_at: "2024-01-15T10:30:00Z" }
40
+ # ```
41
+ #
42
+ # 3. **Unix timestamp conversion**:
43
+ # ```ruby
44
+ # integer :timestamp, cast: :datetime
45
+ # datetime :created_at, cast: :integer
46
+ # ```
47
+ #
48
+ # ## Supported Conversions
49
+ #
50
+ # ### From Integer
51
+ # - integer -> string: Converts to string representation
52
+ # - integer -> boolean: 0 = false, non-zero = true
53
+ # - integer -> datetime: Treats as Unix timestamp
54
+ #
55
+ # ### From String
56
+ # - string -> integer: Parses integer from string
57
+ # - string -> boolean: Parses truthy/falsy strings (true/false, yes/no, 1/0, on/off)
58
+ # - string -> datetime: Parses datetime string (ISO8601, RFC3339, etc.)
59
+ #
60
+ # ### From Boolean
61
+ # - boolean -> string: Converts to "true" or "false"
62
+ # - boolean -> integer: true = 1, false = 0
63
+ #
64
+ # ### From DateTime
65
+ # - datetime -> string: Converts to ISO8601 format
66
+ # - datetime -> integer: Converts to Unix timestamp
67
+ #
68
+ # ## Important Notes
69
+ #
70
+ # - Cast option only works with scalar types (integer, string, boolean, datetime)
71
+ # - Array and Object types are not supported for casting
72
+ # - Casting to the same type is allowed (no-op)
73
+ # - Nil values are not transformed (handled by RequiredValidator)
74
+ # - All conversion errors are caught and re-raised as Validation errors
75
+ #
76
+ # ## Error Handling
77
+ #
78
+ # If conversion fails (e.g., invalid date string, non-numeric string to integer),
79
+ # the error is caught and converted to a Treaty::Exceptions::Validation error.
80
+ #
81
+ # ## Advanced Mode
82
+ #
83
+ # Schema format: `{ to: :target_type, message: "Custom error" }`
84
+ # Note: Uses `:to` key instead of the default `:is` key.
85
+ class CastModifier < Treaty::Attribute::Option::Base
86
+ # Types that support casting (scalar types only)
87
+ ALLOWED_CAST_TYPES = %i[integer string boolean datetime].freeze
88
+
89
+ # Validates that cast option is correctly configured
90
+ #
91
+ # @raise [Treaty::Exceptions::Validation] If cast configuration is invalid
92
+ # @return [void]
93
+ def validate_schema! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
94
+ # If option_schema is nil, cast is not used for this attribute
95
+ return if @option_schema.nil?
96
+
97
+ target_type = option_value
98
+
99
+ # Validate that target type is a Symbol
100
+ unless target_type.is_a?(Symbol)
101
+ raise Treaty::Exceptions::Validation,
102
+ I18n.t(
103
+ "treaty.attributes.modifiers.cast.invalid_type",
104
+ attribute: @attribute_name,
105
+ type: target_type.class
106
+ )
107
+ end
108
+
109
+ # Validate that source type supports casting
110
+ unless ALLOWED_CAST_TYPES.include?(@attribute_type)
111
+ raise Treaty::Exceptions::Validation,
112
+ I18n.t(
113
+ "treaty.attributes.modifiers.cast.source_not_supported",
114
+ attribute: @attribute_name,
115
+ source_type: @attribute_type,
116
+ allowed: ALLOWED_CAST_TYPES.join(", ")
117
+ )
118
+ end
119
+
120
+ # Validate that target type is allowed
121
+ unless ALLOWED_CAST_TYPES.include?(target_type)
122
+ raise Treaty::Exceptions::Validation,
123
+ I18n.t(
124
+ "treaty.attributes.modifiers.cast.target_not_supported",
125
+ attribute: @attribute_name,
126
+ target_type:,
127
+ allowed: ALLOWED_CAST_TYPES.join(", ")
128
+ )
129
+ end
130
+
131
+ # Validate that conversion from source to target is supported
132
+ return if conversion_supported?(@attribute_type, target_type)
133
+
134
+ raise Treaty::Exceptions::Validation,
135
+ I18n.t(
136
+ "treaty.attributes.modifiers.cast.conversion_not_supported",
137
+ attribute: @attribute_name,
138
+ from: @attribute_type,
139
+ to: target_type
140
+ )
141
+ end
142
+
143
+ # Applies type conversion to the value
144
+ # Skips conversion for nil values (handled by RequiredValidator)
145
+ #
146
+ # @param value [Object] The current value
147
+ # @return [Object] Converted value
148
+ def transform_value(value) # rubocop:disable Metrics/MethodLength
149
+ return value if value.nil? # Cast doesn't modify nil, required validator handles it.
150
+
151
+ target_type = option_value
152
+ conversion_lambda = conversion_matrix.dig(@attribute_type, target_type)
153
+
154
+ # Call conversion lambda
155
+ conversion_lambda.call(value:)
156
+ rescue StandardError => e
157
+ attributes = {
158
+ attribute: @attribute_name,
159
+ from: @attribute_type,
160
+ to: target_type,
161
+ value:,
162
+ error: e.message
163
+ }
164
+
165
+ # Catch all exceptions from conversion execution
166
+ error_message = resolve_custom_message(**attributes) || I18n.t(
167
+ "treaty.attributes.modifiers.cast.conversion_error",
168
+ **attributes
169
+ )
170
+
171
+ raise Treaty::Exceptions::Validation, error_message
172
+ end
173
+
174
+ protected
175
+
176
+ # Override value_key to use :to instead of :is
177
+ # This makes advanced mode syntax: cast: { to: :datetime }
178
+ #
179
+ # @return [Symbol] The key :to
180
+ def value_key
181
+ :to
182
+ end
183
+
184
+ private
185
+
186
+ # Checks if conversion from source type to target type is supported
187
+ #
188
+ # @param from_type [Symbol] Source type
189
+ # @param to_type [Symbol] Target type
190
+ # @return [Boolean] True if conversion is supported
191
+ def conversion_supported?(from_type, to_type)
192
+ conversion_matrix.dig(from_type, to_type).present?
193
+ end
194
+
195
+ # Matrix of all supported type conversions
196
+ # Maps from_type => to_type => conversion_lambda
197
+ #
198
+ # @return [Hash] Conversion matrix
199
+ def conversion_matrix # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
200
+ @conversion_matrix ||= {
201
+ integer: {
202
+ integer: ->(value:) { value }, # No-op for same type
203
+ string: ->(value:) { value.to_s },
204
+ boolean: ->(value:) { value != 0 },
205
+ datetime: ->(value:) { Time.at(value) }
206
+ },
207
+ string: {
208
+ string: ->(value:) { value }, # No-op for same type
209
+ integer: ->(value:) { Integer(value) },
210
+ boolean: ->(value:) { parse_boolean(value) },
211
+ datetime: ->(value:) { DateTime.parse(value) }
212
+ },
213
+ boolean: {
214
+ boolean: ->(value:) { value }, # No-op for same type
215
+ string: ->(value:) { value.to_s },
216
+ integer: ->(value:) { value ? 1 : 0 }
217
+ },
218
+ datetime: {
219
+ datetime: ->(value:) { value }, # No-op for same type
220
+ string: ->(value:) { value.iso8601 },
221
+ integer: ->(value:) { value.to_i }
222
+ }
223
+ }
224
+ end
225
+
226
+ # Parses a string value into a boolean
227
+ # Recognizes: true/false, yes/no, 1/0, on/off (case-insensitive)
228
+ #
229
+ # @param value [String] The string value to parse
230
+ # @return [Boolean] Parsed boolean value
231
+ # @raise [ArgumentError] If string is not a recognized boolean value
232
+ def parse_boolean(value)
233
+ normalized = value.to_s.downcase.strip
234
+
235
+ return true if %w[true 1 yes on].include?(normalized)
236
+ return false if %w[false 0 no off].include?(normalized)
237
+
238
+ raise ArgumentError, "Cannot convert '#{value}' to boolean"
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Attribute
5
+ module Option
6
+ module Modifiers
7
+ # Transforms attribute values using custom lambda functions.
8
+ #
9
+ # ## Usage Examples
10
+ #
11
+ # Simple mode:
12
+ # integer :amount, transform: ->(value:) { value * 100 }
13
+ # string :title, transform: ->(value:) { value.strip.upcase }
14
+ #
15
+ # Advanced mode with custom error message:
16
+ # integer :amount, transform: {
17
+ # is: ->(value:) { value * 100 },
18
+ # message: "Failed to transform amount"
19
+ # }
20
+ #
21
+ # ## Use Cases
22
+ #
23
+ # 1. **Request transformation**:
24
+ # ```ruby
25
+ # request do
26
+ # integer :amount_cents, transform: ->(value:) { value * 100 }
27
+ # end
28
+ # # Input: { amount_cents: 10 }
29
+ # # Service receives: { amount_cents: 1000 }
30
+ # ```
31
+ #
32
+ # 2. **Response transformation**:
33
+ # ```ruby
34
+ # response 200 do
35
+ # string :title, transform: ->(value:) { value.titleize }
36
+ # end
37
+ # # Service returns: { title: "hello world" }
38
+ # # Output: { title: "Hello World" }
39
+ # ```
40
+ #
41
+ # 3. **Complex transformations**:
42
+ # ```ruby
43
+ # string :email, transform: ->(value:) { value.downcase.strip }
44
+ # datetime :timestamp, transform: ->(value:) { value.iso8601 }
45
+ # ```
46
+ #
47
+ # ## Important Notes
48
+ #
49
+ # - Lambda must accept named argument `value:`
50
+ # - All exceptions raised in lambda are caught and re-raised as Validation errors
51
+ # - Transformation is applied during Phase 3 (after validation)
52
+ # - Can be combined with other options (required, default, as, etc.)
53
+ #
54
+ # ## Error Handling
55
+ #
56
+ # If the lambda raises any exception, it's caught and converted to a
57
+ # Treaty::Exceptions::Validation with appropriate error message.
58
+ #
59
+ # ## Advanced Mode
60
+ #
61
+ # Schema format: `{ is: lambda, message: nil }`
62
+ class TransformModifier < Treaty::Attribute::Option::Base
63
+ # Validates that transform value is a lambda
64
+ #
65
+ # @raise [Treaty::Exceptions::Validation] If transform is not a Proc/lambda
66
+ # @return [void]
67
+ def validate_schema!
68
+ transform_lambda = option_value
69
+
70
+ return if transform_lambda.respond_to?(:call)
71
+
72
+ raise Treaty::Exceptions::Validation,
73
+ I18n.t(
74
+ "treaty.attributes.modifiers.transform.invalid_type",
75
+ attribute: @attribute_name,
76
+ type: transform_lambda.class
77
+ )
78
+ end
79
+
80
+ # Applies transformation to the value using the provided lambda
81
+ # Catches all exceptions and re-raises as Validation errors
82
+ # Skips transformation for nil values (handled by RequiredValidator)
83
+ #
84
+ # @param value [Object] The current value
85
+ # @return [Object] Transformed value
86
+ def transform_value(value) # rubocop:disable Metrics/MethodLength
87
+ return value if value.nil? # Transform doesn't modify nil, required validator handles it.
88
+
89
+ transform_lambda = option_value
90
+
91
+ # Call lambda with named argument
92
+ transform_lambda.call(value:)
93
+ rescue StandardError => e
94
+ attributes = {
95
+ attribute: @attribute_name,
96
+ error: e.message
97
+ }
98
+
99
+ # Catch all exceptions from lambda execution
100
+ error_message = resolve_custom_message(**attributes) || I18n.t(
101
+ "treaty.attributes.modifiers.transform.execution_error",
102
+ **attributes
103
+ )
104
+
105
+ raise Treaty::Exceptions::Validation, error_message
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -27,6 +27,8 @@ module Treaty
27
27
  #
28
28
  # - `:as` → AsModifier - Renames attributes
29
29
  # - `:default` → DefaultModifier - Provides default values
30
+ # - `:transform` → TransformModifier - Transforms values using custom lambdas
31
+ # - `:cast` → CastModifier - Converts values between types automatically
30
32
  #
31
33
  # ## Auto-Registration
32
34
  #
@@ -81,6 +83,8 @@ module Treaty
81
83
  def register_modifiers!
82
84
  Registry.register(:as, Modifiers::AsModifier, category: :modifier)
83
85
  Registry.register(:default, Modifiers::DefaultModifier, category: :modifier)
86
+ Registry.register(:transform, Modifiers::TransformModifier, category: :modifier)
87
+ Registry.register(:cast, Modifiers::CastModifier, category: :modifier)
84
88
  end
85
89
  end
86
90
  end
@@ -70,7 +70,8 @@ module Treaty
70
70
  OPTION_KEY_MAPPING = {
71
71
  in: { advanced_key: :inclusion, value_key: :in },
72
72
  as: { advanced_key: :as, value_key: :is },
73
- default: { advanced_key: :default, value_key: :is }
73
+ default: { advanced_key: :default, value_key: :is },
74
+ cast: { advanced_key: :cast, value_key: :to }
74
75
  }.freeze
75
76
  private_constant :OPTION_KEY_MAPPING
76
77
 
@@ -3,18 +3,17 @@
3
3
  module Treaty
4
4
  module Attribute
5
5
  module Validation
6
- # Base class for validation strategies (adapter vs non-adapter).
6
+ # Base class for request and response validation.
7
7
  #
8
8
  # ## Purpose
9
9
  #
10
- # Provides common interface for validation strategies used in Treaty.
11
- # Subclasses implement specific validation logic for different strategies.
10
+ # Provides common interface for validation used in Treaty.
11
+ # Subclasses implement specific validation logic for requests and responses.
12
12
  #
13
13
  # ## Responsibilities
14
14
  #
15
- # 1. **Strategy Interface** - Defines common validation interface
15
+ # 1. **Validation Interface** - Defines common validation interface
16
16
  # 2. **Factory Pattern** - Provides class-level validate! method
17
- # 3. **Strategy Detection** - Checks if adapter strategy is active
18
17
  #
19
18
  # ## Subclasses
20
19
  #
@@ -29,14 +28,6 @@ module Treaty
29
28
  # Example usage:
30
29
  # Request::Validation.validate!(version_factory: factory, data: params)
31
30
  #
32
- # ## Strategy Pattern
33
- #
34
- # The validation system supports two strategies:
35
- # - **Adapter Strategy** - Adapts between different API versions
36
- # - **Standard Strategy** - Direct version handling
37
- #
38
- # This base class provides `adapter_strategy?` helper to check current strategy.
39
- #
40
31
  # ## Factory Method
41
32
  #
42
33
  # The `self.validate!(...)` class method provides a convenient factory pattern:
@@ -49,7 +40,7 @@ module Treaty
49
40
  # ## Architecture
50
41
  #
51
42
  # Works with:
52
- # - VersionFactory - Provides version and strategy information
43
+ # - VersionFactory - Provides version information
53
44
  # - Orchestrator::Base - Performs actual validation and transformation
54
45
  class Base
55
46
  # Class-level factory method for validation
@@ -63,7 +54,7 @@ module Treaty
63
54
 
64
55
  # Creates a new validation instance
65
56
  #
66
- # @param version_factory [VersionFactory] Factory containing version and strategy
57
+ # @param version_factory [VersionFactory] Factory containing version information
67
58
  def initialize(version_factory:)
68
59
  @version_factory = version_factory
69
60
  end
@@ -77,15 +68,6 @@ module Treaty
77
68
  raise Treaty::Exceptions::Validation,
78
69
  I18n.t("treaty.attributes.validators.nested.orchestrator.collection_not_implemented")
79
70
  end
80
-
81
- private
82
-
83
- # Checks if adapter strategy is active
84
- #
85
- # @return [Boolean] True if using adapter strategy
86
- def adapter_strategy?
87
- @version_factory.strategy_instance.adapter?
88
- end
89
71
  end
90
72
  end
91
73
  end
@@ -138,8 +138,7 @@ module Treaty
138
138
  def transform(value)
139
139
  value.each_with_index.map do |item, index|
140
140
  if simple_array?
141
- validate_simple_element(item, index)
142
- item
141
+ transform_simple_element(item, index)
143
142
  else
144
143
  transform_array_item(item, index)
145
144
  end
@@ -156,19 +155,21 @@ module Treaty
156
155
  attribute.collection_of_attributes.first.name == SELF_OBJECT
157
156
  end
158
157
 
159
- # Validates a simple array element (primitive value)
158
+ # Transforms a simple array element (primitive value)
159
+ # Validates and applies transformations to the element
160
160
  #
161
- # @param item [Object] Array element to validate
161
+ # @param item [Object] Array element to transform
162
162
  # @param index [Integer] Element index for error messages
163
163
  # @raise [Treaty::Exceptions::Validation] If validation fails
164
- # @return [void]
165
- def validate_simple_element(item, index) # rubocop:disable Metrics/MethodLength
164
+ # @return [Object] Transformed element value
165
+ def transform_simple_element(item, index) # rubocop:disable Metrics/MethodLength
166
166
  self_attr = attribute.collection_of_attributes.first
167
167
  validator = AttributeValidator.new(self_attr)
168
168
  validator.validate_schema!
169
169
 
170
170
  begin
171
171
  validator.validate_value!(item)
172
+ validator.transform_value(item)
172
173
  rescue Treaty::Exceptions::Validation => e
173
174
  raise Treaty::Exceptions::Validation,
174
175
  I18n.t(
@@ -9,7 +9,7 @@ module Treaty
9
9
  attr_reader :attribute_nesting_level
10
10
 
11
11
  def initialize
12
- @version = ->(context) { context }
12
+ @version = ->(controller) { controller }
13
13
 
14
14
  @attribute_nesting_level = 5
15
15
  end
@@ -3,17 +3,19 @@
3
3
  module Treaty
4
4
  module Context
5
5
  module Callable
6
- def call!(version:, params:)
7
- context = send(:new)
6
+ def call!(version:, params:, context: nil, inventory: nil)
7
+ treaty_instance = send(:new)
8
8
 
9
- _call!(context, version:, params:)
9
+ _call!(treaty_instance, context:, inventory:, version:, params:)
10
10
  end
11
11
 
12
12
  private
13
13
 
14
- def _call!(context, version:, params:)
15
- context.send(
14
+ def _call!(treaty_instance, context:, inventory:, version:, params:)
15
+ treaty_instance.send(
16
16
  :_call!,
17
+ context:,
18
+ inventory:,
17
19
  version:,
18
20
  params:,
19
21
  collection_of_versions:
@@ -6,11 +6,15 @@ module Treaty
6
6
  private
7
7
 
8
8
  def _call!(
9
+ context:,
10
+ inventory:,
9
11
  version:,
10
12
  params:,
11
13
  collection_of_versions:
12
14
  )
13
15
  call!(
16
+ context:,
17
+ inventory:,
14
18
  version:,
15
19
  params:,
16
20
  collection_of_versions: