dry-types 1.7.2 → 1.8.2

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/README.md +1 -1
  4. data/dry-types.gemspec +12 -16
  5. data/lib/dry/types/any.rb +3 -9
  6. data/lib/dry/types/array/constructor.rb +3 -9
  7. data/lib/dry/types/array/member.rb +1 -3
  8. data/lib/dry/types/array.rb +1 -1
  9. data/lib/dry/types/builder.rb +19 -35
  10. data/lib/dry/types/builder_methods.rb +5 -15
  11. data/lib/dry/types/coercions/json.rb +3 -3
  12. data/lib/dry/types/coercions/params.rb +13 -13
  13. data/lib/dry/types/coercions.rb +13 -15
  14. data/lib/dry/types/compiler.rb +5 -7
  15. data/lib/dry/types/composition.rb +17 -31
  16. data/lib/dry/types/constrained/coercible.rb +3 -3
  17. data/lib/dry/types/constrained.rb +31 -23
  18. data/lib/dry/types/constructor/function.rb +19 -31
  19. data/lib/dry/types/constructor/wrapper.rb +5 -11
  20. data/lib/dry/types/constructor.rb +9 -17
  21. data/lib/dry/types/core.rb +14 -6
  22. data/lib/dry/types/decorator.rb +7 -18
  23. data/lib/dry/types/default.rb +14 -18
  24. data/lib/dry/types/enum.rb +33 -13
  25. data/lib/dry/types/errors.rb +4 -8
  26. data/lib/dry/types/extensions/maybe.rb +5 -11
  27. data/lib/dry/types/fn_container.rb +3 -3
  28. data/lib/dry/types/hash/constructor.rb +3 -9
  29. data/lib/dry/types/hash.rb +8 -6
  30. data/lib/dry/types/implication.rb +5 -7
  31. data/lib/dry/types/inflector.rb +1 -1
  32. data/lib/dry/types/intersection.rb +3 -9
  33. data/lib/dry/types/lax.rb +5 -11
  34. data/lib/dry/types/map.rb +33 -41
  35. data/lib/dry/types/meta.rb +2 -6
  36. data/lib/dry/types/module.rb +3 -5
  37. data/lib/dry/types/nominal.rb +13 -35
  38. data/lib/dry/types/predicate_inferrer.rb +3 -5
  39. data/lib/dry/types/predicate_registry.rb +1 -3
  40. data/lib/dry/types/primitive_inferrer.rb +4 -12
  41. data/lib/dry/types/printable.rb +1 -3
  42. data/lib/dry/types/printer/composition.rb +3 -3
  43. data/lib/dry/types/printer.rb +6 -9
  44. data/lib/dry/types/result.rb +5 -15
  45. data/lib/dry/types/schema/key.rb +11 -26
  46. data/lib/dry/types/schema.rb +10 -12
  47. data/lib/dry/types/spec/types.rb +2 -2
  48. data/lib/dry/types/sum.rb +4 -8
  49. data/lib/dry/types/type.rb +2 -2
  50. data/lib/dry/types/version.rb +1 -1
  51. data/lib/dry/types.rb +2 -2
  52. metadata +8 -63
@@ -12,7 +12,11 @@ module Dry
12
12
  include Options
13
13
  include Meta
14
14
  include Printable
15
- include Dry::Equalizer(:left, :right, :options, :meta, inspect: false, immutable: true)
15
+ include ::Dry::Equalizer(
16
+ :left, :right, :options, :meta,
17
+ inspect: false,
18
+ immutable: true
19
+ )
16
20
 
17
21
  # @return [Type]
18
22
  attr_reader :left
@@ -35,7 +39,7 @@ module Dry
35
39
  ast_type = Inflector.underscore(composition_name).to_sym
36
40
  base.define_singleton_method(:ast_type) { ast_type }
37
41
  base.define_singleton_method(:composition_name) { composition_name }
38
- base.const_set("Constrained", Class.new(base) { include Constrained })
42
+ base.const_set("Constrained", ::Class.new(base) { include Constrained })
39
43
  end
40
44
 
