super-smart-tool 0.0.1

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/dry-types-1.9.1/CHANGELOG.md +1145 -0
  3. data/dry-types-1.9.1/LICENSE +20 -0
  4. data/dry-types-1.9.1/README.md +17 -0
  5. data/dry-types-1.9.1/dry-types.gemspec +45 -0
  6. data/dry-types-1.9.1/lib/dry/types/any.rb +41 -0
  7. data/dry-types-1.9.1/lib/dry/types/array/constructor.rb +24 -0
  8. data/dry-types-1.9.1/lib/dry/types/array/member.rb +118 -0
  9. data/dry-types-1.9.1/lib/dry/types/array.rb +32 -0
  10. data/dry-types-1.9.1/lib/dry/types/builder.rb +221 -0
  11. data/dry-types-1.9.1/lib/dry/types/builder_methods.rb +138 -0
  12. data/dry-types-1.9.1/lib/dry/types/coercions/json.rb +57 -0
  13. data/dry-types-1.9.1/lib/dry/types/coercions/params.rb +168 -0
  14. data/dry-types-1.9.1/lib/dry/types/coercions.rb +105 -0
  15. data/dry-types-1.9.1/lib/dry/types/compat.rb +1 -0
  16. data/dry-types-1.9.1/lib/dry/types/compiler.rb +138 -0
  17. data/dry-types-1.9.1/lib/dry/types/composition.rb +138 -0
  18. data/dry-types-1.9.1/lib/dry/types/constrained/coercible.rb +59 -0
  19. data/dry-types-1.9.1/lib/dry/types/constrained.rb +146 -0
  20. data/dry-types-1.9.1/lib/dry/types/constraints.rb +30 -0
  21. data/dry-types-1.9.1/lib/dry/types/constructor/function.rb +203 -0
  22. data/dry-types-1.9.1/lib/dry/types/constructor/wrapper.rb +88 -0
  23. data/dry-types-1.9.1/lib/dry/types/constructor.rb +195 -0
  24. data/dry-types-1.9.1/lib/dry/types/container.rb +12 -0
  25. data/dry-types-1.9.1/lib/dry/types/core.rb +106 -0
  26. data/dry-types-1.9.1/lib/dry/types/decorator.rb +94 -0
  27. data/dry-types-1.9.1/lib/dry/types/default.rb +122 -0
  28. data/dry-types-1.9.1/lib/dry/types/enum.rb +121 -0
  29. data/dry-types-1.9.1/lib/dry/types/errors.rb +138 -0
  30. data/dry-types-1.9.1/lib/dry/types/extensions/maybe.rb +128 -0
  31. data/dry-types-1.9.1/lib/dry/types/extensions/monads.rb +34 -0
  32. data/dry-types-1.9.1/lib/dry/types/extensions.rb +9 -0
  33. data/dry-types-1.9.1/lib/dry/types/fn_container.rb +37 -0
  34. data/dry-types-1.9.1/lib/dry/types/hash/constructor.rb +25 -0
  35. data/dry-types-1.9.1/lib/dry/types/hash.rb +134 -0
  36. data/dry-types-1.9.1/lib/dry/types/implication.rb +64 -0
  37. data/dry-types-1.9.1/lib/dry/types/inflector.rb +9 -0
  38. data/dry-types-1.9.1/lib/dry/types/intersection.rb +102 -0
  39. data/dry-types-1.9.1/lib/dry/types/json.rb +35 -0
  40. data/dry-types-1.9.1/lib/dry/types/lax.rb +66 -0
  41. data/dry-types-1.9.1/lib/dry/types/map.rb +134 -0
  42. data/dry-types-1.9.1/lib/dry/types/meta.rb +53 -0
  43. data/dry-types-1.9.1/lib/dry/types/module.rb +127 -0
  44. data/dry-types-1.9.1/lib/dry/types/nominal.rb +201 -0
  45. data/dry-types-1.9.1/lib/dry/types/options.rb +34 -0
  46. data/dry-types-1.9.1/lib/dry/types/params.rb +38 -0
  47. data/dry-types-1.9.1/lib/dry/types/predicate_inferrer.rb +235 -0
  48. data/dry-types-1.9.1/lib/dry/types/predicate_registry.rb +32 -0
  49. data/dry-types-1.9.1/lib/dry/types/primitive_inferrer.rb +75 -0
  50. data/dry-types-1.9.1/lib/dry/types/printable.rb +14 -0
  51. data/dry-types-1.9.1/lib/dry/types/printer/composition.rb +44 -0
  52. data/dry-types-1.9.1/lib/dry/types/printer.rb +313 -0
  53. data/dry-types-1.9.1/lib/dry/types/result.rb +72 -0
  54. data/dry-types-1.9.1/lib/dry/types/schema/key.rb +138 -0
  55. data/dry-types-1.9.1/lib/dry/types/schema.rb +408 -0
  56. data/dry-types-1.9.1/lib/dry/types/spec/types.rb +164 -0
  57. data/dry-types-1.9.1/lib/dry/types/sum.rb +105 -0
  58. data/dry-types-1.9.1/lib/dry/types/type.rb +53 -0
  59. data/dry-types-1.9.1/lib/dry/types/version.rb +7 -0
  60. data/dry-types-1.9.1/lib/dry/types.rb +259 -0
  61. data/dry-types-1.9.1/lib/dry-types.rb +3 -0
  62. data/super-smart-tool.gemspec +11 -0
  63. metadata +101 -0
