treaty 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/locales/en.yml +3 -3
  4. data/lib/treaty/action/base.rb +11 -0
  5. data/lib/treaty/action/context/callable.rb +90 -0
  6. data/lib/treaty/action/context/dsl.rb +56 -0
  7. data/lib/treaty/action/context/workspace.rb +92 -0
  8. data/lib/treaty/action/executor/inventory.rb +136 -0
  9. data/lib/treaty/{info/rest → action/info}/builder.rb +2 -2
  10. data/lib/treaty/{info/rest → action/info}/dsl.rb +2 -2
  11. data/lib/treaty/{info/rest → action/info}/result.rb +2 -2
  12. data/lib/treaty/action/inventory/collection.rb +77 -0
  13. data/lib/treaty/action/inventory/factory.rb +108 -0
  14. data/lib/treaty/action/inventory/inventory.rb +146 -0
  15. data/lib/treaty/action/request/attribute/attribute.rb +76 -0
  16. data/lib/treaty/action/request/attribute/builder.rb +98 -0
  17. data/lib/treaty/action/request/entity.rb +78 -0
  18. data/lib/treaty/action/request/factory.rb +116 -0
  19. data/lib/treaty/action/request/validator.rb +120 -0
  20. data/lib/treaty/action/response/attribute/attribute.rb +79 -0
  21. data/lib/treaty/action/response/attribute/builder.rb +96 -0
  22. data/lib/treaty/action/response/entity.rb +79 -0
  23. data/lib/treaty/action/response/factory.rb +129 -0
  24. data/lib/treaty/action/response/validator.rb +111 -0
  25. data/lib/treaty/action/result.rb +81 -0
  26. data/lib/treaty/action/versions/collection.rb +47 -0
  27. data/lib/treaty/action/versions/dsl.rb +116 -0
  28. data/lib/treaty/action/versions/execution/request.rb +287 -0
  29. data/lib/treaty/action/versions/executor.rb +61 -0
  30. data/lib/treaty/action/versions/factory.rb +253 -0
  31. data/lib/treaty/action/versions/resolver.rb +150 -0
  32. data/lib/treaty/action/versions/semantic.rb +64 -0
  33. data/lib/treaty/action/versions/workspace.rb +106 -0
  34. data/lib/treaty/action.rb +31 -0
  35. data/lib/treaty/controller/dsl.rb +1 -1
  36. data/lib/treaty/engine.rb +1 -1
  37. data/lib/treaty/{attribute/entity → entity/attribute}/attribute.rb +4 -4
  38. data/lib/treaty/entity/attribute/base.rb +184 -0
  39. data/lib/treaty/entity/attribute/builder/base.rb +275 -0
  40. data/lib/treaty/entity/attribute/collection.rb +67 -0
  41. data/lib/treaty/entity/attribute/dsl.rb +92 -0
  42. data/lib/treaty/entity/attribute/helper_mapper.rb +74 -0
  43. data/lib/treaty/entity/attribute/option/base.rb +190 -0
  44. data/lib/treaty/entity/attribute/option/conditionals/base.rb +92 -0
  45. data/lib/treaty/entity/attribute/option/conditionals/if_conditional.rb +136 -0
  46. data/lib/treaty/entity/attribute/option/conditionals/unless_conditional.rb +153 -0
  47. data/lib/treaty/entity/attribute/option/modifiers/as_modifier.rb +93 -0
  48. data/lib/treaty/entity/attribute/option/modifiers/cast_modifier.rb +285 -0
  49. data/lib/treaty/entity/attribute/option/modifiers/computed_modifier.rb +128 -0
  50. data/lib/treaty/entity/attribute/option/modifiers/default_modifier.rb +105 -0
  51. data/lib/treaty/entity/attribute/option/modifiers/transform_modifier.rb +114 -0
  52. data/lib/treaty/entity/attribute/option/registry.rb +157 -0
  53. data/lib/treaty/entity/attribute/option/registry_initializer.rb +117 -0
  54. data/lib/treaty/entity/attribute/option/validators/format_validator.rb +222 -0
  55. data/lib/treaty/entity/attribute/option/validators/inclusion_validator.rb +94 -0
  56. data/lib/treaty/entity/attribute/option/validators/required_validator.rb +100 -0
  57. data/lib/treaty/entity/attribute/option/validators/type_validator.rb +219 -0
  58. data/lib/treaty/entity/attribute/option_normalizer.rb +168 -0
  59. data/lib/treaty/entity/attribute/option_orchestrator.rb +192 -0
  60. data/lib/treaty/entity/attribute/validation/attribute_validator.rb +147 -0
  61. data/lib/treaty/entity/attribute/validation/base.rb +76 -0
  62. data/lib/treaty/entity/attribute/validation/nested_array_validator.rb +207 -0
  63. data/lib/treaty/entity/attribute/validation/nested_object_validator.rb +105 -0
  64. data/lib/treaty/entity/attribute/validation/nested_transformer.rb +432 -0
  65. data/lib/treaty/entity/attribute/validation/orchestrator/base.rb +262 -0
  66. data/lib/treaty/entity/base.rb +90 -0
  67. data/lib/treaty/entity/builder.rb +101 -0
  68. data/lib/treaty/{info/entity → entity/info}/builder.rb +8 -8
  69. data/lib/treaty/{info/entity → entity/info}/dsl.rb +2 -2
  70. data/lib/treaty/{info/entity → entity/info}/result.rb +2 -2
  71. data/lib/treaty/entity.rb +7 -79
  72. data/lib/treaty/version.rb +1 -1
  73. metadata +66 -64
  74. data/lib/treaty/attribute/base.rb +0 -182
  75. data/lib/treaty/attribute/builder/base.rb +0 -273
  76. data/lib/treaty/attribute/collection.rb +0 -65
  77. data/lib/treaty/attribute/dsl.rb +0 -90
  78. data/lib/treaty/attribute/entity/builder.rb +0 -46
  79. data/lib/treaty/attribute/helper_mapper.rb +0 -72
  80. data/lib/treaty/attribute/option/base.rb +0 -188
  81. data/lib/treaty/attribute/option/conditionals/base.rb +0 -90
  82. data/lib/treaty/attribute/option/conditionals/if_conditional.rb +0 -134
  83. data/lib/treaty/attribute/option/conditionals/unless_conditional.rb +0 -151
  84. data/lib/treaty/attribute/option/modifiers/as_modifier.rb +0 -91
  85. data/lib/treaty/attribute/option/modifiers/cast_modifier.rb +0 -283
  86. data/lib/treaty/attribute/option/modifiers/computed_modifier.rb +0 -126
  87. data/lib/treaty/attribute/option/modifiers/default_modifier.rb +0 -103
  88. data/lib/treaty/attribute/option/modifiers/transform_modifier.rb +0 -112
  89. data/lib/treaty/attribute/option/registry.rb +0 -155
  90. data/lib/treaty/attribute/option/registry_initializer.rb +0 -115
  91. data/lib/treaty/attribute/option/validators/format_validator.rb +0 -220
  92. data/lib/treaty/attribute/option/validators/inclusion_validator.rb +0 -92
  93. data/lib/treaty/attribute/option/validators/required_validator.rb +0 -98
  94. data/lib/treaty/attribute/option/validators/type_validator.rb +0 -217
  95. data/lib/treaty/attribute/option_normalizer.rb +0 -166
  96. data/lib/treaty/attribute/option_orchestrator.rb +0 -190
  97. data/lib/treaty/attribute/validation/attribute_validator.rb +0 -145
  98. data/lib/treaty/attribute/validation/base.rb +0 -74
  99. data/lib/treaty/attribute/validation/nested_array_validator.rb +0 -205
  100. data/lib/treaty/attribute/validation/nested_object_validator.rb +0 -103
  101. data/lib/treaty/attribute/validation/nested_transformer.rb +0 -430
  102. data/lib/treaty/attribute/validation/orchestrator/base.rb +0 -260
  103. data/lib/treaty/base.rb +0 -9
  104. data/lib/treaty/context/callable.rb +0 -26
  105. data/lib/treaty/context/dsl.rb +0 -12
  106. data/lib/treaty/context/workspace.rb +0 -32
  107. data/lib/treaty/executor/inventory.rb +0 -122
  108. data/lib/treaty/inventory/collection.rb +0 -71
  109. data/lib/treaty/inventory/factory.rb +0 -91
  110. data/lib/treaty/inventory/inventory.rb +0 -92
  111. data/lib/treaty/request/attribute/attribute.rb +0 -25
  112. data/lib/treaty/request/attribute/builder.rb +0 -46
  113. data/lib/treaty/request/entity.rb +0 -33
  114. data/lib/treaty/request/factory.rb +0 -81
  115. data/lib/treaty/request/validator.rb +0 -60
  116. data/lib/treaty/response/attribute/attribute.rb +0 -25
  117. data/lib/treaty/response/attribute/builder.rb +0 -46
  118. data/lib/treaty/response/entity.rb +0 -33
  119. data/lib/treaty/response/factory.rb +0 -87
  120. data/lib/treaty/response/validator.rb +0 -53
  121. data/lib/treaty/result.rb +0 -23
  122. data/lib/treaty/versions/collection.rb +0 -15
  123. data/lib/treaty/versions/dsl.rb +0 -42
  124. data/lib/treaty/versions/execution/request.rb +0 -177
  125. data/lib/treaty/versions/executor.rb +0 -14
  126. data/lib/treaty/versions/factory.rb +0 -112
  127. data/lib/treaty/versions/resolver.rb +0 -70
  128. data/lib/treaty/versions/semantic.rb +0 -22
  129. data/lib/treaty/versions/workspace.rb +0 -43
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Context
5
- module Callable
6
- def call!(version:, params:, context: nil, inventory: nil)
7
- treaty_instance = send(:new)
8
-
9
- _call!(treaty_instance, context:, inventory:, version:, params:)
10
- end
11
-
12
- private
13
-
14
- def _call!(treaty_instance, context:, inventory:, version:, params:)
15
- treaty_instance.send(
16
- :_call!,
17
- context:,
18
- inventory:,
19
- version:,
20
- params:,
21
- collection_of_versions:
22
- )
23
- end
24
- end
25
- end
26
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Context
5
- module DSL
6
- def self.included(base)
7
- base.extend(Callable)
8
- base.include(Workspace)
9
- end
10
- end
11
- end
12
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Context
5
- module Workspace
6
- private
7
-
8
- def _call!(
9
- context:,
10
- inventory:,
11
- version:,
12
- params:,
13
- collection_of_versions:
14
- )
15
- call!(
16
- context:,
17
- inventory:,
18
- version:,
19
- params:,
20
- collection_of_versions:
21
- )
22
- end
23
-
24
- def call!(
25
- collection_of_versions:,
26
- **
27
- )
28
- @collection_of_versions = collection_of_versions
29
- end
30
- end
31
- end
32
- end
@@ -1,122 +0,0 @@
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
@@ -1,71 +0,0 @@
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
@@ -1,91 +0,0 @@
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
@@ -1,92 +0,0 @@
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
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- module Attribute
6
- # Request-specific attribute that defaults to required: true
7
- class Attribute < Treaty::Attribute::Base
8
- private
9
-
10
- def apply_defaults!
11
- # For request: required by default (true).
12
- # message: nil means use I18n default message from validators
13
- @options[:required] ||= { is: true, message: nil }
14
- end
15
-
16
- def process_nested_attributes(&block)
17
- return unless object_or_array?
18
-
19
- builder = Builder.new(collection_of_attributes, @nesting_level + 1)
20
- builder.instance_eval(&block)
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- module Attribute
6
- # Request-specific attribute builder
7
- class Builder < Treaty::Attribute::Builder::Base
8
- private
9
-
10
- def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
11
- Attribute.new(
12
- name,
13
- type,
14
- *helpers,
15
- nesting_level:,
16
- **options,
17
- &block
18
- )
19
- end
20
-
21
- # Deep copies an attribute with adjusted nesting level for Request context.
22
- #
23
- # @param source_attribute [Treaty::Attribute::Base] Attribute to copy
24
- # @param new_nesting_level [Integer] New nesting level
25
- # @return [Request::Attribute::Attribute] Copied attribute
26
- def deep_copy_attribute(source_attribute, new_nesting_level) # rubocop:disable Metrics/MethodLength
27
- copied = Attribute.new(
28
- source_attribute.name,
29
- source_attribute.type,
30
- nesting_level: new_nesting_level,
31
- **deep_copy_options(source_attribute.options)
32
- )
33
-
34
- return copied unless source_attribute.nested?
35
-
36
- source_attribute.collection_of_attributes.each do |nested_source|
37
- nested_copied = deep_copy_attribute(nested_source, new_nesting_level + 1)
38
- copied.collection_of_attributes << nested_copied
39
- end
40
-
41
- copied
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- # Entity class for request definitions.
6
- # Attributes are required by default.
7
- #
8
- # This class is used internally when defining request blocks.
9
- # When you write a request block, Treaty creates an anonymous
10
- # class based on Request::Entity.
11
- class Entity
12
- include Treaty::Attribute::DSL
13
-
14
- class << self
15
- private
16
-
17
- # Creates a Request::Attribute::Attribute for this Request::Entity class
18
- #
19
- # @return [Request::Attribute::Attribute] Created attribute instance
20
- def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
21
- Attribute::Attribute.new(
22
- name,
23
- type,
24
- *helpers,
25
- nesting_level:,
26
- **options,
27
- &block
28
- )
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- # Factory for creating request definitions.
6
- #
7
- # Supports two modes:
8
- # 1. Block mode: Creates an anonymous Request::Entity class with the block
9
- # 2. Entity mode: Uses a provided Entity class directly
10
- #
11
- # ## Block Mode
12
- #
13
- # ```ruby
14
- # request do
15
- # object :post do
16
- # string :title
17
- # end
18
- # end
19
- # ```
20
- #
21
- # ## Entity Mode
22
- #
23
- # ```ruby
24
- # request Posts::Create::RequestEntity
25
- # ```
26
- class Factory
27
- # Uses a provided Entity class
28
- #
29
- # @param entity_class [Class] Entity class to use
30
- # @return [void]
31
- # @raise [Treaty::Exceptions::Validation] if entity_class is not a valid Treaty::Entity subclass
32
- def use_entity(entity_class)
33
- validate_entity_class!(entity_class)
34
- @entity_class = entity_class
35
- end
36
-
37
- # Returns collection of attributes from the entity class
38
- #
39
- # @return [Collection] Collection of attributes
40
- def collection_of_attributes
41
- return Treaty::Attribute::Collection.new if @entity_class.nil?
42
-
43
- @entity_class.collection_of_attributes
44
- end
45
-
46
- # Handles DSL methods for defining attributes
47
- #
48
- # This allows the factory to be used with method_missing
49
- # for backwards compatibility with direct method calls.
50
- # Creates an anonymous Request::Entity class on first use.
51
- def method_missing(type, *helpers, **options, &block)
52
- # If no entity class yet, create one
53
- @entity_class ||= Class.new(Entity)
54
-
55
- # Call the method on the entity class
56
- @entity_class.public_send(type, *helpers, **options, &block)
57
- end
58
-
59
- def respond_to_missing?(name, *)
60
- super
61
- end
62
-
63
- private
64
-
65
- # Validates that the provided entity_class is a valid Treaty::Entity subclass
66
- #
67
- # @param entity_class [Class] Entity class to validate
68
- # @raise [Treaty::Exceptions::Validation] if entity_class is not a valid Treaty::Entity subclass
69
- def validate_entity_class!(entity_class)
70
- return if entity_class.is_a?(Class) && entity_class < Treaty::Entity
71
-
72
- raise Treaty::Exceptions::Validation,
73
- I18n.t(
74
- "treaty.request.factory.invalid_entity_class",
75
- type: entity_class.class,
76
- value: entity_class
77
- )
78
- end
79
- end
80
- end
81
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Treaty
4
- module Request
5
- # Validator for request data
6
- class Validator
7
- class << self
8
- # Validates request parameters against the request definition
9
- #
10
- # @param params [Hash] Request parameters to validate
11
- # @param version_factory [Versions::Factory] Version factory with request definition
12
- # @return [Hash] Validated and transformed parameters
13
- def validate!(params:, version_factory:)
14
- new(params:, version_factory:).validate!
15
- end
16
- end
17
-
18
- def initialize(params:, version_factory:)
19
- @params = params
20
- @version_factory = version_factory
21
- end
22
-
23
- def validate!
24
- validate_request_attributes!
25
- end
26
-
27
- private
28
-
29
- def request_data
30
- @request_data ||= begin
31
- @params.to_unsafe_h
32
- rescue NoMethodError
33
- @params
34
- end
35
- end
36
-
37
- def validate_request_attributes!
38
- return request_data unless request_attributes_exist?
39
-
40
- # Validate request attributes with orchestrator:
41
- orchestrator_class = Class.new(Treaty::Attribute::Validation::Orchestrator::Base) do
42
- define_method(:collection_of_attributes) do
43
- @version_factory.request_factory.collection_of_attributes
44
- end
45
- end
46
-
47
- orchestrator_class.validate!(
48
- version_factory: @version_factory,
49
- data: request_data
50
- )
51
- end
52
-
53
- def request_attributes_exist?
54
- return false if @version_factory.request_factory&.collection_of_attributes&.empty?
55
-
56
- @version_factory.request_factory.collection_of_attributes.exists?
57
- end
58
- end
59
- end
60
- end