41
45
  # @param [Type] left
@@ -52,55 +56,41 @@ module Dry
52
56
  # @return [String]
53
57
  #
54
58
  # @api public
55
- def name
56
- [left, right].map(&:name).join(" #{self.class.operator} ")
57
- end
59
+ def name = "#{left.name} #{self.class.operator} #{right.name}"
58
60
 
59
61
  # @return [false]
60
62
  #
61
63
  # @api public
62
- def default?
63
- false
64
- end
64
+ def default? = false
65
65
 
66
66
  # @return [false]
67
67
  #
68
68
  # @api public
69
- def constrained?
70
- false
71
- end
69
+ def constrained? = false
72
70
 
73
71
  # @return [Boolean]
74
72
  #
75
73
  # @api public
76
- def optional?
77
- false
78
- end
74
+ def optional? = false
79
75
 
80
76
  # @param [Object] input
81
77
  #
82
78
  # @return [Object]
83
79
  #
84
80
  # @api private
85
- def call_unsafe(input)
86
- raise NotImplementedError
87
- end
81
+ def call_unsafe(input) = raise ::NotImplementedError
88
82
 
89
83
  # @param [Object] input
90
84
  #
91
85
  # @return [Object]
92
86
  #
93
87
  # @api private
94
- def call_safe(input, &block)
95
- raise NotImplementedError
96
- end
88
+ def call_safe(input, &) = raise ::NotImplementedError
97
89
 
98
90
  # @param [Object] input
99
91
  #
100
92
  # @api public
101
- def try(input)
102
- raise NotImplementedError
103
- end
93
+ def try(input, &) = raise ::NotImplementedError
104
94
 
105
95
  # @api private
106
96
  def success(input)
@@ -108,7 +98,7 @@ module Dry
108
98
  if result.success?
109
99
  result
110
100
  else
111
- raise ArgumentError, "Invalid success value '#{input}' for #{inspect}"
101
+ raise ::ArgumentError, "Invalid success value '#{input}' for #{inspect}"
112
102
  end
113
103
  end
114
104
 
@@ -118,7 +108,7 @@ module Dry
118
108
  if result.failure?
119
109
  result
120
110
  else
121
- raise ArgumentError, "Invalid failure value '#{input}' for #{inspect}"
111
+ raise ::ArgumentError, "Invalid failure value '#{input}' for #{inspect}"
122
112
  end
123
113
  end
124
114
 
@@ -127,9 +117,7 @@ module Dry
127
117
  # @return [Boolean]
128
118
  #
129
119
  # @api private
130
- def primitive?(value)
131
- raise NotImplementedError
132
- end
120
+ def primitive?(value) = raise ::NotImplementedError
133
121
 
134
122
  # @see Nominal#to_ast
135
123
  #
@@ -144,9 +132,7 @@ module Dry
144
132
  # @return [Proc]
145
133
  #
146
134
  # @api public
147
- def to_proc
148
- proc { |value| self.(value) }
149
- end
135
+ def to_proc = proc { |value| self.(value) }
150
136
  end
151
137
  end
152
138
  end
@@ -37,7 +37,7 @@ module Dry
37
37
  # @see Dry::Types::Constrained#try
38
38
  #
39
39
  # @api public
40
- def try(input, &block)
40
+ def try(input, &)
41
41
  result = type.try(input)
42
42
 
43
43
  if result.success?
@@ -47,10 +47,10 @@ module Dry
47
47
  result
48
48
  else
49
49
  failure = failure(result.input, ConstraintError.new(validation, input))
50
- block ? yield(failure) : failure
50
+ block_given? ? yield(failure) : failure
51
51
  end
52
52
  else
53
- block ? yield(result) : result
53
+ block_given? ? yield(result) : result
54
54
  end
55
55
  end
56
56
  end
@@ -10,7 +10,7 @@ module Dry
10
10
  include Decorator
11
11
  include Builder
12
12
  include Printable
13
- include Dry::Equalizer(:type, :rule, inspect: false, immutable: true)
13
+ include ::Dry::Equalizer(:type, :rule, inspect: false, immutable: true)
14
14
 
