treaty 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +14 -3
- data/config/locales/en.yml +21 -5
- data/lib/treaty/attribute/option/base.rb +3 -3
- data/lib/treaty/attribute/validation/base.rb +6 -24
- data/lib/treaty/configuration.rb +1 -1
- data/lib/treaty/context/callable.rb +7 -5
- data/lib/treaty/context/workspace.rb +4 -0
- data/lib/treaty/controller/dsl.rb +25 -3
- data/lib/treaty/exceptions/base.rb +0 -1
- data/lib/treaty/exceptions/inventory.rb +70 -0
- data/lib/treaty/exceptions/method_name.rb +0 -2
- data/lib/treaty/executor/inventory.rb +122 -0
- data/lib/treaty/info/rest/builder.rb +0 -1
- data/lib/treaty/inventory/collection.rb +71 -0
- data/lib/treaty/inventory/factory.rb +91 -0
- data/lib/treaty/inventory/inventory.rb +92 -0
- data/lib/treaty/request/validator.rb +2 -7
- data/lib/treaty/response/validator.rb +1 -5
- data/lib/treaty/version.rb +1 -1
- data/lib/treaty/versions/execution/request.rb +29 -4
- data/lib/treaty/versions/factory.rb +0 -6
- data/lib/treaty/versions/workspace.rb +3 -1
- metadata +6 -3
- data/lib/treaty/exceptions/strategy.rb +0 -63
- data/lib/treaty/strategy.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c10e0241b816115758a4383e524fc0f9cfb6738e95f4fddff5a1b32eb114c719
|
|
4
|
+
data.tar.gz: 4a5bfe17af7e6efb87975770aed1a3a67fb817aeee13cdbd05c88c187407efe0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 12b23780c22b9987d317323e723c81d5be07ff2099e74a6cf90c4eab5bb4d0d74a6f0c8b05f11f7800daf16c02d5dd7c317c2d7df770ad99c1638933e94b6297
|
|
7
|
+
data.tar.gz: 4074fa86260a0f9e79adeaf6b41a61f70f5e568e7cbd6592b4eb8c19c270c954753db941be3490ce40944d1edc8cf9445caf75ef1de0b18e65e7939ad6ffd191
|
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.
|
|
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
|
|
data/config/locales/en.yml
CHANGED
|
@@ -91,14 +91,10 @@ en:
|
|
|
91
91
|
# Version factory
|
|
92
92
|
factory:
|
|
93
93
|
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,
|
|
94
|
+
unknown_method: "Unknown method '%{method}' in version definition. Available methods: summary, deprecated, request, response, delegate_to"
|
|
95
95
|
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
96
|
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
97
|
|
|
98
|
-
# Strategy validation
|
|
99
|
-
strategy:
|
|
100
|
-
unknown: "Unknown strategy: %{strategy}"
|
|
101
|
-
|
|
102
98
|
# ============================================================================
|
|
103
99
|
# Execution: Service and executor invocation
|
|
104
100
|
# ============================================================================
|
|
@@ -120,3 +116,23 @@ en:
|
|
|
120
116
|
# ============================================================================
|
|
121
117
|
controller:
|
|
122
118
|
treaty_class_not_found: "%{class_name}"
|
|
119
|
+
|
|
120
|
+
# ============================================================================
|
|
121
|
+
# Inventory: Controller data provisioning system
|
|
122
|
+
# ============================================================================
|
|
123
|
+
inventory:
|
|
124
|
+
# Factory DSL errors
|
|
125
|
+
unknown_method: "Unknown method '%{method}' in treaty block for action '%{action}'. Only 'provide' method is supported. Use: provide :name, from: :source OR provide :name"
|
|
126
|
+
name_must_be_symbol: "Inventory name must be a Symbol, got %{name}. Use: provide :name, from: :source OR provide :name"
|
|
127
|
+
|
|
128
|
+
# Inventory validation errors
|
|
129
|
+
invalid_name: "Inventory name must be a non-empty Symbol, got %{name}"
|
|
130
|
+
source_required: "Inventory source cannot be nil. Provide a Symbol (method name), Proc/Lambda, or direct value"
|
|
131
|
+
evaluation_error: "Failed to evaluate inventory item '%{name}': %{error}"
|
|
132
|
+
|
|
133
|
+
# ============================================================================
|
|
134
|
+
# Executor: Inventory executor wrapper
|
|
135
|
+
# ============================================================================
|
|
136
|
+
executor:
|
|
137
|
+
inventory:
|
|
138
|
+
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
|
|
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(**
|
|
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(**
|
|
154
|
+
message.call(**attributes)
|
|
155
155
|
else
|
|
156
156
|
message
|
|
157
157
|
end
|
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
module Treaty
|
|
4
4
|
module Attribute
|
|
5
5
|
module Validation
|
|
6
|
-
# Base class for
|
|
6
|
+
# Base class for request and response validation.
|
|
7
7
|
#
|
|
8
8
|
# ## Purpose
|
|
9
9
|
#
|
|
10
|
-
# Provides common interface for validation
|
|
11
|
-
# Subclasses implement specific validation logic for
|
|
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. **
|
|
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
|
|
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
|
|
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
|
data/lib/treaty/configuration.rb
CHANGED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
module Treaty
|
|
4
4
|
module Context
|
|
5
5
|
module Callable
|
|
6
|
-
def call!(version:, params:)
|
|
7
|
-
|
|
6
|
+
def call!(version:, params:, context: nil, inventory: nil)
|
|
7
|
+
treaty_instance = send(:new)
|
|
8
8
|
|
|
9
|
-
_call!(
|
|
9
|
+
_call!(treaty_instance, context:, inventory:, version:, params:)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
private
|
|
13
13
|
|
|
14
|
-
def _call!(
|
|
15
|
-
|
|
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:
|
|
@@ -11,11 +11,23 @@ module Treaty
|
|
|
11
11
|
module ClassMethods
|
|
12
12
|
private
|
|
13
13
|
|
|
14
|
-
def treaty(action_name)
|
|
14
|
+
def treaty(action_name, &block) # rubocop:disable Metrics/MethodLength
|
|
15
|
+
# Capture block in a local variable before using in define_method.
|
|
16
|
+
# This is necessary because define_method creates a new closure,
|
|
17
|
+
# and the block parameter might not be accessible without explicit capture.
|
|
18
|
+
inventory_block = block
|
|
19
|
+
|
|
15
20
|
define_method(action_name) do
|
|
16
|
-
|
|
21
|
+
inventory_collection = treaty_build_inventory_for(action_name, inventory_block)
|
|
22
|
+
|
|
23
|
+
treaty_result = treaty_class.call!(
|
|
24
|
+
context: self,
|
|
25
|
+
inventory: inventory_collection,
|
|
26
|
+
version: treaty_version,
|
|
27
|
+
params:
|
|
28
|
+
)
|
|
17
29
|
|
|
18
|
-
render json:
|
|
30
|
+
render json: treaty_result.data, status: treaty_result.status
|
|
19
31
|
end
|
|
20
32
|
end
|
|
21
33
|
end
|
|
@@ -39,6 +51,16 @@ module Treaty
|
|
|
39
51
|
def treaty_version
|
|
40
52
|
Treaty::Engine.config.treaty.version.call(self)
|
|
41
53
|
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def treaty_build_inventory_for(action_name, block)
|
|
58
|
+
return nil unless block
|
|
59
|
+
|
|
60
|
+
factory = Treaty::Inventory::Factory.new(action_name)
|
|
61
|
+
factory.instance_eval(&block)
|
|
62
|
+
factory.collection
|
|
63
|
+
end
|
|
42
64
|
end
|
|
43
65
|
end
|
|
44
66
|
end
|
|
@@ -37,7 +37,6 @@ module Treaty
|
|
|
37
37
|
# - Deprecated - API version deprecation
|
|
38
38
|
# - SpecifiedVersionNotFound - No version specified and no default configured
|
|
39
39
|
# - VersionNotFound - Requested version doesn't exist
|
|
40
|
-
# - Strategy - Invalid strategy specification
|
|
41
40
|
# - ClassName - Treaty class not found
|
|
42
41
|
# - MethodName - Unknown method in DSL
|
|
43
42
|
# - NestedAttributes - Nesting depth exceeded
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Exceptions
|
|
5
|
+
# Raised when inventory definition or processing fails
|
|
6
|
+
#
|
|
7
|
+
# ## Purpose
|
|
8
|
+
#
|
|
9
|
+
# Indicates errors during inventory DSL processing, including invalid method calls,
|
|
10
|
+
# missing parameters, and invalid source types. Provides consistent error handling
|
|
11
|
+
# for the inventory system.
|
|
12
|
+
#
|
|
13
|
+
# ## Usage
|
|
14
|
+
#
|
|
15
|
+
# Raised in various inventory scenarios:
|
|
16
|
+
#
|
|
17
|
+
# ### Invalid DSL Method
|
|
18
|
+
# ```ruby
|
|
19
|
+
# # When calling unknown method in treaty block
|
|
20
|
+
# treaty :index do
|
|
21
|
+
# unknown_method :something
|
|
22
|
+
# end
|
|
23
|
+
# # => Treaty::Exceptions::Inventory: Unknown method 'unknown_method' in treaty block for action 'index'
|
|
24
|
+
# ```
|
|
25
|
+
#
|
|
26
|
+
# ### Missing Required Parameters
|
|
27
|
+
# ```ruby
|
|
28
|
+
# # When 'from' parameter is missing
|
|
29
|
+
# treaty :index do
|
|
30
|
+
# provide :posts
|
|
31
|
+
# end
|
|
32
|
+
# # => Treaty::Exceptions::Inventory: Inventory 'posts' requires 'from' parameter
|
|
33
|
+
#
|
|
34
|
+
# # When inventory name is not a symbol
|
|
35
|
+
# treaty :index do
|
|
36
|
+
# provide "posts", from: :load_posts
|
|
37
|
+
# end
|
|
38
|
+
# # => Treaty::Exceptions::Inventory: Inventory name must be a Symbol, got "posts"
|
|
39
|
+
# ```
|
|
40
|
+
#
|
|
41
|
+
# ### Invalid Source
|
|
42
|
+
# ```ruby
|
|
43
|
+
# # When source is nil
|
|
44
|
+
# treaty :index do
|
|
45
|
+
# provide :posts, from: nil
|
|
46
|
+
# end
|
|
47
|
+
# # => Treaty::Exceptions::Inventory: Inventory source cannot be nil
|
|
48
|
+
# ```
|
|
49
|
+
#
|
|
50
|
+
# ## Integration
|
|
51
|
+
#
|
|
52
|
+
# Can be rescued by application controllers:
|
|
53
|
+
#
|
|
54
|
+
# ```ruby
|
|
55
|
+
# rescue_from Treaty::Exceptions::Inventory, with: :render_inventory_error
|
|
56
|
+
#
|
|
57
|
+
# def render_inventory_error(exception)
|
|
58
|
+
# render json: { error: exception.message }, status: :internal_server_error
|
|
59
|
+
# end
|
|
60
|
+
# ```
|
|
61
|
+
#
|
|
62
|
+
# ## Valid Sources
|
|
63
|
+
#
|
|
64
|
+
# - Symbol: Method name to call on controller (e.g., `:load_posts`)
|
|
65
|
+
# - Proc/Lambda: Callable object (e.g., `-> { Post.all }`)
|
|
66
|
+
# - Direct value: String, number, or any other value (e.g., `"Welcome"`)
|
|
67
|
+
class Inventory < Base
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -15,7 +15,6 @@ module Treaty
|
|
|
15
15
|
#
|
|
16
16
|
# ```ruby
|
|
17
17
|
# version 1 do
|
|
18
|
-
# strategy Treaty::Strategy::ADAPTER # Valid
|
|
19
18
|
# deprecated true # Valid
|
|
20
19
|
# summary "Version 1" # Valid
|
|
21
20
|
#
|
|
@@ -38,7 +37,6 @@ module Treaty
|
|
|
38
37
|
# ## Valid DSL Methods
|
|
39
38
|
#
|
|
40
39
|
# Within a version block, these methods are valid:
|
|
41
|
-
# - strategy(code) - Set version strategy (DIRECT/ADAPTER)
|
|
42
40
|
# - deprecated(condition) - Mark version as deprecated
|
|
43
41
|
# - summary(text) - Add version description
|
|
44
42
|
# - request(&block) - Define request schema
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Executor
|
|
5
|
+
# Inventory wrapper that provides method-based access to inventory items.
|
|
6
|
+
#
|
|
7
|
+
# ## Purpose
|
|
8
|
+
#
|
|
9
|
+
# Wraps inventory collection and controller context, providing lazy evaluation
|
|
10
|
+
# of inventory items through method calls. This encapsulates all inventory logic
|
|
11
|
+
# within the class and provides a clean API for services.
|
|
12
|
+
#
|
|
13
|
+
# ## Usage
|
|
14
|
+
#
|
|
15
|
+
# ```ruby
|
|
16
|
+
# # Created internally by Treaty
|
|
17
|
+
# inventory = Treaty::Executor::Inventory.new(inventory_collection, controller_context)
|
|
18
|
+
#
|
|
19
|
+
# # Access via method calls - evaluates lazily
|
|
20
|
+
# inventory.posts # => Calls controller method or evaluates proc
|
|
21
|
+
# inventory.current_user # => Returns evaluated value
|
|
22
|
+
#
|
|
23
|
+
# # Raises exception for missing items
|
|
24
|
+
# inventory.missing_item # => Treaty::Exceptions::Inventory
|
|
25
|
+
#
|
|
26
|
+
# # Convert to hash - evaluates all items
|
|
27
|
+
# inventory.to_h # => { posts: [...], current_user: ... }
|
|
28
|
+
# ```
|
|
29
|
+
#
|
|
30
|
+
# ## Architecture
|
|
31
|
+
#
|
|
32
|
+
# The class encapsulates:
|
|
33
|
+
# - Inventory collection (from controller's treaty block)
|
|
34
|
+
# - Controller context (for method calls and proc evaluation)
|
|
35
|
+
# - Lazy evaluation logic (items evaluated on access)
|
|
36
|
+
#
|
|
37
|
+
# ## Error Handling
|
|
38
|
+
#
|
|
39
|
+
# If an inventory item is not found, raises `Treaty::Exceptions::Inventory` with
|
|
40
|
+
# an I18n-translated error message listing available items.
|
|
41
|
+
class Inventory
|
|
42
|
+
# Creates a new inventory instance
|
|
43
|
+
#
|
|
44
|
+
# @param inventory [Treaty::Inventory::Collection] Collection of inventory items
|
|
45
|
+
# @param context [Object] Controller instance for evaluation
|
|
46
|
+
def initialize(inventory, context)
|
|
47
|
+
@inventory = inventory
|
|
48
|
+
@context = context
|
|
49
|
+
@evaluated_cache = {}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Provides method-based access to inventory items with lazy evaluation
|
|
53
|
+
#
|
|
54
|
+
# @param method_name [Symbol] The inventory item name
|
|
55
|
+
# @param _args [Array] Arguments (not used, for compatibility)
|
|
56
|
+
# @return [Object] The evaluated inventory item value
|
|
57
|
+
# @raise [Treaty::Exceptions::Inventory] If item not found
|
|
58
|
+
def method_missing(method_name, *_args)
|
|
59
|
+
# Check cache first
|
|
60
|
+
return @evaluated_cache[method_name] if @evaluated_cache.key?(method_name)
|
|
61
|
+
|
|
62
|
+
# Find inventory item
|
|
63
|
+
item = find_inventory_item(method_name)
|
|
64
|
+
|
|
65
|
+
# Evaluate and cache
|
|
66
|
+
@evaluated_cache[method_name] = item.evaluate(@context)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Checks if inventory responds to a method
|
|
70
|
+
#
|
|
71
|
+
# @param method_name [Symbol] The method name to check
|
|
72
|
+
# @param include_private [Boolean] Whether to include private methods
|
|
73
|
+
# @return [Boolean] True if inventory has the item
|
|
74
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
75
|
+
return false if @inventory.nil?
|
|
76
|
+
|
|
77
|
+
@inventory.names.include?(method_name) || super
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Converts inventory to hash, evaluating all items
|
|
81
|
+
#
|
|
82
|
+
# @return [Hash] Hash of all evaluated inventory items
|
|
83
|
+
def to_h
|
|
84
|
+
return {} if @inventory.nil?
|
|
85
|
+
|
|
86
|
+
@inventory.evaluate(@context)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns string representation
|
|
90
|
+
#
|
|
91
|
+
# @return [String] Inventory description
|
|
92
|
+
def inspect
|
|
93
|
+
items = @inventory&.names || []
|
|
94
|
+
"#<Treaty::Executor::Inventory items=#{items.inspect}>"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
# Finds inventory item by name
|
|
100
|
+
#
|
|
101
|
+
# @param name [Symbol] Inventory item name
|
|
102
|
+
# @return [Treaty::Inventory::Inventory] The inventory item
|
|
103
|
+
# @raise [Treaty::Exceptions::Inventory] If not found or inventory is nil
|
|
104
|
+
def find_inventory_item(name)
|
|
105
|
+
# Use find method for cleaner search
|
|
106
|
+
item = @inventory&.find { |item| item.name == name }
|
|
107
|
+
|
|
108
|
+
return item if item
|
|
109
|
+
|
|
110
|
+
# Item not found - list available items
|
|
111
|
+
available = @inventory&.names || []
|
|
112
|
+
|
|
113
|
+
raise Treaty::Exceptions::Inventory,
|
|
114
|
+
I18n.t(
|
|
115
|
+
"treaty.executor.inventory.item_not_found",
|
|
116
|
+
name:,
|
|
117
|
+
available: available.join(", ")
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -36,7 +36,6 @@ module Treaty
|
|
|
36
36
|
segments: gem_version.segments,
|
|
37
37
|
default: version.default_result,
|
|
38
38
|
summary: version.summary_text,
|
|
39
|
-
strategy: version.strategy_instance.code,
|
|
40
39
|
deprecated: version.deprecated_result,
|
|
41
40
|
executor: build_executor_with(version),
|
|
42
41
|
request: build_request_with(version),
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Inventory
|
|
5
|
+
# Collection wrapper for sets of inventory items.
|
|
6
|
+
#
|
|
7
|
+
# ## Purpose
|
|
8
|
+
#
|
|
9
|
+
# Provides a unified interface for working with collections of inventory items.
|
|
10
|
+
# Uses Ruby Set internally for uniqueness but exposes Array-like interface.
|
|
11
|
+
#
|
|
12
|
+
# ## Usage
|
|
13
|
+
#
|
|
14
|
+
# Used internally by:
|
|
15
|
+
# - Inventory::Factory (to store inventory items during DSL processing)
|
|
16
|
+
#
|
|
17
|
+
# ## Methods
|
|
18
|
+
#
|
|
19
|
+
# Delegates common collection methods to internal Set:
|
|
20
|
+
# - `<<` - Add inventory item
|
|
21
|
+
# - `empty?` - Check if collection is empty
|
|
22
|
+
#
|
|
23
|
+
# Custom methods:
|
|
24
|
+
# - `exists?` - Returns true if collection is not empty
|
|
25
|
+
# - `evaluate` - Evaluates all inventory items with context
|
|
26
|
+
#
|
|
27
|
+
# ## Example
|
|
28
|
+
#
|
|
29
|
+
# collection = Collection.new
|
|
30
|
+
# collection << Inventory.new(name: :posts, source: :load_posts)
|
|
31
|
+
# collection << Inventory.new(name: :meta, source: -> { { count: 10 } })
|
|
32
|
+
# collection.size # => 2
|
|
33
|
+
# collection.exists? # => true
|
|
34
|
+
class Collection
|
|
35
|
+
extend Forwardable
|
|
36
|
+
|
|
37
|
+
def_delegators :@collection, :<<, :each_with_object, :find, :empty?
|
|
38
|
+
|
|
39
|
+
# Creates a new collection instance
|
|
40
|
+
#
|
|
41
|
+
# @param collection [Set] Initial collection (default: empty Set)
|
|
42
|
+
def initialize(collection = Set.new)
|
|
43
|
+
@collection = collection
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Checks if collection has any elements
|
|
47
|
+
#
|
|
48
|
+
# @return [Boolean] True if collection is not empty
|
|
49
|
+
def exists?
|
|
50
|
+
!empty?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns array of all inventory item names
|
|
54
|
+
#
|
|
55
|
+
# @return [Array<Symbol>] Array of inventory item names
|
|
56
|
+
def names
|
|
57
|
+
@collection.each_with_object([]) { |item, names| names << item.name }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Evaluates all inventory items with the given context
|
|
61
|
+
#
|
|
62
|
+
# @param context [Object] The controller instance to call methods on
|
|
63
|
+
# @return [Hash{Symbol => Object}] Hash of inventory name => resolved value
|
|
64
|
+
def evaluate(context)
|
|
65
|
+
@collection.each_with_object({}) do |inventory_item, hash|
|
|
66
|
+
hash[inventory_item.name] = inventory_item.evaluate(context)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Inventory
|
|
5
|
+
# Factory for building inventory collections via DSL.
|
|
6
|
+
#
|
|
7
|
+
# ## Purpose
|
|
8
|
+
#
|
|
9
|
+
# Provides the `provide` DSL method for defining inventory items in controller blocks.
|
|
10
|
+
# Captures calls like `provide :posts, from: :load_posts` and builds a collection.
|
|
11
|
+
#
|
|
12
|
+
# ## Usage
|
|
13
|
+
#
|
|
14
|
+
# Used internally by Controller::DSL when processing treaty blocks:
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# treaty :index do
|
|
18
|
+
# provide :posts, from: :load_posts # Explicit source
|
|
19
|
+
# provide :meta, from: -> { { count: 10 } } # Lambda source
|
|
20
|
+
# provide :current_user # Shorthand: uses :current_user as source
|
|
21
|
+
# end
|
|
22
|
+
# ```
|
|
23
|
+
#
|
|
24
|
+
# ## Valid Sources
|
|
25
|
+
#
|
|
26
|
+
# - Symbol: Method name to call on controller (e.g., `:load_posts`)
|
|
27
|
+
# - Proc/Lambda: Callable object (e.g., `-> { Post.all }`)
|
|
28
|
+
# - Direct value: String, number, or any other value (e.g., `"Welcome"`)
|
|
29
|
+
# - Omitted: Uses inventory name as source (e.g., `provide :posts` → `from: :posts`)
|
|
30
|
+
#
|
|
31
|
+
# ## Invalid Sources
|
|
32
|
+
#
|
|
33
|
+
# - Direct method calls without symbol/proc (e.g., `from: load_posts`)
|
|
34
|
+
# - Explicit nil values (e.g., `from: nil`)
|
|
35
|
+
class Factory
|
|
36
|
+
attr_reader :collection
|
|
37
|
+
|
|
38
|
+
def initialize(action_name)
|
|
39
|
+
@action_name = action_name
|
|
40
|
+
@collection = Collection.new
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Handles the `provide` DSL method via method_missing
|
|
44
|
+
#
|
|
45
|
+
# @param method_name [Symbol] Should be :provide
|
|
46
|
+
# @param args [Array] First argument is the inventory name
|
|
47
|
+
# @param options [Hash] Optional :from key with source (defaults to inventory name)
|
|
48
|
+
# @return [Collection] The collection being built
|
|
49
|
+
# @raise [Treaty::Exceptions::Inventory] For invalid method or missing parameters
|
|
50
|
+
def method_missing(method_name, *args, **options, &_block) # rubocop:disable Metrics/MethodLength
|
|
51
|
+
# Only handle 'provide' method
|
|
52
|
+
unless method_name == :provide
|
|
53
|
+
raise Treaty::Exceptions::Inventory,
|
|
54
|
+
I18n.t(
|
|
55
|
+
"treaty.inventory.unknown_method",
|
|
56
|
+
method: method_name,
|
|
57
|
+
action: @action_name
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Extract inventory name
|
|
62
|
+
inventory_name = args.first
|
|
63
|
+
|
|
64
|
+
unless inventory_name.is_a?(Symbol)
|
|
65
|
+
raise Treaty::Exceptions::Inventory,
|
|
66
|
+
I18n.t(
|
|
67
|
+
"treaty.inventory.name_must_be_symbol",
|
|
68
|
+
name: inventory_name.inspect
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Extract source from options (default to inventory name if not provided)
|
|
73
|
+
source = if options.key?(:from)
|
|
74
|
+
options.fetch(:from)
|
|
75
|
+
else
|
|
76
|
+
inventory_name
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Create and add inventory item to collection
|
|
80
|
+
@collection << Inventory.new(name: inventory_name, source:)
|
|
81
|
+
|
|
82
|
+
# Return collection for potential chaining
|
|
83
|
+
@collection
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def respond_to_missing?(method_name, *)
|
|
87
|
+
method_name == :provide || super
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Inventory
|
|
5
|
+
# Represents a single inventory item that provides data to the treaty execution.
|
|
6
|
+
#
|
|
7
|
+
# An inventory item has a name and a source. The source can be:
|
|
8
|
+
# - Symbol: A method name to call on the controller (e.g., :load_posts)
|
|
9
|
+
# - Proc/Lambda: A callable object (e.g., -> { Post.all })
|
|
10
|
+
# - Direct value: Any other value to pass directly (e.g., "text", 42)
|
|
11
|
+
#
|
|
12
|
+
# ## Usage
|
|
13
|
+
#
|
|
14
|
+
# ```ruby
|
|
15
|
+
# # In controller
|
|
16
|
+
# treaty :index do
|
|
17
|
+
# provide :posts, from: :load_posts
|
|
18
|
+
# provide :meta, from: -> { { count: 10 } }
|
|
19
|
+
# provide :title, from: "Welcome"
|
|
20
|
+
# end
|
|
21
|
+
# ```
|
|
22
|
+
class Inventory
|
|
23
|
+
attr_reader :name, :source
|
|
24
|
+
|
|
25
|
+
def initialize(name:, source:)
|
|
26
|
+
validate_name!(name)
|
|
27
|
+
validate_source!(source)
|
|
28
|
+
|
|
29
|
+
@name = name
|
|
30
|
+
@source = source
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Evaluates the inventory source with the given context
|
|
34
|
+
#
|
|
35
|
+
# @param context [Object] The controller instance to call methods on
|
|
36
|
+
# @return [Object] The resolved value
|
|
37
|
+
# @raise [Treaty::Exceptions::Inventory] If evaluation fails
|
|
38
|
+
def evaluate(context) # rubocop:disable Metrics/MethodLength
|
|
39
|
+
case source
|
|
40
|
+
when Symbol
|
|
41
|
+
evaluate_symbol(context)
|
|
42
|
+
when Proc
|
|
43
|
+
evaluate_proc(context)
|
|
44
|
+
else
|
|
45
|
+
source
|
|
46
|
+
end
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
raise Treaty::Exceptions::Inventory,
|
|
49
|
+
I18n.t(
|
|
50
|
+
"treaty.inventory.evaluation_error",
|
|
51
|
+
name: @name,
|
|
52
|
+
error: e.message
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Evaluates Symbol source by calling method on context
|
|
59
|
+
#
|
|
60
|
+
# @param context [Object] The controller instance
|
|
61
|
+
# @return [Object] The method return value
|
|
62
|
+
def evaluate_symbol(context)
|
|
63
|
+
context.send(source)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Evaluates Proc source within controller context
|
|
67
|
+
#
|
|
68
|
+
# @param context [Object] The controller instance
|
|
69
|
+
# @return [Object] The proc return value
|
|
70
|
+
def evaluate_proc(context)
|
|
71
|
+
# Execute proc in controller context to access instance variables and methods
|
|
72
|
+
context.instance_exec(&source)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def validate_name!(name)
|
|
76
|
+
return if name.is_a?(Symbol) && !name.to_s.empty?
|
|
77
|
+
|
|
78
|
+
raise Treaty::Exceptions::Inventory,
|
|
79
|
+
I18n.t("treaty.inventory.invalid_name", name: name.inspect)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def validate_source!(source)
|
|
83
|
+
# Source must be Symbol, Proc, or any other direct value
|
|
84
|
+
# We don't allow nil as it's likely a mistake
|
|
85
|
+
return unless source.nil?
|
|
86
|
+
|
|
87
|
+
raise Treaty::Exceptions::Inventory,
|
|
88
|
+
I18n.t("treaty.inventory.source_required")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -34,11 +34,10 @@ module Treaty
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def validate_request_attributes!
|
|
38
|
-
return request_data unless adapter_strategy?
|
|
37
|
+
def validate_request_attributes!
|
|
39
38
|
return request_data unless request_attributes_exist?
|
|
40
39
|
|
|
41
|
-
#
|
|
40
|
+
# Validate request attributes with orchestrator:
|
|
42
41
|
orchestrator_class = Class.new(Treaty::Attribute::Validation::Orchestrator::Base) do
|
|
43
42
|
define_method(:collection_of_attributes) do
|
|
44
43
|
@version_factory.request_factory.collection_of_attributes
|
|
@@ -51,10 +50,6 @@ module Treaty
|
|
|
51
50
|
)
|
|
52
51
|
end
|
|
53
52
|
|
|
54
|
-
def adapter_strategy?
|
|
55
|
-
!@version_factory.strategy_instance.direct?
|
|
56
|
-
end
|
|
57
|
-
|
|
58
53
|
def request_attributes_exist?
|
|
59
54
|
return false if @version_factory.request_factory&.collection_of_attributes&.empty?
|
|
60
55
|
|
|
@@ -29,7 +29,7 @@ module Treaty
|
|
|
29
29
|
def validate_response_attributes!
|
|
30
30
|
return @response_data unless response_attributes_exist?
|
|
31
31
|
|
|
32
|
-
# Create orchestrator for
|
|
32
|
+
# Create orchestrator for response validation
|
|
33
33
|
# Orchestrator filters data by attributes and performs transformation
|
|
34
34
|
orchestrator_class = Class.new(Treaty::Attribute::Validation::Orchestrator::Base) do
|
|
35
35
|
define_method(:collection_of_attributes) do
|
|
@@ -43,10 +43,6 @@ module Treaty
|
|
|
43
43
|
)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def adapter_strategy?
|
|
47
|
-
!@version_factory.strategy_instance.direct?
|
|
48
|
-
end
|
|
49
|
-
|
|
50
46
|
def response_attributes_exist?
|
|
51
47
|
return false if @version_factory.response_factory&.collection_of_attributes&.empty?
|
|
52
48
|
|
data/lib/treaty/version.rb
CHANGED
|
@@ -8,7 +8,9 @@ module Treaty
|
|
|
8
8
|
new(...).execute!
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def initialize(version_factory:, validated_params:)
|
|
11
|
+
def initialize(version_factory:, validated_params:, inventory: nil, context: nil)
|
|
12
|
+
@inventory = inventory
|
|
13
|
+
@context = context
|
|
12
14
|
@version_factory = version_factory
|
|
13
15
|
@validated_params = validated_params
|
|
14
16
|
end
|
|
@@ -89,15 +91,26 @@ module Treaty
|
|
|
89
91
|
########################################################################
|
|
90
92
|
########################################################################
|
|
91
93
|
|
|
94
|
+
# Creates inventory wrapper with lazy evaluation
|
|
95
|
+
#
|
|
96
|
+
# @return [Treaty::Executor::Inventory] Inventory wrapper with method-based access
|
|
97
|
+
def evaluated_inventory
|
|
98
|
+
@evaluated_inventory ||= Treaty::Executor::Inventory.new(@inventory, @context)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
########################################################################
|
|
102
|
+
|
|
92
103
|
def execute_proc
|
|
93
|
-
|
|
104
|
+
# For Proc executors, pass inventory if collection exists
|
|
105
|
+
executor.call(**build_call_params)
|
|
94
106
|
rescue StandardError => e
|
|
95
107
|
raise Treaty::Exceptions::Execution,
|
|
96
108
|
I18n.t("treaty.execution.proc_error", message: e.message)
|
|
97
109
|
end
|
|
98
110
|
|
|
99
111
|
def execute_servactory # rubocop:disable Metrics/MethodLength
|
|
100
|
-
|
|
112
|
+
# For Servactory services, pass inventory if collection exists
|
|
113
|
+
executor.call!(**build_call_params)
|
|
101
114
|
rescue Servactory::Exceptions::Input => e
|
|
102
115
|
raise Treaty::Exceptions::Execution,
|
|
103
116
|
I18n.t("treaty.execution.servactory_input_error", message: e.message)
|
|
@@ -124,7 +137,8 @@ module Treaty
|
|
|
124
137
|
)
|
|
125
138
|
end
|
|
126
139
|
|
|
127
|
-
|
|
140
|
+
# For regular classes, pass inventory if collection exists
|
|
141
|
+
executor.public_send(method_name, **build_call_params)
|
|
128
142
|
rescue StandardError => e
|
|
129
143
|
raise Treaty::Exceptions::Execution,
|
|
130
144
|
I18n.t("treaty.execution.regular_service_error", message: e.message)
|
|
@@ -134,6 +148,17 @@ module Treaty
|
|
|
134
148
|
########################################################################
|
|
135
149
|
########################################################################
|
|
136
150
|
|
|
151
|
+
# Builds call parameters hash with inventory if it exists
|
|
152
|
+
#
|
|
153
|
+
# @return [Hash] Parameters hash with :params and optionally :inventory
|
|
154
|
+
def build_call_params
|
|
155
|
+
if @inventory&.exists?
|
|
156
|
+
{ params: @validated_params, inventory: evaluated_inventory }
|
|
157
|
+
else
|
|
158
|
+
{ params: @validated_params }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
137
162
|
def raise_executor_missing_error!
|
|
138
163
|
raise Treaty::Exceptions::Execution,
|
|
139
164
|
I18n.t(
|
|
@@ -6,7 +6,6 @@ module Treaty
|
|
|
6
6
|
attr_reader :version,
|
|
7
7
|
:default_result,
|
|
8
8
|
:summary_text,
|
|
9
|
-
:strategy_instance,
|
|
10
9
|
:deprecated_result,
|
|
11
10
|
:executor,
|
|
12
11
|
:request_factory,
|
|
@@ -16,7 +15,6 @@ module Treaty
|
|
|
16
15
|
@version = Semantic.new(version)
|
|
17
16
|
@default_result = default.is_a?(Proc) ? default.call : default
|
|
18
17
|
@summary_text = nil
|
|
19
|
-
@strategy_instance = Strategy.new(Strategy::ADAPTER) # without .validate!
|
|
20
18
|
@deprecated_result = false
|
|
21
19
|
@executor = nil
|
|
22
20
|
|
|
@@ -35,10 +33,6 @@ module Treaty
|
|
|
35
33
|
@summary_text = text
|
|
36
34
|
end
|
|
37
35
|
|
|
38
|
-
def strategy(code)
|
|
39
|
-
@strategy_instance = Strategy.new(code).validate!
|
|
40
|
-
end
|
|
41
|
-
|
|
42
36
|
def deprecated(condition = nil)
|
|
43
37
|
result =
|
|
44
38
|
if condition.is_a?(Proc)
|
|
@@ -5,7 +5,7 @@ module Treaty
|
|
|
5
5
|
module Workspace
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def call!(version:, params:, **) # rubocop:disable Metrics/MethodLength
|
|
8
|
+
def call!(context:, inventory:, version:, params:, **) # rubocop:disable Metrics/MethodLength
|
|
9
9
|
super
|
|
10
10
|
|
|
11
11
|
version_factory = Resolver.resolve!(
|
|
@@ -19,6 +19,8 @@ module Treaty
|
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
executor_result = Execution::Request.execute!(
|
|
22
|
+
context:,
|
|
23
|
+
inventory:,
|
|
22
24
|
version_factory:,
|
|
23
25
|
validated_params:
|
|
24
26
|
)
|
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.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anton Sokolov
|
|
@@ -183,22 +183,26 @@ files:
|
|
|
183
183
|
- lib/treaty/exceptions/class_name.rb
|
|
184
184
|
- lib/treaty/exceptions/deprecated.rb
|
|
185
185
|
- lib/treaty/exceptions/execution.rb
|
|
186
|
+
- lib/treaty/exceptions/inventory.rb
|
|
186
187
|
- lib/treaty/exceptions/method_name.rb
|
|
187
188
|
- lib/treaty/exceptions/nested_attributes.rb
|
|
188
189
|
- lib/treaty/exceptions/not_implemented.rb
|
|
189
190
|
- lib/treaty/exceptions/specified_version_not_found.rb
|
|
190
|
-
- lib/treaty/exceptions/strategy.rb
|
|
191
191
|
- lib/treaty/exceptions/unexpected.rb
|
|
192
192
|
- lib/treaty/exceptions/validation.rb
|
|
193
193
|
- lib/treaty/exceptions/version_default_deprecated_conflict.rb
|
|
194
194
|
- lib/treaty/exceptions/version_multiple_defaults.rb
|
|
195
195
|
- lib/treaty/exceptions/version_not_found.rb
|
|
196
|
+
- lib/treaty/executor/inventory.rb
|
|
196
197
|
- lib/treaty/info/entity/builder.rb
|
|
197
198
|
- lib/treaty/info/entity/dsl.rb
|
|
198
199
|
- lib/treaty/info/entity/result.rb
|
|
199
200
|
- lib/treaty/info/rest/builder.rb
|
|
200
201
|
- lib/treaty/info/rest/dsl.rb
|
|
201
202
|
- lib/treaty/info/rest/result.rb
|
|
203
|
+
- lib/treaty/inventory/collection.rb
|
|
204
|
+
- lib/treaty/inventory/factory.rb
|
|
205
|
+
- lib/treaty/inventory/inventory.rb
|
|
202
206
|
- lib/treaty/request/attribute/attribute.rb
|
|
203
207
|
- lib/treaty/request/attribute/builder.rb
|
|
204
208
|
- lib/treaty/request/entity.rb
|
|
@@ -210,7 +214,6 @@ files:
|
|
|
210
214
|
- lib/treaty/response/factory.rb
|
|
211
215
|
- lib/treaty/response/validator.rb
|
|
212
216
|
- lib/treaty/result.rb
|
|
213
|
-
- lib/treaty/strategy.rb
|
|
214
217
|
- lib/treaty/support/loader.rb
|
|
215
218
|
- lib/treaty/version.rb
|
|
216
219
|
- lib/treaty/versions/collection.rb
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Treaty
|
|
4
|
-
module Exceptions
|
|
5
|
-
# Raised when an unknown or invalid strategy is specified
|
|
6
|
-
#
|
|
7
|
-
# ## Purpose
|
|
8
|
-
#
|
|
9
|
-
# Ensures only valid strategy types are used in version definitions.
|
|
10
|
-
# Prevents typos and invalid strategy configurations.
|
|
11
|
-
#
|
|
12
|
-
# ## Usage
|
|
13
|
-
#
|
|
14
|
-
# Raised when specifying an invalid strategy in version definition:
|
|
15
|
-
#
|
|
16
|
-
# ```ruby
|
|
17
|
-
# version 1 do
|
|
18
|
-
# strategy Treaty::Strategy::ADAPTER # Valid
|
|
19
|
-
# strategy Treaty::Strategy::DIRECT # Valid
|
|
20
|
-
#
|
|
21
|
-
# strategy :invalid_strategy # Raises Treaty::Exceptions::Strategy
|
|
22
|
-
# end
|
|
23
|
-
# ```
|
|
24
|
-
#
|
|
25
|
-
# ## Valid Strategies
|
|
26
|
-
#
|
|
27
|
-
# Only two strategies are supported:
|
|
28
|
-
#
|
|
29
|
-
# - `Treaty::Strategy::DIRECT` - Direct pass-through mode
|
|
30
|
-
# - No transformation between service and response
|
|
31
|
-
# - Service output becomes response output directly
|
|
32
|
-
# - Faster but less flexible
|
|
33
|
-
#
|
|
34
|
-
# - `Treaty::Strategy::ADAPTER` - Adapter mode (default)
|
|
35
|
-
# - Transforms service output to match response schema
|
|
36
|
-
# - Validates and adapts data structure
|
|
37
|
-
# - More flexible, recommended for most cases
|
|
38
|
-
#
|
|
39
|
-
# ## Integration
|
|
40
|
-
#
|
|
41
|
-
# Can be rescued by application controllers:
|
|
42
|
-
#
|
|
43
|
-
# ```ruby
|
|
44
|
-
# rescue_from Treaty::Exceptions::Strategy, with: :render_strategy_error
|
|
45
|
-
#
|
|
46
|
-
# def render_strategy_error(exception)
|
|
47
|
-
# render json: { error: exception.message }, status: :internal_server_error
|
|
48
|
-
# end
|
|
49
|
-
# ```
|
|
50
|
-
#
|
|
51
|
-
# ## Default Behavior
|
|
52
|
-
#
|
|
53
|
-
# If no strategy is specified, ADAPTER is used by default:
|
|
54
|
-
#
|
|
55
|
-
# ```ruby
|
|
56
|
-
# version 1 do
|
|
57
|
-
# # strategy defaults to Treaty::Strategy::ADAPTER
|
|
58
|
-
# end
|
|
59
|
-
# ```
|
|
60
|
-
class Strategy < Base
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
data/lib/treaty/strategy.rb
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Treaty
|
|
4
|
-
class Strategy
|
|
5
|
-
DIRECT = :direct
|
|
6
|
-
ADAPTER = :adapter
|
|
7
|
-
|
|
8
|
-
LIST = [DIRECT, ADAPTER].freeze
|
|
9
|
-
|
|
10
|
-
attr_reader :code
|
|
11
|
-
|
|
12
|
-
def initialize(code)
|
|
13
|
-
@code = code
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def validate!
|
|
17
|
-
return self if LIST.include?(@code)
|
|
18
|
-
|
|
19
|
-
raise Treaty::Exceptions::Strategy,
|
|
20
|
-
I18n.t("treaty.versioning.strategy.unknown", strategy: @code)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def direct?
|
|
24
|
-
@code == DIRECT
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def adapter?
|
|
28
|
-
@code == ADAPTER
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|