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 +4 -4
- data/README.md +14 -3
- data/config/locales/en.yml +32 -5
- data/lib/treaty/attribute/option/base.rb +3 -3
- data/lib/treaty/attribute/option/modifiers/cast_modifier.rb +244 -0
- data/lib/treaty/attribute/option/modifiers/transform_modifier.rb +111 -0
- data/lib/treaty/attribute/option/registry_initializer.rb +4 -0
- data/lib/treaty/attribute/option_normalizer.rb +2 -1
- data/lib/treaty/attribute/validation/base.rb +6 -24
- data/lib/treaty/attribute/validation/nested_transformer.rb +7 -6
- 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 +8 -3
- data/lib/treaty/exceptions/strategy.rb +0 -63
- data/lib/treaty/strategy.rb +0 -31
|
@@ -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)
|