15
15
  # @return [Dry::Logic::Rule]
16
16
  attr_reader :rule
@@ -41,9 +41,9 @@ module Dry
41
41
  # @return [Object]
42
42
  #
43
43
  # @api private
44
- def call_safe(input, &block)
44
+ def call_safe(input, &)
45
45
  if rule[input]
46
- type.call_safe(input, &block)
46
+ type.call_safe(input, &)
47
47
  else
48
48
  yield
49
49
  end
@@ -64,19 +64,22 @@ module Dry
64
64
  # @return [Object]
65
65
  #
66
66
  # @api public
67
- def try(input, &block)
67
+ def try(input, &)
68
68
  result = rule.(input)
69
69
 
70
70
  if result.success?
71
- type.try(input, &block)
71
+ type.try(input, &)
72
72
  else
73
73
  failure = failure(input, ConstraintError.new(result, input))
74
74
  block_given? ? yield(failure) : failure
75
75
  end
76
76
  end
77
77
 
78
- # @param [Hash] options
79
- # The options hash provided to {Types.Rule} and combined
78
+ # @param *nullary_rules [Array<Symbol>] a list of rules that do not require an additional
79
+ # argument (e.g., :odd)
80
+ # @param **unary_rules [Hash] a list of rules that require an additional argument
81
+ # (e.g., gt: 0)
82
+ # The parameters are merger to create a rules hash provided to {Types.Rule} and combined
80
83
  # using {&} with previous {#rule}
81
84
  #
82
85
  # @return [Constrained]
@@ -84,33 +87,31 @@ module Dry
84
87
  # @see Dry::Logic::Operators#and
85
88
  #
86
89
  # @api public
87
- def constrained(options)
88
- with(rule: rule & Types.Rule(options))
90
+ def constrained(*nullary_rules, **unary_rules)
91
+ nullary_rules_hash = parse_arguments(nullary_rules)
92
+
93
+ rules = nullary_rules_hash.merge(unary_rules)
94
+
95
+ with(rule: rule & Types.Rule(rules))
89
96
  end
90
97
 
91
98
  # @return [true]
92
99
  #
93
100
  # @api public
94
- def constrained?
95
- true
96
- end
101
+ def constrained? = true
97
102
 
98
103
  # @param [Object] value
99
104
  #
100
105
  # @return [Boolean]
101
106
  #
102
107
  # @api public
103
- def ===(value)
104
- valid?(value)
105
- end
108
+ def ===(value) = valid?(value)
106
109
 
107
110
  # Build lax type. Constraints are not applicable to lax types hence unwrapping
108
111
  #
109
112
  # @return [Lax]
110
113
  # @api public
111
- def lax
112
- type.lax
113
- end
114
+ def lax = type.lax
114
115
 
115
116
  # @see Nominal#to_ast
116
117
  # @api public
@@ -119,9 +120,7 @@ module Dry
119
120
  end
120
121
 
121
122
  # @api private
122
- def constructor_type
123
- type.constructor_type
124
- end
123
+ def constructor_type = type.constructor_type
125
124
 
126
125
  private
127
126
 
@@ -130,8 +129,17 @@ module Dry
130
129
  # @return [Boolean]
131
130
  #
132
131
  # @api private
133
- def decorate?(response)
134
- super || response.is_a?(Constructor)
132
+ def decorate?(response) = super || response.is_a?(Constructor)
133
+
134
+ # @param [Array] positional_args
135
+ #
136
+ # @return [Hash]
137
+ #
138
+ # @api private
139
+ def parse_arguments(positional_arguments)
140
+ return positional_arguments.first if positional_arguments.first.is_a?(::Hash)
141
+
142
+ positional_arguments.flatten.zip([]).to_h
135
143
  end
136
144
  end
137
145
  end
@@ -13,10 +13,10 @@ module Dry
13
13
  #
14
14
  # @api private
15
15
  class Safe < Function
16
- def call(input, &block)
17
- @fn.(input, &block)
16
+ def call(input, &)
17
+ @fn.(input)
18
18
  rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