@@ -0,0 +1,408 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # The built-in Hash type can be defined in terms of keys and associated types
6
+ # its values can contain. Such definitions are named {Schema}s and defined
7
+ # as lists of {Key} types.
8
+ #
9
+ # @see Dry::Types::Schema::Key
10
+ #
11
+ # {Schema} evaluates default values for keys missing in input hash
12
+ #
13
+ # @see Dry::Types::Default#evaluate
14
+ # @see Dry::Types::Default::Callable#evaluate
15
+ #
16
+ # {Schema} implements Enumerable using its keys as collection.
17
+ #
18
+ # @api public
19
+ class Schema < Hash
20
+ NO_TRANSFORM = ::Dry::Types::FnContainer.register { |x| x }
21
+ SYMBOLIZE_KEY = ::Dry::Types::FnContainer.register(:to_sym.to_proc)
22
+
23
+ include ::Enumerable
24
+
25
+ # @return [Array[Dry::Types::Schema::Key]]
26
+ attr_reader :keys
27
+
28
+ # @return [Hash[Symbol, Dry::Types::Schema::Key]]
29
+ attr_reader :name_key_map
30
+
31
+ # @return [#call]
32
+ attr_reader :transform_key
33
+
34
+ # @param [Class] _primitive
35
+ # @param [Hash] options
36
+ #
37
+ # @option options [Array[Dry::Types::Schema::Key]] :keys
38
+ # @option options [String] :key_transform_fn
39
+ #
40
+ # @api private
41
+ def initialize(_primitive, **options)
42
+ @keys = options.fetch(:keys)
43
+ @name_key_map = keys.each_with_object({}) do |key, idx|
44
+ idx[key.name] = key
45
+ end
46
+
47
+ key_fn = options.fetch(:key_transform_fn, NO_TRANSFORM)
48
+
49
+ @transform_key = ::Dry::Types::FnContainer[key_fn]
50
+
51
+ super
52
+ end
53
+
54
+ # @param [Hash] hash
55
+ #
56
+ # @return [Hash{Symbol => Object}]
57
+ #
58
+ # @api private
59
+ def call_unsafe(hash, options = EMPTY_HASH)
60
+ resolve_unsafe(coerce(hash), options)
61
+ end
62
+
63
+ # @param [Hash] hash
64
+ #
65
+ # @return [Hash{Symbol => Object}]
66
+ #
67
+ # @api private
68
+ def call_safe(hash, options = EMPTY_HASH)
69
+ resolve_safe(coerce(hash) { return yield }, options) { return yield }
70
+ end
71
+
72
+ # @param [Hash] hash
73
+ #
74
+ # @option options [Boolean] :skip_missing If true don't raise error if on missing keys
75
+ # @option options [Boolean] :resolve_defaults If false default value
76
+ # won't be evaluated for missing key
77
+ # @return [Hash{Symbol => Object}]
78
+ #
79
+ # @api public
80
+ def apply(hash, options = EMPTY_HASH) = call_unsafe(hash, options)
81
+
82
+ # @param input [Hash] hash
83
+ #
84
+ # @yieldparam [Failure] failure
85
+ # @yieldreturn [Result]
86
+ #
87
+ # @return [Logic::Result]
88
+ # @return [Object] if coercion fails and a block is given
89
+ #
90
+ # @api public
91
+ # rubocop:disable Metrics/AbcSize
92
+ # rubocop:disable Metrics/PerceivedComplexity
93
+ def try(input)
94
+ if primitive?(input)
95
+ success = true
96
+ output = {}
97
+ result = {}
98
+
99
+ input.each do |key, value|
100
+ k = @transform_key.(key)
101
+ type = @name_key_map[k]
102
+
103
+ if type
104
+ key_result = type.try(value)
105
+ result[k] = key_result
106
+ output[k] = key_result.input
107
+ success &&= key_result.success?
108
+ elsif strict?
109
+ success = false
110
+ end
111
+ end
112
+
113
+ if output.size < keys.size
114
+ resolve_missing_keys(output, options) do
115
+ success = false
116
+ end
117
+ end
118
+
119
+ success &&= primitive?(output)
120
+
121
+ if success
122
+ failure = nil
123
+ else
124
+ error = CoercionError.new("#{input} doesn't conform schema", meta: result)
125
+ failure = failure(output, error)
126
+ end
127
+ else
128
+ failure = failure(input, CoercionError.new("#{input} must be a hash"))
129
+ end
130
+
131
+ if failure.nil?
132
+ success(output)
133
+ elsif block_given?
134
+ yield(failure)
135
+ else
136
+ failure
137
+ end
138
+ end
139
+ # rubocop:enable Metrics/AbcSize
140
+ # rubocop:enable Metrics/PerceivedComplexity
141
+
142
+ # @param meta [Boolean] Whether to dump the meta to the AST
143
+ #
144
+ # @return [Array] An AST representation
145
+ #
146
+ # @api public
147
+ def to_ast(meta: true)
148
+ [
149
+ :schema,
150
+ [keys.map { |key| key.to_ast(meta: meta) },
151
+ options.slice(:key_transform_fn, :type_transform_fn, :strict, :namespace),
152
+ meta_ast(meta)]
153
+ ]
154
+ end
155
+
156
+ # Whether the schema rejects unknown keys
157
+ #
158
+ # @return [Boolean]
159
+ #
160
+ # @api public
161
+ def strict?
162
+ options.fetch(:strict, false)
163
+ end
164
+
165
+ # Make the schema intolerant to unknown keys
166
+ #
167
+ # @return [Schema]
168
+ #
169
+ # @api public
170
+ def strict(strict = true)
171
+ with(strict: strict)
172
+ end
173
+
174
+ # Inject a key transformation function
175
+ #
176
+ # @param [#call,nil] proc
177
+ # @param [#call,nil] block
178
+ #
179
+ # @return [Schema]
180
+ #
181
+ # @api public
182
+ def with_key_transform(proc = nil, &block)
183
+ fn = proc || block
184
+
185
+ raise ::ArgumentError, "a block or callable argument is required" if fn.nil?
186
+
187
+ handle = ::Dry::Types::FnContainer.register(fn)
188
+ with(key_transform_fn: handle)
189
+ end
190
+
191
+ # Whether the schema transforms input keys
192
+ #
193
+ # @return [Boolean]
194
+ #
195
+ # @api public
196
+ def transform_keys?
197
+ !options[:key_transform_fn].nil?
198
+ end
199
+
200
+ # @overload schema(type_map, meta = EMPTY_HASH)
201
+ # @param [{Symbol => Dry::Types::Nominal}] type_map
202
+ # @param [Hash] meta
203
+ # @return [Dry::Types::Schema]
204
+ #
205
+ # @overload schema(keys)
206
+ # @param [Array<Dry::Types::Schema::Key>] key List of schema keys
207
+ # @param [Hash] meta
208
+ # @return [Dry::Types::Schema]
209
+ #
210
+ # @api public
211
+ def schema(keys_or_map)
212
+ if keys_or_map.is_a?(::Array)
213
+ new_keys = keys_or_map
214
+ else
215
+ new_keys = build_keys(keys_or_map)
216
+ end
217
+
218
+ keys = merge_keys(self.keys, new_keys)
219
+ Schema.new(primitive, **options, keys: keys, meta: meta)
220
+ end
221
+
222
+ # Iterate over each key type
223
+ #
224
+ # @return [Array<Dry::Types::Schema::Key>,Enumerator]
225
+ #
226
+ # @api public
227
+ def each(&)
228
+ keys.each(&)
229
+ end
230
+
231
+ # Whether the schema has the given key
232
+ #
233
+ # @param [Symbol] name Key name
234
+ #
235
+ # @return [Boolean]
236
+ #
237
+ # @api public
238
+ def key?(name)
239
+ name_key_map.key?(name)
240
+ end
241
+
242
+ # Fetch key type by a key name
243
+ #
244
+ # Behaves as ::Hash#fetch
245
+ #
246
+ # @overload key(name, fallback = Undefined)
247
+ # @param [Symbol] name Key name
248
+ # @param [Object] fallback Optional fallback, returned if key is missing
249
+ # @return [Dry::Types::Schema::Key,Object] key type or fallback if key is not in schema
250
+ #
251
+ # @overload key(name, &block)
252
+ # @param [Symbol] name Key name
253
+ # @param [Proc] block Fallback block, runs if key is missing
254
+ # @return [Dry::Types::Schema::Key,Object] key type or block value if key is not in schema
255
+ #
256
+ # @api public
257
+ def key(name, fallback = Undefined, &)
258
+ if Undefined.equal?(fallback)
259
+ name_key_map.fetch(name, &)
260
+ else
261
+ name_key_map.fetch(name, fallback)
262
+ end
263
+ end
264
+
265
+ # @return [Boolean]
266
+ #
267
+ # @api public
268
+ def constrained?
269
+ true
270
+ end
271
+
272
+ # @return [Lax]
273
+ #
274
+ # @api public
275
+ def lax
276
+ Lax.new(schema(keys.map(&:lax)))
277
+ end
278
+
279
+ # Merge given schema keys into current schema
280
+ #
281
+ # A new instance is returned.
282
+ #
283
+ # @param other [Schema] schema
284
+ # @return [Schema]
285
+ #
286
+ # @api public
287
+ def merge(other)
288
+ schema(other.keys)
289
+ end
290
+
291
+ # Empty schema with the same options
292
+ #
293
+ # @return [Schema]
294
+ #
295
+ # @api public
296
+ def clear
297
+ with(keys: EMPTY_ARRAY)
298
+ end
299
+
300
+ private
301
+
302
+ # @param [Array<Dry::Types::Schema::Keys>] keys
303
+ #
304
+ # @return [Dry::Types::Schema]
305
+ #
306
+ # @api private
307
+ def merge_keys(*keys)
308
+ keys
309
+ .flatten(1)
310
+ .each_with_object({}) { |key, merged| merged[key.name] = key }
311
+ .values
312
+ end
313
+
314
+ # Validate and coerce a hash. Raise an exception on any error
315
+ #
316
+ # @api private
317
+ #
318
+ # @return [Hash]
319
+ def resolve_unsafe(hash, options = EMPTY_HASH)
320
+ result = {}
321
+
322
+ hash.each do |key, value|
323
+ k = @transform_key.(key)
324
+ type = @name_key_map[k]
325
+
326
+ if type
327
+ begin
328
+ result[k] = type.call_unsafe(value)
329
+ rescue ConstraintError => exception
330
+ raise SchemaError.new(type.name, value, exception.result)
331
+ rescue CoercionError => exception
332
+ raise SchemaError.new(type.name, value, exception.message)
333
+ end
334
+ elsif strict?
335
+ raise unexpected_keys(hash.keys)
336
+ end
337
+ end
338
+
339
+ resolve_missing_keys(result, options) if result.size < keys.size
340
+
341
+ result
342
+ end
343
+
344
+ # Validate and coerce a hash. Call a block and halt on any error
345
+ #
346
+ # @api private
347
+ #
348
+ # @return [Hash]
349
+ def resolve_safe(hash, options = EMPTY_HASH, &block)
350
+ result = {}
351
+
352
+ hash.each do |key, value|
353
+ k = @transform_key.(key)
354
+ type = @name_key_map[k]
355
+
356
+ if type
357
+ result[k] = type.call_safe(value, &block)
358
+ elsif strict?
359
+ yield
360
+ end
361
+ end
362
+
363
+ resolve_missing_keys(result, options, &block) if result.size < keys.size
364
+
365
+ result
366
+ end
367
+
368
+ # Try to add missing keys to the hash
369
+ #
370
+ # @api private
371
+ def resolve_missing_keys(hash, options) # rubocop:disable Metrics/PerceivedComplexity
372
+ skip_missing = options.fetch(:skip_missing, false)
373
+ resolve_defaults = options.fetch(:resolve_defaults, true)
374
+
375
+ keys.each do |key|
376
+ next if hash.key?(key.name)
377
+
378
+ if key.default? && resolve_defaults
379
+ hash[key.name] = key.call_unsafe(Undefined)
380
+ elsif key.required? && !skip_missing
381
+ if block_given?
382
+ return yield
383
+ else
384
+ raise missing_key(key.name)
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ # @param hash_keys [Array<Symbol>]
391
+ #
392
+ # @return [UnknownKeysError]
393
+ #
394
+ # @api private
395
+ def unexpected_keys(hash_keys)
396
+ extra_keys = hash_keys.map(&transform_key) - name_key_map.keys
397
+ UnknownKeysError.new(extra_keys)
398
+ end
399
+
400
+ # @return [MissingKeyError]
401
+ #
402
+ # @api private
403
+ def missing_key(key)
404
+ MissingKeyError.new(key)
405
+ end
406
+ end
407
+ end
408
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples_for "Dry::Types::Nominal without primitive" do
4
+ def be_boolean
5
+ satisfy { |x| x == true || x == false }
6
+ end
7
+
8
+ describe "#constrained?" do
9
+ it "returns a boolean value" do
10
+ expect(type.constrained?).to be_boolean
11
+ end
12
+ end
13
+
14
+ describe "#default?" do
15
+ it "returns a boolean value" do
16
+ expect(type.default?).to be_boolean
17
+ end
18
+ end
19
+
20
+ describe "#valid?" do
21
+ it "returns a boolean value" do
22
+ expect(type.valid?(1)).to be_boolean
23
+ end
24
+ end
25
+
26
+ describe "#eql?" do
27
+ it "has #eql? defined" do
28
+ expect(type).to eql(type)
29
+ end
30
+ end
31
+
32
+ describe "#==" do
33
+ it "has #== defined" do
34
+ expect(type).to eql(type)
35
+ end
36
+ end
37
+
38
+ describe "#optional?" do
39
+ it "returns a boolean value" do
40
+ expect(type.optional?).to be_boolean
41
+ end
42
+ end
43
+
44
+ describe "#to_s" do
45
+ it "returns a custom string representation" do
46
+ expect(type.to_s).to start_with("#<Dry::Types") if type.class.name.start_with?("Dry::Types")
47
+ end
48
+ end
49
+
50
+ describe "#to_proc" do
51
+ subject(:callable) { type.to_proc }
52
+
53
+ it "converts a type to a proc" do
54
+ expect(callable).to be_a(Proc)
55
+ end
56
+ end
57
+ end
58
+
59
+ RSpec.shared_examples_for "Dry::Types::Nominal#meta" do
60
+ describe "#meta" do
61
+ it "allows setting meta information" do
62
+ with_meta = type.meta(foo: :bar).meta(baz: "1")
63
+
64
+ expect(with_meta).to be_instance_of(type.class)
65
+ expect(with_meta.meta).to eql(foo: :bar, baz: "1")
66
+ end
67
+
68
+ it "equalizes on empty meta" do
69
+ expect(type).to eql(type.meta({}))
70
+ end
71
+
72
+ it "equalizes on filled meta" do
73
+ expect(type).to_not eql(type.meta(i_am: "different"))
74
+ end
75
+
76
+ it "is locally immutable" do
77
+ expect(type.meta).to be_a Hash
78
+ expect(type.meta).to be_frozen
79
+ expect(type.meta).not_to have_key :immutable_test
80
+ derived = type.meta(immutable_test: 1)
81
+ expect(derived.meta).to be_frozen
82
+ expect(derived.meta).to eql(immutable_test: 1)
83
+ expect(type.meta).not_to have_key :immutable_test
84
+ end
85
+ end
86
+
87
+ describe "#pristine" do
88
+ it "erases meta" do
89
+ expect(type.meta(foo: :bar).pristine).to eql(type)
90
+ end
91
+ end
92
+ end
93
+
94
+ RSpec.shared_examples_for Dry::Types::Nominal do
95
+ it_behaves_like "Dry::Types::Nominal without primitive"
96
+
97
+ describe "#primitive" do
98
+ it "returns a class" do
99
+ expect(type.primitive).to be_instance_of(Class)
100
+ end
101
+ end
102
+
103
+ describe "#constructor" do
104
+ it "returns a constructor" do
105
+ constructor = type.constructor(&:to_s)
106
+
107
+ expect(constructor).to be_a(Dry::Types::Type)
108
+ end
109
+ end
110
+ end
111
+
112
+ RSpec.shared_examples_for "a constrained type" do |options = {inputs: Object.new}|
113
+ inputs = options[:inputs]
114
+
115
+ let(:fallback) { Object.new }
116
+
117
+ describe "#call" do
118
+ it "yields a block on failure" do
119
+ Array(inputs).each do |input|
120
+ expect(type.(input) { fallback }).to be(fallback)
121
+ end
122
+ end
123
+
124
+ it "throws an error on invalid input" do
125
+ Array(inputs).each do |input|
126
+ expect { type.(input) }.to raise_error(Dry::Types::CoercionError)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "#constructor" do
132
+ let(:wrapping_constructor) do
133
+ type.constructor { |input, type| type.(input) { fallback } }
134
+ end
135
+
136
+ it "can be wrapped" do
137
+ Array(inputs).each do |input|
138
+ expect(wrapping_constructor.(input)).to be(fallback)
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ RSpec.shared_examples_for "a nominal type" do |inputs: Object.new|
145
+ describe "#call" do
146
+ it "always returns the input back" do
147
+ Array(inputs).each do |input|
148
+ expect(type.(input) { raise }).to be(input)
149
+ expect(type.(input)).to be(input)
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ RSpec.shared_examples_for "a composable constructor" do
156
+ describe "#constructor" do
157
+ it "has aliases for composition" do
158
+ expect(type.method(:append)).to eql(type.method(:constructor))
159
+ expect(type.method(:prepend)).to eql(type.method(:constructor))
160
+ expect(type.method(:<<)).to eql(type.method(:constructor))
161
+ expect(type.method(:>>)).to eql(type.method(:constructor))
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # Sum type
6
+ #
7
+ # Sum types try each constituent type from left to right and return the result
8
+ # from the first successful type. If all types fail, the error from the rightmost
9
+ # (last attempted) type is raised, not necessarily the most "relevant" error.
10
+ #
11
+ # @example
12
+ # # Given: FixedAmount | Percentage
13
+ # # Input: { type: "fixed", value: -1.1 }
14
+ # # FixedAmount fails on value constraint, Percentage fails on type mismatch
15
+ # # Error raised will be from Percentage (rightmost), not FixedAmount
16
+ #
17
+ # @api public
18
+ class Sum
19
+ include Composition
20
+
21
+ def self.operator = :|
22
+
23
+ # @return [Boolean]
24
+ #
25
+ # @api public
26
+ def optional? = primitive?(nil)
27
+
28
+ # @param [Object] input
29
+ #
30
+ # @return [Object]
31
+ #
32
+ # @note Tries left type first, then right type. If both fail, raises the
33
+ # error from the right (last attempted) type for performance reasons.
34
+ #
35
+ # @api private
36
+ def call_unsafe(input)
37
+ left.call_safe(input) { right.call_unsafe(input) }
38
+ end
39
+
40
+ # @param [Object] input
41
+ #
42
+ # @return [Object]
43
+ #
44
+ # @api private
45
+ def call_safe(input, &block)
46
+ left.call_safe(input) { right.call_safe(input, &block) }
47
+ end
48
+
49
+ # @param [Object] input
50
+ #
51
+ # @api public
52
+ def try(input)
53
+ left.try(input) do
54
+ right.try(input) do |failure|
55
+ if block_given?
56
+ yield(failure)
57
+ else
58
+ failure
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # @param [Object] value
65
+ #
66
+ # @return [Boolean]
67
+ #
68
+ # @api private
69
+ def primitive?(value)
70
+ left.primitive?(value) || right.primitive?(value)
71
+ end
72
+
73
+ # Manage metadata to the type. If the type is an optional, #meta delegates
74
+ # to the right branch
75
+ #
76
+ # @see [Meta#meta]
77
+ #
78
+ # @api public
79
+ def meta(data = Undefined)
80
+ if Undefined.equal?(data)
81
+ optional? ? right.meta : super
82
+ elsif optional?
83
+ self.class.new(left, right.meta(data), **options)
84
+ else
85
+ super
86
+ end
87
+ end
88
+
89
+ # @param [Hash] options
90
+ #
91
+ # @return [Constrained,Sum]
92
+ #
93
+ # @see Builder#constrained
94
+ #
95
+ # @api public
96
+ def constrained(...)
97
+ if optional?
98
+ right.constrained(...).optional
99
+ else
100
+ super
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end