domainic-type 0.1.0.alpha.2.1.0 → 0.1.0.alpha.3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/LICENSE +1 -1
  4. data/README.md +28 -4
  5. data/lib/domainic/type/accessors.rb +41 -0
  6. data/lib/domainic/type/behavior/enumerable_behavior.rb +262 -0
  7. data/lib/domainic/type/behavior/numeric_behavior.rb +340 -0
  8. data/lib/domainic/type/behavior/sizable_behavior.rb +246 -0
  9. data/lib/domainic/type/behavior/string_behavior.rb +379 -0
  10. data/lib/domainic/type/behavior.rb +239 -0
  11. data/lib/domainic/type/config/registry.yml +101 -0
  12. data/lib/domainic/type/constraint/behavior.rb +342 -0
  13. data/lib/domainic/type/constraint/constraints/all_constraint.rb +81 -0
  14. data/lib/domainic/type/constraint/constraints/and_constraint.rb +105 -0
  15. data/lib/domainic/type/constraint/constraints/any_constraint.rb +83 -0
  16. data/lib/domainic/type/constraint/constraints/case_constraint.rb +104 -0
  17. data/lib/domainic/type/constraint/constraints/character_set_constraint.rb +111 -0
  18. data/lib/domainic/type/constraint/constraints/divisibility_constraint.rb +126 -0
  19. data/lib/domainic/type/constraint/constraints/emptiness_constraint.rb +69 -0
  20. data/lib/domainic/type/constraint/constraints/equality_constraint.rb +75 -0
  21. data/lib/domainic/type/constraint/constraints/finiteness_constraint.rb +123 -0
  22. data/lib/domainic/type/constraint/constraints/inclusion_constraint.rb +74 -0
  23. data/lib/domainic/type/constraint/constraints/match_pattern_constraint.rb +87 -0
  24. data/lib/domainic/type/constraint/constraints/method_presence_constraint.rb +72 -0
  25. data/lib/domainic/type/constraint/constraints/none_constraint.rb +83 -0
  26. data/lib/domainic/type/constraint/constraints/nor_constraint.rb +105 -0
  27. data/lib/domainic/type/constraint/constraints/not_constraint.rb +76 -0
  28. data/lib/domainic/type/constraint/constraints/or_constraint.rb +106 -0
  29. data/lib/domainic/type/constraint/constraints/ordering_constraint.rb +75 -0
  30. data/lib/domainic/type/constraint/constraints/parity_constraint.rb +102 -0
  31. data/lib/domainic/type/constraint/constraints/polarity_constraint.rb +147 -0
  32. data/lib/domainic/type/constraint/constraints/range_constraint.rb +135 -0
  33. data/lib/domainic/type/constraint/constraints/type_constraint.rb +110 -0
  34. data/lib/domainic/type/constraint/constraints/uniqueness_constraint.rb +69 -0
  35. data/lib/domainic/type/constraint/resolver.rb +172 -0
  36. data/lib/domainic/type/constraint/set.rb +266 -0
  37. data/lib/domainic/type/definitions.rb +364 -0
  38. data/lib/domainic/type/types/core/array_type.rb +48 -0
  39. data/lib/domainic/type/types/core/float_type.rb +39 -0
  40. data/lib/domainic/type/types/core/hash_type.rb +143 -0
  41. data/lib/domainic/type/types/core/integer_type.rb +38 -0
  42. data/lib/domainic/type/types/core/string_type.rb +51 -0
  43. data/lib/domainic/type/types/core/symbol_type.rb +51 -0
  44. data/lib/domainic/type/types/specification/anything_type.rb +22 -0
  45. data/lib/domainic/type/types/specification/duck_type.rb +55 -0
  46. data/lib/domainic/type/types/specification/enum_type.rb +26 -0
  47. data/lib/domainic/type/types/specification/union_type.rb +26 -0
  48. data/lib/domainic/type/types/specification/void_type.rb +12 -0
  49. data/lib/domainic/type.rb +7 -0
  50. data/lib/domainic-type.rb +3 -0
  51. data/sig/domainic/type/accessors.rbs +22 -0
  52. data/sig/domainic/type/behavior/enumerable_behavior.rbs +238 -0
  53. data/sig/domainic/type/behavior/numeric_behavior.rbs +299 -0
  54. data/sig/domainic/type/behavior/sizable_behavior.rbs +218 -0
  55. data/sig/domainic/type/behavior/string_behavior.rbs +315 -0
  56. data/sig/domainic/type/behavior.rbs +153 -0
  57. data/sig/domainic/type/constraint/behavior.rbs +258 -0
  58. data/sig/domainic/type/constraint/constraints/all_constraint.rbs +55 -0
  59. data/sig/domainic/type/constraint/constraints/and_constraint.rbs +72 -0
  60. data/sig/domainic/type/constraint/constraints/any_constraint.rbs +57 -0
  61. data/sig/domainic/type/constraint/constraints/case_constraint.rbs +73 -0
  62. data/sig/domainic/type/constraint/constraints/character_set_constraint.rbs +82 -0
  63. data/sig/domainic/type/constraint/constraints/divisibility_constraint.rbs +91 -0
  64. data/sig/domainic/type/constraint/constraints/emptiness_constraint.rbs +54 -0
  65. data/sig/domainic/type/constraint/constraints/equality_constraint.rbs +60 -0
  66. data/sig/domainic/type/constraint/constraints/finiteness_constraint.rbs +82 -0
  67. data/sig/domainic/type/constraint/constraints/inclusion_constraint.rbs +59 -0
  68. data/sig/domainic/type/constraint/constraints/match_pattern_constraint.rbs +66 -0
  69. data/sig/domainic/type/constraint/constraints/method_presence_constraint.rbs +51 -0
  70. data/sig/domainic/type/constraint/constraints/none_constraint.rbs +57 -0
  71. data/sig/domainic/type/constraint/constraints/nor_constraint.rbs +72 -0
  72. data/sig/domainic/type/constraint/constraints/not_constraint.rbs +56 -0
  73. data/sig/domainic/type/constraint/constraints/or_constraint.rbs +74 -0
  74. data/sig/domainic/type/constraint/constraints/ordering_constraint.rbs +60 -0
  75. data/sig/domainic/type/constraint/constraints/parity_constraint.rbs +71 -0
  76. data/sig/domainic/type/constraint/constraints/polarity_constraint.rbs +101 -0
  77. data/sig/domainic/type/constraint/constraints/range_constraint.rbs +88 -0
  78. data/sig/domainic/type/constraint/constraints/type_constraint.rbs +86 -0
  79. data/sig/domainic/type/constraint/constraints/uniqueness_constraint.rbs +54 -0
  80. data/sig/domainic/type/constraint/resolver.rbs +117 -0
  81. data/sig/domainic/type/constraint/set.rbs +159 -0
  82. data/sig/domainic/type/definitions.rbs +304 -0
  83. data/sig/domainic/type/types/core/array_type.rbs +42 -0
  84. data/sig/domainic/type/types/core/float_type.rbs +33 -0
  85. data/sig/domainic/type/types/core/hash_type.rbs +107 -0
  86. data/sig/domainic/type/types/core/integer_type.rbs +32 -0
  87. data/sig/domainic/type/types/core/string_type.rbs +45 -0
  88. data/sig/domainic/type/types/core/symbol_type.rbs +45 -0
  89. data/sig/domainic/type/types/specification/anything_type.rbs +14 -0
  90. data/sig/domainic/type/types/specification/duck_type.rbs +41 -0
  91. data/sig/domainic/type/types/specification/enum_type.rbs +14 -0
  92. data/sig/domainic/type/types/specification/union_type.rbs +14 -0
  93. data/sig/domainic/type/types/specification/void_type.rbs +8 -0
  94. data/sig/domainic/type.rbs +5 -0
  95. data/sig/domainic-type.rbs +1 -0
  96. data/sig/manifest.yaml +2 -0
  97. metadata +108 -71
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Domainic
6
+ module Type
7
+ module Constraint
8
+ # @rbs!
9
+ # interface _ConstraintClass
10
+ # def new: (Type::accessor accessor, ?untyped expectation, **untyped options) -> Behavior
11
+ # end
12
+
13
+ # A factory class responsible for dynamically loading and resolving constraint types.
14
+ #
15
+ # The Resolver handles the dynamic loading and instantiation of constraint classes based on
16
+ # their type symbols. It manages the conversion of constraint type symbols (like :string or
17
+ # :numeric) into their corresponding constraint classes (like StringConstraint or
18
+ # NumericConstraint).
19
+ #
20
+ # Key responsibilities:
21
+ # - Converting constraint type symbols into file paths
22
+ # - Loading constraint class files dynamically
23
+ # - Resolving constraint class constants
24
+ # - Providing clear error messages for unknown constraints
25
+ #
26
+ # @example Resolving a string constraint
27
+ # resolver = Resolver.new(:string)
28
+ # string_constraint_class = resolver.resolve! # => StringConstraint
29
+ #
30
+ # @example Resolving an unknown constraint
31
+ # resolver = Resolver.new(:unknown)
32
+ # resolver.resolve! # raises ArgumentError: Unknown constraint: unknown
33
+ #
34
+ # @author {https://aaronmallen.me Aaron Allen}
35
+ # @since 0.1.0
36
+ class Resolver
37
+ # @rbs!
38
+ # type registry_constraint = { constant: String, require_path: String }
39
+
40
+ # @rbs self.@registry: Hash[Symbol, Hash[Symbol, registry_constraint]]
41
+ # @rbs @constant_name: String
42
+ # @rbs @constraint_type: Symbol
43
+ # @rbs @file_name: String
44
+
45
+ class << self
46
+ # Register a new constraint with the resolver.
47
+ #
48
+ # @param lookup_key [String, Symbol] The lookup key for the constraint. This is how types should reference
49
+ # the constraint when constraining themselves.
50
+ # @param constant_name [String] The name of the constraint class constant.
51
+ # @param require_path [String] The path to the constraint class file.
52
+ #
53
+ # @raise [ArgumentError] if the constraint is already registered
54
+ # @return [void]
55
+ # @rbs (String | Symbol lookup_key, String constant_name, String require_path) -> void
56
+ def register_constraint(lookup_key, constant_name, require_path)
57
+ raise ArgumentError, "Constraint already registered: #{lookup_key}" if registry.key?(lookup_key.to_sym)
58
+
59
+ registry[lookup_key.to_sym] = { constant: constant_name, require_path: require_path }
60
+ end
61
+
62
+ # Resolve a constraint type to its corresponding class.
63
+ #
64
+ # This is a convenience method that creates a new Resolver instance and
65
+ # immediately resolves the constraint class.
66
+ #
67
+ # @param constraint_type [Symbol] The type of constraint to resolve
68
+ #
69
+ # @raise [ArgumentError] if the constraint type is unknown
70
+ # @return [Class] The resolved constraint class
71
+ # @rbs (Symbol constraint_type) -> _ConstraintClass
72
+ def resolve!(constraint_type)
73
+ new(constraint_type).resolve!
74
+ end
75
+
76
+ private
77
+
78
+ # The registry of known constraints
79
+ #
80
+ # @return [Hash{Symbol => Hash{Symbol => String}}] The constraint registry
81
+ # @rbs () -> Hash[Symbol, registry_constraint]
82
+ def registry
83
+ @registry ||= begin
84
+ config_file = File.expand_path('../config/registry.yml', File.dirname(__FILE__))
85
+ raw_config = YAML.load_file(config_file, symbolize_names: true)
86
+ raw_config[:constraints].transform_values do |value|
87
+ value.is_a?(Hash) ? value.transform_keys(&:to_sym) : value #: Hash[Symbol, registry_constraint]
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ # Initialize a new Resolver instance.
94
+ #
95
+ # @param constraint_type [Symbol] The type of constraint to resolve
96
+ #
97
+ # @return [void]
98
+ # @rbs (String | Symbol constraint_type) -> void
99
+ def initialize(constraint_type)
100
+ @constraint_type = constraint_type.to_sym
101
+ end
102
+
103
+ # Resolve the constraint type to its corresponding class.
104
+ #
105
+ # This method attempts to load and resolve the constraint class file based on
106
+ # the constraint type. The constraint class must be defined under the
107
+ # Domainic::Type::Constraint namespace and follow the naming convention:
108
+ # "{type}_constraint.rb".
109
+ #
110
+ # @example File naming convention
111
+ # :string -> string_constraint.rb -> StringConstraint
112
+ # :numeric -> numeric_constraint.rb -> NumericConstraint
113
+ #
114
+ # @raise [ArgumentError] if the constraint type is unknown
115
+ # @return [Class] The resolved constraint class
116
+ # @rbs () -> _ConstraintClass
117
+ def resolve!
118
+ load_constraint!
119
+ constraint_class
120
+ end
121
+
122
+ private
123
+
124
+ # Get the constraint class constant.
125
+ #
126
+ # Attempts to find the constraint class constant in the Domainic::Type::Constraint
127
+ # namespace.
128
+ #
129
+ # @raise [ArgumentError] if the constant cannot be found
130
+ # @return [Class] The constraint class constant
131
+ # @rbs () -> _ConstraintClass
132
+ def constraint_class
133
+ registry_constraint = registered
134
+ raise ArgumentError, "Unknown constraint: #{@constraint_type}" if registry_constraint.nil?
135
+
136
+ # @type var registry_constraint: registry_constraint
137
+ Object.const_get(registry_constraint[:constant])
138
+ rescue NameError
139
+ raise ArgumentError, "Unknown constraint: #{@constraint_type}"
140
+ end
141
+
142
+ # Load the constraint class file.
143
+ #
144
+ # Attempts to require the constraint class file from the constraints directory.
145
+ #
146
+ # @raise [ArgumentError] if the constraint file cannot be loaded
147
+ # @return [void]
148
+ # @rbs () -> void
149
+ def load_constraint!
150
+ registry_constraint = registered
151
+ raise ArgumentError, "Unknown constraint: #{@constraint_type}" if registry_constraint.nil?
152
+
153
+ # @type var registry_constraint: registry_constraint
154
+ require registry_constraint[:require_path]
155
+ rescue LoadError
156
+ raise ArgumentError, "Constraint require path doesn't exist: #{@constraint_type}"
157
+ end
158
+
159
+ # Fetch the registered constraint from the registry
160
+ #
161
+ # @return [Hash{Symbol => String}] The registered constraint
162
+ # @rbs () -> registry_constraint?
163
+ def registered
164
+ registry = self.class.send(:registry)
165
+ return unless registry.key?(@constraint_type)
166
+
167
+ registry[@constraint_type]
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'domainic/type/accessors'
4
+ require 'domainic/type/constraint/resolver'
5
+ require 'forwardable'
6
+
7
+ module Domainic
8
+ module Type
9
+ module Constraint
10
+ # A class managing collections of type constraints.
11
+ #
12
+ # The Set class provides a structured way to manage multiple constraints,
13
+ # organizing them by their accessor method and constraint name. It handles
14
+ # the creation, storage, and retrieval of constraints while maintaining
15
+ # their relationships and configuration.
16
+ #
17
+ # Key features:
18
+ # - Organized storage by accessor and constraint name
19
+ # - Dynamic constraint resolution and creation
20
+ # - Flexible constraint lookup and enumeration
21
+ # - Support for both symbol and string identifiers
22
+ #
23
+ # @example Creating and managing constraints
24
+ # set = Set.new
25
+ # set.add(:self, :string, 'being a')
26
+ # set.add(:length, :range, 'having length', minimum: 5)
27
+ #
28
+ # set.constraint?(:self, 'being a') # => true
29
+ # set.constraints # => [StringConstraint, RangeConstraint]
30
+ # set.count # => 2
31
+ #
32
+ # @author {https://aaronmallen.me Aaron Allen}
33
+ # @since 0.1.0
34
+ class Set
35
+ extend Forwardable
36
+
37
+ # @rbs @lookup: Hash[Type::accessor, Hash[Symbol, Behavior]]
38
+
39
+ # Initialize a new empty constraint set.
40
+ #
41
+ # @return [void]
42
+ # @rbs () -> void
43
+ def initialize
44
+ @lookup = Type::ACCESSORS.each_with_object({}) do |accessor, lookup|
45
+ lookup[accessor] = {}
46
+ end
47
+ end
48
+
49
+ # Add a new constraint to the set.
50
+ #
51
+ # Creates and configures a new constraint instance based on the provided type
52
+ # and configuration. If a constraint with the same accessor and name already
53
+ # exists, it will be replaced.
54
+ #
55
+ # @param accessor [String, Symbol] The accessor method for the constraint
56
+ # @param constraint_type [String, Symbol] The type of constraint to create
57
+ # @param expectation [Object, nil] The expected value for the constraint
58
+ # @param options [Hash{Symbol, String => Object}] Additional options for the constraint
59
+ #
60
+ # @option options [String, Symbol] concerning The subject of the constraint. This is used to namespace
61
+ # constraints that may have compatibility issues with other constraints (i,e, min/max size constraints).
62
+ # @option options [String, Symbol, nil] description The quantifier description of the constraint when given
63
+ # a string that ends with "not_described" it will not be included in the constraint set description.
64
+ # @option options [Boolean] :abort_on_failure Whether to stop on failure
65
+ # @option options [Array<Proc>, Proc] :coerce_with Coercers to run on the value before validating the
66
+ # constraint.
67
+ #
68
+ # @return [void]
69
+ # @rbs (
70
+ # Type::accessor accessor,
71
+ # String | Symbol constraint_type,
72
+ # ?untyped expectation,
73
+ # **untyped options
74
+ # ) -> void
75
+ def add(accessor, constraint_type, expectation = nil, **options)
76
+ accessor, type = [accessor, constraint_type].map(&:to_sym)
77
+ namespace = options[:concerning]&.to_sym || constraint_type
78
+
79
+ # @type var accessor: Type::accessor
80
+ # @type var type: Symbol
81
+ # @type var namespace: Symbol
82
+
83
+ @lookup[accessor][namespace] ||= build_constraint(type, accessor, options[:description])
84
+ @lookup[accessor][namespace].expecting(expectation)
85
+ .with_options(options.except(:concerning, :description)) # steep:ignore
86
+ end
87
+
88
+ # Get all constraints in the set.
89
+ #
90
+ # @return [Array<Behavior>] Array of all constraints
91
+ # @rbs () -> Array[Behavior]
92
+ def all
93
+ @lookup.values.flat_map(&:values)
94
+ end
95
+
96
+ # @!method all?
97
+ # Check if all constraints match the provided block.
98
+ #
99
+ # @yield [Behavior] The constraint to check
100
+ # @return [Boolean] true if all constraints match.
101
+ def_delegators :all, :all?
102
+
103
+ # @rbs!
104
+ # # Check if all constraints match the provided block.
105
+ #
106
+ # # @yield [Behavior] The constraint to check
107
+ # # @return [Boolean] true if all constraints match.
108
+ # def all?: () -> bool
109
+ # | (Class | Module) -> bool
110
+ # | () { (Behavior) -> boolish } -> bool
111
+
112
+ # Get the total number of constraints.
113
+ #
114
+ # @return [Integer] The number of constraints
115
+ # @rbs () -> Integer
116
+ def count
117
+ @lookup.values.sum(&:count)
118
+ end
119
+ alias length count
120
+ alias size count
121
+
122
+ # The aggregate description of all constraints in the set.
123
+ #
124
+ # @return [String] The description of all constraints
125
+ # @rbs () -> String
126
+ def description
127
+ Type::ACCESSORS.flat_map do |accessor|
128
+ @lookup[accessor].values.filter_map(&:full_description)
129
+ end.join(', ').strip
130
+ end
131
+
132
+ # @!method each
133
+ # Iterate over each constraint in the set.
134
+ #
135
+ # @yield [Behavior] The constraint to process
136
+ # @return [void]
137
+ def_delegators :all, :each
138
+
139
+ # @rbs!
140
+ # # Iterate over each constraint in the set.
141
+ #
142
+ # # @yield [Behavior] The constraint to process
143
+ # # @return [void]
144
+ # def each: () { (Behavior) -> void } -> void
145
+
146
+ # Check if a specific constraint exists.
147
+ #
148
+ # @param accessor [Symbol] The accessor method for the constraint
149
+ # @param concerning [String, Symbol] The subject of the constraint.
150
+ #
151
+ # @return [Boolean] true if the constraint exists
152
+ # @rbs (Type::accessor accessor, Symbol | String concerning) -> bool
153
+ def exist?(accessor, concerning)
154
+ accessor = accessor.to_sym
155
+ # @type var accessor: Type::accessor
156
+ @lookup.key?(accessor) && @lookup[accessor].key?(concerning.to_sym)
157
+ end
158
+ alias has_constraint? exist?
159
+
160
+ # Whether any constraints in the set have failed satisfaction.
161
+ #
162
+ # @return [Boolean] true if any constraints have failed
163
+ # @rbs () -> bool
164
+ def failures?
165
+ all.any?(&:failure?)
166
+ end
167
+
168
+ # @!method filter_map
169
+ # Iterate over each constraint in the set, returning the results of the block excluding nil values.
170
+ #
171
+ # @yield [Behavior] The constraint to process
172
+ # @return [Array] The results of the block.
173
+ def_delegators :all, :filter_map
174
+
175
+ # @rbs!
176
+ # # Iterate over each constraint in the set, returning the results of the block excluding nil values.
177
+ #
178
+ # # @yield [Behavior] The constraint to process
179
+ # # @return [Array] The results of the block.
180
+ # def filter_map: () { (Behavior) -> untyped } -> Array[Behavior]
181
+
182
+ # Get a specific constraint by its accessor and name.
183
+ #
184
+ # @param accessor [Symbol] The accessor method for the constraint
185
+ # @param concerning [String, Symbol] The subject of the constraint.
186
+ #
187
+ # @return [Behavior, nil] The constraint if found, nil otherwise
188
+ # @rbs (Type::accessor accessor, String | Symbol concerning) -> Behavior?
189
+ def find(accessor, concerning)
190
+ # @type var accessor: Type::accessor
191
+ @lookup.dig(accessor.to_sym, concerning.to_sym)
192
+ end
193
+
194
+ # Prepare a new constraint.
195
+ #
196
+ # This is useful for preparing a constraint to use as a sub-constraint for a more complex constraint.
197
+ #
198
+ # @param accessor [String, Symbol] The accessor method for the constraint
199
+ # @param constraint_type [String, Symbol] The type of constraint to create
200
+ # @param expectation [Object, nil] The expected value for the constraint
201
+ # @param options [Hash{Symbol, String => Object}] Additional options for the constraint
202
+ #
203
+ # @option options [String, Symbol, nil] description The quantifier description of the constraint when given
204
+ # a string that ends with "not_described" it will not be included in the constraint set description.
205
+ # @option options [Boolean] :abort_on_failure Whether to stop on failure
206
+ # @option options [Array<Proc>, Proc] :coerce_with Coercers to run on the value before validating the
207
+ # constraint.
208
+ #
209
+ # @return [Behavior] The new constraint instance
210
+ # @rbs (
211
+ # Type::accessor accessor,
212
+ # String | Symbol constraint_type,
213
+ # ?untyped expectation,
214
+ # **untyped options
215
+ # ) -> Behavior
216
+ def prepare(accessor, constraint_type, expectation = nil, **options)
217
+ build_constraint(constraint_type, accessor, options[:description])
218
+ .expecting(expectation)
219
+ .with_options(options.except(:description)) # steep:ignore ArgumentTypeMismatch
220
+ end
221
+
222
+ # The aggregate violation description of all constraints in the set.
223
+ #
224
+ # @return [String] The description of all constraints
225
+ # @rbs () -> String
226
+ def violation_description
227
+ Type::ACCESSORS.flat_map do |accessor|
228
+ @lookup[accessor].values.reject(&:successful?).filter_map(&:full_violation_description)
229
+ end.join(', ').strip
230
+ end
231
+
232
+ private
233
+
234
+ # Build a new constraint instance for the given type.
235
+ #
236
+ # @param constraint_type [String, Symbol] The type of constraint to create
237
+ # @param accessor [String, Symbol] The accessor method for the constraint
238
+ # @param quantifier_description [String, Symbol] The quantifier description of the constraint
239
+ #
240
+ # @return [Behavior] The new constraint instance
241
+ # @rbs (
242
+ # String | Symbol constraint_type,
243
+ # Type::accessor accessor,
244
+ # (String | Symbol)?,
245
+ # ) -> Behavior
246
+ def build_constraint(constraint_type, accessor, quantifier_description)
247
+ Resolver.resolve!(constraint_type.to_sym)
248
+ .new(accessor.to_sym, quantifier_description)
249
+ end
250
+
251
+ # Ensure that the lookup hash is deep copied when duplicating.
252
+ #
253
+ # @param source [Set] The source object to copy
254
+ #
255
+ # @return [void]
256
+ # @rbs override
257
+ def initialize_copy(source)
258
+ @lookup = source.instance_variable_get(:@lookup).transform_values do |accessor|
259
+ accessor.transform_values(&:dup)
260
+ end
261
+ super
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end