19
- CoercionError.handle(e, &block)
19
+ CoercionError.handle(e, &)
20
20
  end
21
21
  end
22
22
 
@@ -61,16 +61,16 @@ module Dry
61
61
  ::Module.new do
62
62
  if safe
63
63
  module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
64
- def call(input, &block) # def call(input, &block)
65
- @target.#{method}(input, &block) # @target.coerve(input, &block)
66
- end # end
64
+ def call(input, &) # def call(input, &)
65
+ @target.#{method}(input, &) # @target.coerce(input, &)
66
+ end # end
67
67
  RUBY
68
68
  else
69
69
  module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
70
- def call(input, &block) # def call(input, &block)
70
+ def call(input, &) # def call(input, &)
71
71
  @target.#{method}(input) # @target.coerce(input)
72
72
  rescue ::NoMethodError, ::TypeError, ::ArgumentError => error # rescue ::NoMethodError, ::TypeError, ::ArgumentError => error
73
- CoercionError.handle(error, &block) # CoercionError.handle(error, &block)
73
+ CoercionError.handle(error, &) # CoercionError.handle(error, &)
74
74
  end # end
75
75
  RUBY
76
76
  end
@@ -83,19 +83,17 @@ module Dry
83
83
  #
84
84
  # @api private
85
85
  class PrivateCall < MethodCall
86
- def call(input, &block)
87
- @target.send(@name, input, &block)
88
- end
86
+ def call(input, &) = @target.send(@name, input, &)
89
87
  end
90
88
 
91
89
  # Coercion via an unsafe private method call
92
90
  #
93
91
  # @api private
94
92
  class PrivateSafeCall < PrivateCall
95
- def call(input, &block)
93
+ def call(input, &)
96
94
  @target.send(@name, input)
97
95
  rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
98
- CoercionError.handle(e, &block)
96
+ CoercionError.handle(e, &)
99
97
  end
100
98
  end
101
99
 
@@ -115,23 +113,19 @@ module Dry
115
113
  @name = fn.name
116
114
  end
117
115
 
118
- def to_ast
119
- [:method, target, name]
120
- end
116
+ def to_ast = [:method, target, name]
121
117
  end
122
118
 
123
119
  class Wrapper < Function
124
120
  # @return [Object]
125
- def call(input, type, &block)
126
- @fn.(input, type, &block)
121
+ def call(input, type, &)
122
+ @fn.(input, type, &)
127
123
  rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
128
- CoercionError.handle(e, &block)
124
+ CoercionError.handle(e, &)
129
125
  end
130
126
  alias_method :[], :call
131
127
 
132
- def arity
133
- 2
134
- end
128
+ def arity = 2
135
129
  end
136
130
 
137
131
  # Choose or build specialized invokation code for a callable
@@ -175,19 +169,13 @@ module Dry
175
169
  end
176
170
 
177
171
  # @return [Object]
178
- def call(input, &block)
179
- @fn.(input, &block)
180
- end
172
+ def call(input, &) = @fn.(input, &)
181
173
  alias_method :[], :call
182
174
 
183
175
  # @return [Integer]
184
- def arity
185
- 1
186
- end
176
+ def arity = 1
187
177
 
188
- def wrapper?
189
- arity.equal?(2)
190
- end
178
+ def wrapper? = arity.equal?(2)
191
179
 
192
180
  # @return [Array]
193
181
  def to_ast
@@ -8,16 +8,12 @@ module Dry
8
8
  # @return [Object]
9
9
  #
10
10
  # @api private
11
- def call_safe(input, &block)
12
- fn.(input, type, &block)
13
- end
11
+ def call_safe(input, &) = fn.(input, type, &)
14
12
 
15
13
  # @return [Object]
16
14
  #
17
15
  # @api private
18
- def call_unsafe(input)
19
- fn.(input, type)
20
- end
16
+ def call_unsafe(input) = fn.(input, type)
21
17
 
22
18
  # @param [Object] input
23
19
  # @param [#call,nil] block
@@ -26,13 +22,13 @@ module Dry
26
22
  # @return [Object] if block given and try fails
