treaty 0.19.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/treaty/action/base.rb +11 -0
  4. data/lib/treaty/action/context/callable.rb +90 -0
  5. data/lib/treaty/action/context/dsl.rb +56 -0
  6. data/lib/treaty/action/context/workspace.rb +92 -0
  7. data/lib/treaty/action/executor/inventory.rb +136 -0
  8. data/lib/treaty/{info/rest → action/info}/builder.rb +2 -2
  9. data/lib/treaty/{info/rest → action/info}/dsl.rb +2 -2
  10. data/lib/treaty/{info/rest → action/info}/result.rb +2 -2
  11. data/lib/treaty/action/inventory/collection.rb +77 -0
  12. data/lib/treaty/action/inventory/factory.rb +108 -0
  13. data/lib/treaty/action/inventory/inventory.rb +146 -0
  14. data/lib/treaty/action/request/attribute/attribute.rb +76 -0
  15. data/lib/treaty/action/request/attribute/builder.rb +98 -0
  16. data/lib/treaty/action/request/entity.rb +78 -0
  17. data/lib/treaty/action/request/factory.rb +116 -0
  18. data/lib/treaty/action/request/validator.rb +120 -0
  19. data/lib/treaty/action/response/attribute/attribute.rb +79 -0
  20. data/lib/treaty/action/response/attribute/builder.rb +96 -0
  21. data/lib/treaty/action/response/entity.rb +79 -0
  22. data/lib/treaty/action/response/factory.rb +129 -0
  23. data/lib/treaty/action/response/validator.rb +111 -0
  24. data/lib/treaty/action/result.rb +81 -0
  25. data/lib/treaty/action/versions/collection.rb +47 -0
  26. data/lib/treaty/action/versions/dsl.rb +116 -0
  27. data/lib/treaty/action/versions/execution/request.rb +287 -0
  28. data/lib/treaty/action/versions/executor.rb +61 -0
  29. data/lib/treaty/action/versions/factory.rb +253 -0
  30. data/lib/treaty/action/versions/resolver.rb +150 -0
  31. data/lib/treaty/action/versions/semantic.rb +64 -0
  32. data/lib/treaty/action/versions/workspace.rb +106 -0
  33. data/lib/treaty/action.rb +31 -0
  34. data/lib/treaty/controller/dsl.rb +1 -1
  35. data/lib/treaty/entity/attribute/base.rb +1 -1
  36. data/lib/treaty/entity/attribute/builder/base.rb +1 -1
  37. data/lib/treaty/entity/attribute/dsl.rb +1 -1
  38. data/lib/treaty/entity/base.rb +1 -1
  39. data/lib/treaty/entity/builder.rb +62 -5
  40. data/lib/treaty/version.rb +1 -1
  41. metadata +32 -31
  42. data/lib/treaty/base.rb +0 -9
  43. data/lib/treaty/context/callable.rb +0 -26
  44. data/lib/treaty/context/dsl.rb +0 -12
  45. data/lib/treaty/context/workspace.rb +0 -32
  46. data/lib/treaty/executor/inventory.rb +0 -122
  47. data/lib/treaty/inventory/collection.rb +0 -71
  48. data/lib/treaty/inventory/factory.rb +0 -91
  49. data/lib/treaty/inventory/inventory.rb +0 -92
  50. data/lib/treaty/request/attribute/attribute.rb +0 -25
  51. data/lib/treaty/request/attribute/builder.rb +0 -46
  52. data/lib/treaty/request/entity.rb +0 -33
  53. data/lib/treaty/request/factory.rb +0 -81
  54. data/lib/treaty/request/validator.rb +0 -60
  55. data/lib/treaty/response/attribute/attribute.rb +0 -25
  56. data/lib/treaty/response/attribute/builder.rb +0 -46
  57. data/lib/treaty/response/entity.rb +0 -33
  58. data/lib/treaty/response/factory.rb +0 -87
  59. data/lib/treaty/response/validator.rb +0 -53
  60. data/lib/treaty/result.rb +0 -23
  61. data/lib/treaty/versions/collection.rb +0 -15
  62. data/lib/treaty/versions/dsl.rb +0 -42
  63. data/lib/treaty/versions/execution/request.rb +0 -177
  64. data/lib/treaty/versions/executor.rb +0 -14
  65. data/lib/treaty/versions/factory.rb +0 -112
  66. data/lib/treaty/versions/resolver.rb +0 -70
  67. data/lib/treaty/versions/semantic.rb +0 -22
  68. data/lib/treaty/versions/workspace.rb +0 -43
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ module Response
6
+ module Attribute
7
+ # Response-specific attribute definition.
8
+ #
9
+ # ## Purpose
10
+ #
11
+ # Extends Entity::Attribute::Base with Response-specific behavior.
12
+ # Key difference: attributes are **optional by default**.
13
+ #
14
+ # ## Default Behavior
15
+ #
16
+ # Unlike Request attributes (required by default), Response attributes
17
+ # default to `required: false`. This allows flexible response structures
18
+ # where not all fields must be present.
19
+ #
20
+ # ## Usage
21
+ #
22
+ # Created internally by:
23
+ # - Response::Attribute::Builder (when defining nested attributes)
24
+ # - Response::Entity (when defining top-level attributes)
25
+ #
26
+ # ## Nesting
27
+ #
28
+ # Object and array types create nested builders:
29
+ #
30
+ # response 200 do
31
+ # object :post do # Creates Attribute with nested builder
32
+ # string :title # Nested attribute (optional by default)
33
+ # array :comments do # Nested array
34
+ # object :_self do # Array element definition
35
+ # string :text
36
+ # end
37
+ # end
38
+ # end
39
+ # end
40
+ #
41
+ # ## Example
42
+ #
43
+ # # These are equivalent:
44
+ # string :title # optional by default
45
+ # string :title, :optional # explicit optional
46
+ #
47
+ # # Must explicitly mark required:
48
+ # string :id, :required
49
+ class Attribute < Treaty::Entity::Attribute::Base
50
+ private
51
+
52
+ # Sets default required behavior for response attributes
53
+ #
54
+ # Response attributes are optional by default (is: false).
55
+ # This can be overridden with `:required` helper or `required: true`.
56
+ #
57
+ # @return [void]
58
+ def apply_defaults!
59
+ @options[:required] ||= { is: false, message: nil }
60
+ end
61
+
62
+ # Creates nested builder for object/array type processing
63
+ #
64
+ # When a block is given to object or array attributes,
65
+ # creates a Builder to process the nested attribute definitions.
66
+ #
67
+ # @param block [Proc] Block containing nested attribute definitions
68
+ # @return [void]
69
+ def process_nested_attributes(&block)
70
+ return unless object_or_array?
71
+
72
+ builder = Builder.new(collection_of_attributes, @nesting_level + 1)
73
+ builder.instance_eval(&block)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ module Response
6
+ module Attribute
7
+ # DSL builder for defining response attributes.
8
+ #
9
+ # ## Purpose
10
+ #
11
+ # Provides Response-specific implementation of the attribute builder.
12
+ # Creates Response::Attribute::Attribute instances instead of generic ones.
13
+ #
14
+ # ## Inheritance
15
+ #
16
+ # Extends Treaty::Entity::Attribute::Builder::Base which provides:
17
+ # - DSL interface (string, integer, object, array, etc.)
18
+ # - method_missing magic for type-based method calls
19
+ # - Helper support (:required, :optional)
20
+ # - Entity reuse via use_entity
21
+ #
22
+ # ## Usage
23
+ #
24
+ # Used internally by:
25
+ # - Response::Attribute::Attribute (when processing nested object/array blocks)
26
+ #
27
+ # ## DSL Example
28
+ #
29
+ # response 201 do
30
+ # object :post do
31
+ # string :id
32
+ # string :title
33
+ # datetime :created_at
34
+ # end
35
+ # end
36
+ #
37
+ # ## Methods
38
+ #
39
+ # Implements abstract methods from base class:
40
+ # - `create_attribute` - Creates Response::Attribute::Attribute
41
+ # - `deep_copy_attribute` - Deep copies attribute for use_entity support
42
+ class Builder < Treaty::Entity::Attribute::Builder::Base
43
+ private
44
+
45
+ # Creates a new response attribute instance
46
+ #
47
+ # Called by base class when defining attributes via DSL.
48
+ #
49
+ # @param name [Symbol] Attribute name
50
+ # @param type [Symbol] Attribute type (:string, :integer, :object, etc.)
51
+ # @param helpers [Array<Symbol>] Helper symbols (:required, :optional)
52
+ # @param nesting_level [Integer] Current nesting depth
53
+ # @param options [Hash] Attribute options (default:, format:, etc.)
54
+ # @param block [Proc] Block for nested attributes (object/array)
55
+ # @return [Treaty::Action::Response::Attribute::Attribute] Created attribute instance
56
+ def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
57
+ Attribute.new(
58
+ name,
59
+ type,
60
+ *helpers,
61
+ nesting_level:,
62
+ **options,
63
+ &block
64
+ )
65
+ end
66
+
67
+ # Deep copies an attribute with adjusted nesting level
68
+ #
69
+ # Used when copying attributes from Entity classes via use_entity.
70
+ # Recursively copies nested attributes for object/array types.
71
+ #
72
+ # @param source_attribute [Treaty::Entity::Attribute::Base] Source attribute to copy
73
+ # @param new_nesting_level [Integer] Nesting level for copied attribute
74
+ # @return [Treaty::Action::Response::Attribute::Attribute] Deep copied attribute
75
+ def deep_copy_attribute(source_attribute, new_nesting_level) # rubocop:disable Metrics/MethodLength
76
+ copied = Attribute.new(
77
+ source_attribute.name,
78
+ source_attribute.type,
79
+ nesting_level: new_nesting_level,
80
+ **deep_copy_options(source_attribute.options)
81
+ )
82
+
83
+ return copied unless source_attribute.nested?
84
+
85
+ source_attribute.collection_of_attributes.each do |nested_source|
86
+ nested_copied = deep_copy_attribute(nested_source, new_nesting_level + 1)
87
+ copied.collection_of_attributes << nested_copied
88
+ end
89
+
90
+ copied
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ module Response
6
+ # Internal entity class for response attribute definitions.
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Provides DSL interface for defining response attributes when using
11
+ # inline block syntax in treaty definitions. Serves as the anonymous
12
+ # class base when `response STATUS do ... end` is used.
13
+ #
14
+ # ## Usage
15
+ #
16
+ # Created internally by:
17
+ # - Response::Factory (when using inline DSL blocks)
18
+ #
19
+ # ## DSL Interface
20
+ #
21
+ # Includes Treaty::Entity::Attribute::DSL which provides:
22
+ # - Type methods: string, integer, boolean, date, time, datetime
23
+ # - Structure methods: object, array
24
+ # - Helper support: :required, :optional
25
+ #
26
+ # ## Difference from Treaty::Entity::Base
27
+ #
28
+ # While Treaty::Entity::Base creates standalone entity classes,
29
+ # Response::Entity creates Response-specific attributes with:
30
+ # - Optional by default behavior
31
+ # - Response::Attribute::Attribute instances
32
+ #
33
+ # ## Example
34
+ #
35
+ # # When you write:
36
+ # version 1 do
37
+ # response 201 do
38
+ # object :post do
39
+ # string :id
40
+ # string :title
41
+ # end
42
+ # end
43
+ # end
44
+ #
45
+ # # Factory creates: Class.new(Response::Entity)
46
+ # # and calls string, object etc. on it
47
+ class Entity
48
+ include Treaty::Entity::Attribute::DSL
49
+
50
+ class << self
51
+ private
52
+
53
+ # Creates response-specific attribute instances
54
+ #
55
+ # Called by DSL methods (string, integer, etc.) to create
56
+ # Response::Attribute::Attribute instead of generic attributes.
57
+ #
58
+ # @param name [Symbol] Attribute name
59
+ # @param type [Symbol] Attribute type
60
+ # @param helpers [Array<Symbol>] Helper symbols (:required, :optional)
61
+ # @param nesting_level [Integer] Current nesting depth
62
+ # @param options [Hash] Attribute options
63
+ # @param block [Proc] Block for nested attributes
64
+ # @return [Treaty::Action::Response::Attribute::Attribute] Created attribute
65
+ def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
66
+ Attribute::Attribute.new(
67
+ name,
68
+ type,
69
+ *helpers,
70
+ nesting_level:,
71
+ **options,
72
+ &block
73
+ )
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ module Response
6
+ # Factory for creating response attribute collections.
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Captures response attribute definitions from treaty DSL and provides
11
+ # access to the resulting attribute collection. Supports both inline
12
+ # block syntax and Entity class references.
13
+ #
14
+ # ## Difference from Request::Factory
15
+ #
16
+ # Response::Factory stores HTTP status code along with attributes.
17
+ # This allows treaty to validate responses against expected status.
18
+ #
19
+ # ## Usage
20
+ #
21
+ # Created internally by:
22
+ # - Versions::Factory (when `response STATUS do ... end` is called)
23
+ #
24
+ # Consumed by:
25
+ # - Response::Validator (to validate service output)
26
+ # - Info::Builder (to build response schema information)
27
+ # - Versions::Execution::Base (to set response status)
28
+ #
29
+ # ## Definition Modes
30
+ #
31
+ # ### Inline Block Mode
32
+ #
33
+ # response 201 do
34
+ # object :post do
35
+ # string :id
36
+ # string :title
37
+ # end
38
+ # end
39
+ #
40
+ # ### Entity Class Mode
41
+ #
42
+ # response 201, Posts::Create::ResponseEntity
43
+ #
44
+ # ## Example
45
+ #
46
+ # factory = Response::Factory.new(201)
47
+ # factory.status # => 201
48
+ # factory.object :post do
49
+ # factory.string :id
50
+ # end
51
+ # factory.collection_of_attributes # => Collection with post attribute
52
+ class Factory
53
+ # @return [Integer] HTTP status code for this response
54
+ attr_reader :status
55
+
56
+ # Creates new response factory with HTTP status
57
+ #
58
+ # @param status [Integer] HTTP status code (200, 201, 404, etc.)
59
+ def initialize(status)
60
+ @status = status
61
+ end
62
+
63
+ # Registers an Entity class for response schema
64
+ #
65
+ # Use this to reference a pre-defined Entity class instead of
66
+ # inline attribute definitions.
67
+ #
68
+ # @param entity_class [Class] Must be Treaty::Entity::Base subclass
69
+ # @raise [Treaty::Exceptions::Validation] If entity_class is invalid
70
+ # @return [void]
71
+ def use_entity(entity_class)
72
+ validate_entity_class!(entity_class)
73
+ @entity_class = entity_class
74
+ end
75
+
76
+ # Returns the collection of defined attributes
77
+ #
78
+ # @return [Treaty::Entity::Attribute::Collection] Attribute collection
79
+ def collection_of_attributes
80
+ return Treaty::Entity::Attribute::Collection.new if @entity_class.nil?
81
+
82
+ @entity_class.collection_of_attributes
83
+ end
84
+
85
+ # Forwards DSL method calls to internal Entity class
86
+ #
87
+ # Creates an anonymous Entity class on first call, then forwards
88
+ # all DSL methods (string, integer, object, etc.) to it.
89
+ #
90
+ # @param type [Symbol] Attribute type (method name)
91
+ # @param helpers [Array] Helper symbols and arguments
92
+ # @param options [Hash] Attribute options
93
+ # @param block [Proc] Block for nested attributes
94
+ # @return [void]
95
+ def method_missing(type, *helpers, **options, &block)
96
+ @entity_class ||= Class.new(Entity)
97
+
98
+ @entity_class.public_send(type, *helpers, **options, &block)
99
+ end
100
+
101
+ # Checks if method should be handled by method_missing
102
+ #
103
+ # @param name [Symbol] Method name
104
+ # @return [Boolean]
105
+ def respond_to_missing?(name, *)
106
+ super
107
+ end
108
+
109
+ private
110
+
111
+ # Validates that entity_class is a Treaty::Entity::Base subclass
112
+ #
113
+ # @param entity_class [Class] Class to validate
114
+ # @raise [Treaty::Exceptions::Validation] If validation fails
115
+ # @return [void]
116
+ def validate_entity_class!(entity_class)
117
+ return if entity_class.is_a?(Class) && entity_class < Treaty::Entity::Base
118
+
119
+ raise Treaty::Exceptions::Validation,
120
+ I18n.t(
121
+ "treaty.response.factory.invalid_entity_class",
122
+ type: entity_class.class,
123
+ value: entity_class
124
+ )
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ module Response
6
+ # Validates service response data against schema.
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Validates response data from service execution against the response
11
+ # schema defined in the treaty version. Ensures service output matches
12
+ # the documented API contract.
13
+ #
14
+ # ## Usage
15
+ #
16
+ # Called internally by:
17
+ # - Versions::Execution::Response (after service execution)
18
+ #
19
+ # ## Validation Flow
20
+ #
21
+ # 1. Check if response schema is defined
22
+ # 2. Create dynamic Orchestrator with version's response attributes
23
+ # 3. Run validation pipeline (validate + transform)
24
+ # 4. Return validated/transformed data or raise error
25
+ #
26
+ # ## Error Handling
27
+ #
28
+ # Raises Treaty::Exceptions::Validation with detailed messages
29
+ # including attribute path and specific validation failures.
30
+ #
31
+ # ## Difference from Request::Validator
32
+ #
33
+ # - Validates service output, not controller params
34
+ # - Response attributes are optional by default
35
+ # - Used after service execution, not before
36
+ #
37
+ # ## Example
38
+ #
39
+ # # Typically called via class method:
40
+ # validated = Response::Validator.validate!(
41
+ # version_factory: version_factory,
42
+ # response_data: service_result
43
+ # )
44
+ #
45
+ # # validated contains transformed response for client
46
+ class Validator
47
+ class << self
48
+ # Validates response data
49
+ #
50
+ # @param version_factory [Treaty::Action::Versions::Factory] Version with response schema
51
+ # @param response_data [Hash] Response data from service
52
+ # @return [Hash] Validated and transformed response
53
+ # @raise [Treaty::Exceptions::Validation] If validation fails
54
+ def validate!(version_factory:, response_data: {})
55
+ new(version_factory:, response_data:).validate!
56
+ end
57
+ end
58
+
59
+ # Creates new validator instance
60
+ #
61
+ # @param version_factory [Treaty::Action::Versions::Factory] Version with response schema
62
+ # @param response_data [Hash] Response data to validate
63
+ def initialize(version_factory:, response_data: {})
64
+ @version_factory = version_factory
65
+ @response_data = response_data
66
+ end
67
+
68
+ # Runs validation pipeline
69
+ #
70
+ # @return [Hash] Validated and transformed response
71
+ # @raise [Treaty::Exceptions::Validation] If validation fails
72
+ def validate!
73
+ validate_response_attributes!
74
+ end
75
+
76
+ private
77
+
78
+ # Validates and transforms response data
79
+ #
80
+ # Creates dynamic Orchestrator class that uses version's response
81
+ # attributes for validation. Returns raw data if no schema defined.
82
+ #
83
+ # @return [Hash] Validated and transformed data
84
+ # @raise [Treaty::Exceptions::Validation] If validation fails
85
+ def validate_response_attributes!
86
+ return @response_data unless response_attributes_exist?
87
+
88
+ orchestrator_class = Class.new(Treaty::Entity::Attribute::Validation::Orchestrator::Base) do
89
+ define_method(:collection_of_attributes) do
90
+ @version_factory.response_factory.collection_of_attributes
91
+ end
92
+ end
93
+
94
+ orchestrator_class.validate!(
95
+ version_factory: @version_factory,
96
+ data: @response_data
97
+ )
98
+ end
99
+
100
+ # Checks if response schema is defined for this version
101
+ #
102
+ # @return [Boolean] True if response attributes exist
103
+ def response_attributes_exist?
104
+ return false if @version_factory.response_factory&.collection_of_attributes&.empty?
105
+
106
+ @version_factory.response_factory.collection_of_attributes.exists?
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ # Value object returned from treaty execution.
6
+ #
7
+ # ## Purpose
8
+ #
9
+ # Encapsulates the result of a treaty call, containing validated
10
+ # response data, HTTP status code, and resolved version. This is
11
+ # the primary return type from `Treaty.call!`.
12
+ #
13
+ # ## Usage
14
+ #
15
+ # Created by:
16
+ # - Versions::Workspace (at the end of treaty execution)
17
+ #
18
+ # Consumed by:
19
+ # - Controller::DSL (to render JSON response)
20
+ # - Test assertions (to verify treaty behavior)
21
+ #
22
+ # ## Attributes
23
+ #
24
+ # | Attribute | Description |
25
+ # |-----------|-------------|
26
+ # | `data` | Validated and transformed response hash |
27
+ # | `status` | HTTP status code from response definition |
28
+ # | `version` | Resolved Gem::Version object |
29
+ #
30
+ # ## Example
31
+ #
32
+ # result = Posts::CreateTreaty.call!(
33
+ # version: "1",
34
+ # params: { post: { title: "Hello" } }
35
+ # )
36
+ #
37
+ # result.data # => { post: { id: "abc", title: "Hello" } }
38
+ # result.status # => 201
39
+ # result.version # => Gem::Version.new("1")
40
+ #
41
+ # # In controller:
42
+ # render json: result.data, status: result.status
43
+ class Result
44
+ # @return [Hash] Validated response data
45
+ attr_reader :data
46
+
47
+ # @return [Integer] HTTP status code
48
+ attr_reader :status
49
+
50
+ # @return [Gem::Version] Resolved API version
51
+ attr_reader :version
52
+
53
+ # Creates a new result instance
54
+ #
55
+ # @param data [Hash] Validated response data
56
+ # @param status [Integer] HTTP status code
57
+ # @param version [Gem::Version] Resolved version
58
+ def initialize(data:, status:, version:)
59
+ @data = data
60
+ @status = status
61
+ @version = version
62
+ end
63
+
64
+ # Returns human-readable representation for debugging
65
+ #
66
+ # @return [String] Inspection string with all attributes
67
+ def inspect
68
+ "#<#{self.class.name} #{draw_result}>"
69
+ end
70
+
71
+ private
72
+
73
+ # Formats attributes for inspect output
74
+ #
75
+ # @return [String] Formatted attribute string
76
+ def draw_result
77
+ "@data=#{@data.inspect}, @status=#{@status.inspect}, @version=#{@version.inspect}"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Treaty
4
+ module Action
5
+ module Versions
6
+ # Collection wrapper for sets of version factories.
7
+ #
8
+ # ## Purpose
9
+ #
10
+ # Provides a unified interface for working with collections of version factories.
11
+ # Uses Ruby Set internally for uniqueness but exposes Array-like interface.
12
+ #
13
+ # ## Usage
14
+ #
15
+ # Used internally by:
16
+ # - Versions::DSL (to store version factories)
17
+ # - Versions::Resolver (to find matching version)
18
+ # - Info::Builder (to build version information)
19
+ #
20
+ # ## Methods
21
+ #
22
+ # Delegates common collection methods to internal Set:
23
+ # - `<<` - Add version factory
24
+ # - `map` - Iteration with transformation
25
+ # - `find` - Access by condition
26
+ #
27
+ # ## Example
28
+ #
29
+ # collection = Collection.new
30
+ # collection << Factory.new(version: 1, default: true)
31
+ # collection << Factory.new(version: 2, default: false)
32
+ # collection.find { |f| f.default_result } # => Factory(v1)
33
+ class Collection
34
+ extend Forwardable
35
+
36
+ def_delegators :@collection, :<<, :map, :find
37
+
38
+ # Creates a new collection instance
39
+ #
40
+ # @param collection [Set] Initial collection (default: empty Set)
41
+ def initialize(collection = Set.new)
42
+ @collection = collection
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end