27
23
  #
28
24
  # @api public
29
- def try(input, &block)
25
+ def try(input, &)
30
26
  value = fn.(input, type)
31
27
  rescue CoercionError => e
32
28
  failure = failure(input, e)
33
29
  block_given? ? yield(failure) : failure
34
30
  else
35
- type.try(value, &block)
31
+ type.try(value, &)
36
32
  end
37
33
 
38
34
  # Define a constructor for the type
@@ -85,9 +81,7 @@ module Dry
85
81
  # Replace underlying type
86
82
  #
87
83
  # @api private
88
- def __new__(type)
89
- self.class.new(type, *@__args__.drop(1), **@options)
90
- end
84
+ def __new__(type) = self.class.new(type, *@__args__.drop(1), **@options)
91
85
  end
92
86
  end
93
87
  end
@@ -7,7 +7,7 @@ module Dry
7
7
  #
8
8
  # @api public
9
9
  class Constructor < Nominal
10
- include Dry::Equalizer(:type, :options, inspect: false, immutable: true)
10
+ include ::Dry::Equalizer(:type, :options, inspect: false, immutable: true)
11
11
 
12
12
  # @return [#call]
13
13
  attr_reader :fn
@@ -77,9 +77,7 @@ module Dry
77
77
  # @return [Object]
78
78
  #
79
79
  # @api private
80
- def call_unsafe(input)
81
- type.call_unsafe(fn.(input))
82
- end
80
+ def call_unsafe(input) = type.call_unsafe(fn.(input))
83
81
 
84
82
  # @param [Object] input
85
83
  # @param [#call,nil] block
@@ -88,13 +86,13 @@ module Dry
88
86
  # @return [Object] if block given and try fails
89
87
  #
90
88
  # @api public
91
- def try(input, &block)
89
+ def try(input, &)
92
90
  value = fn.(input)
93
91
  rescue CoercionError => e
94
92
  failure = failure(input, e)
95
93
  block_given? ? yield(failure) : failure
96
94
  else
97
- type.try(value, &block)
95
+ type.try(value, &)
98
96
  end
99
97
 
100
98
  # Build a new constructor by appending a block to the coercion function
@@ -121,9 +119,7 @@ module Dry
121
119
  # @return [Class]
122
120
  #
123
121
  # @api private
124
- def constrained_type
125
- Constrained::Coercible
126
- end
122
+ def constrained_type = Constrained::Coercible
127
123
 
128
124
  # @see Nominal#to_ast
129
125
  #
@@ -150,18 +146,14 @@ module Dry
150
146
  #
151
147
  # @return [Lax]
152
148
  # @api public
153
- def lax
154
- Lax.new(constructor_type[type.lax, **options])
155
- end
149
+ def lax = Lax.new(constructor_type[type.lax, **options])
156
150
 
157
151
  # Wrap the type with a proc
158
152
  #
159
153
  # @return [Proc]
160
154
  #
161
155
  # @api public
162
- def to_proc
163
- proc { |value| self.(value) }
164
- end
156
+ def to_proc = proc { self.(_1) }
165
157
 
166
158
  private
167
159
 
@@ -181,9 +173,9 @@ module Dry
181
173
  # @param [#call, nil] block
182
174
  #
183
175
  # @api private
184
- def method_missing(method, *args, &block)
176
+ def method_missing(method, ...)
185
177
  if type.respond_to?(method)
186
- response = type.public_send(method, *args, &block)
178
+ response = type.public_send(method, ...)
187
179
 
188
180
  if response.is_a?(Type) && response.instance_of?(type.class)
189
181
  response.constructor_type[response, **options]
@@ -35,13 +35,15 @@ module Dry
35
35
  }.freeze
36
36
 
37
37
  # All built-in primitives
38
- ALL_PRIMITIVES = [KERNEL_COERCIBLE, METHOD_COERCIBLE, NON_COERCIBLE].reduce(&:merge).freeze
38
+ ALL_PRIMITIVES = [
39
+ KERNEL_COERCIBLE, METHOD_COERCIBLE, NON_COERCIBLE
40
+ ].reduce(&:merge).freeze
39
41
 
40
42
  # All coercible types
41
43
  COERCIBLE = KERNEL_COERCIBLE.merge(METHOD_COERCIBLE).freeze
42
44
 
43
45
  # All built-in primitives except {NilClass}
44
- NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
46
+ NON_NIL = ALL_PRIMITIVES.except(:nil).freeze
45
47
 
46
48
  # Register generic types for {ALL_PRIMITIVES}
47
49
  ALL_PRIMITIVES.each do |name, primitive|
@@ -58,14 +60,17 @@ module Dry
58
60
 
59
61
  # Register {KERNEL_COERCIBLE} types
60
62
  KERNEL_COERCIBLE.each do |name, primitive|
61
- register("coercible.#{name}",
62
- self["nominal.#{name}"].constructor(Kernel.method(primitive.name)))
63
+ register(
64
+ "coercible.#{name}",
65
+ self["nominal.#{name}"].constructor(::Kernel.method(primitive.name))
66
+ )
63
67
  end
64
68
 
65
69
  # Register {METHOD_COERCIBLE} types
66
70
  METHOD_COERCIBLE.each_key do |name|
67
71
  register(
68
- "coercible.#{name}", self["nominal.#{name}"].constructor(&METHOD_COERCIBLE_METHODS[name])
72
+ "coercible.#{name}",
73
+ self["nominal.#{name}"].constructor(&METHOD_COERCIBLE_METHODS[name])
69
74
  )
70
75
  end
71
76
 
@@ -78,7 +83,10 @@ module Dry
78
83
 
79
84
  # Register optional {COERCIBLE} types
80
85
  COERCIBLE.each_key do |name|
81
- register("optional.coercible.#{name}", self["coercible.#{name}"].optional)
86
+ register(
87
+ "optional.coercible.#{name}",
88
+ self["coercible.#{name}"].optional
89
+ )
82
90
  end
83
91
 
84
92
  # Register `:bool` since it's common and not a built-in Ruby type :(
@@ -24,23 +24,17 @@ module Dry
24
24
  # @return [Object] if block given and try fails
25
25
  #
26
26
  # @api public
27
- def try(input, &block)
28
- type.try(input, &block)
29
- end
27
+ def try(input, &) = type.try(input, &)
30
28
 
31
29
  # @return [Boolean]
32
30
  #
33
31
  # @api public
34
- def default?
35
- type.default?
36
- end
32
+ def default? = type.default?
37
33
 
38
34
  # @return [Boolean]
39
35
  #
40
36
  # @api public
41
- def constrained?
42
- type.constrained?
43
- end
37
+ def constrained? = type.constrained?
44
38
 
45
39
  # @param [Symbol] meth
46
40
  # @param [Boolean] include_private
@@ -57,9 +51,7 @@ module Dry
57
51
  # @return [Proc]
58
52
  #
59
53
  # @api public
60
- def to_proc
61
- proc { |value| self.(value) }
62
- end
54
+ def to_proc = proc { |value| self.(value) }
63
55
 
64
56
  private
65
57
 
@@ -68,9 +60,7 @@ module Dry
68
60
  # @return [Boolean]
69
61
  #
70
62
  # @api private
71
- def decorate?(response)
72
- response.is_a?(type.class)
73
- end
63
+ def decorate?(response) = response.is_a?(type.class)
74
64
 
75
65
  # Delegates missing methods to {#type}
76
66
  #
@@ -79,9 +69,9 @@ module Dry
79
69
  # @param [#call, nil] block
80
70
  #
81
71
  # @api private
82
- def method_missing(meth, *args, &block)
72
+ def method_missing(meth, ...)
83
73
  if type.respond_to?(meth)
84
- response = type.public_send(meth, *args, &block)
74
+ response = type.public_send(meth, ...)
85
75
 
86
76
  if decorate?(response)
87
77
  __new__(response)
@@ -92,7 +82,6 @@ module Dry
92
82
  super
93
83
  end
94
84
  end
95
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
96
85
 
97
86
  # Replace underlying type
98
87
  #