dry-types 1.1.0 → 1.3.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +378 -206
  3. data/LICENSE +17 -17
  4. data/README.md +14 -13
  5. data/dry-types.gemspec +27 -31
  6. data/lib/dry/types.rb +7 -11
  7. data/lib/dry/types/any.rb +2 -2
  8. data/lib/dry/types/array/member.rb +3 -3
  9. data/lib/dry/types/builder.rb +5 -1
  10. data/lib/dry/types/builder_methods.rb +22 -8
  11. data/lib/dry/types/coercions.rb +6 -0
  12. data/lib/dry/types/coercions/params.rb +1 -1
  13. data/lib/dry/types/compiler.rb +2 -2
  14. data/lib/dry/types/constrained.rb +2 -2
  15. data/lib/dry/types/constructor.rb +7 -34
  16. data/lib/dry/types/constructor/function.rb +17 -31
  17. data/lib/dry/types/core.rb +3 -1
  18. data/lib/dry/types/decorator.rb +3 -9
  19. data/lib/dry/types/default.rb +2 -2
  20. data/lib/dry/types/enum.rb +2 -2
  21. data/lib/dry/types/errors.rb +5 -5
  22. data/lib/dry/types/extensions.rb +4 -0
  23. data/lib/dry/types/extensions/maybe.rb +14 -14
  24. data/lib/dry/types/extensions/monads.rb +29 -0
  25. data/lib/dry/types/hash.rb +3 -2
  26. data/lib/dry/types/lax.rb +2 -5
  27. data/lib/dry/types/meta.rb +3 -3
  28. data/lib/dry/types/module.rb +3 -3
  29. data/lib/dry/types/nominal.rb +1 -1
  30. data/lib/dry/types/params.rb +6 -0
  31. data/lib/dry/types/predicate_inferrer.rb +197 -0
  32. data/lib/dry/types/predicate_registry.rb +34 -0
  33. data/lib/dry/types/primitive_inferrer.rb +97 -0
  34. data/lib/dry/types/printer.rb +7 -3
  35. data/lib/dry/types/result.rb +2 -2
  36. data/lib/dry/types/schema.rb +23 -2
  37. data/lib/dry/types/schema/key.rb +16 -3
  38. data/lib/dry/types/spec/types.rb +14 -1
  39. data/lib/dry/types/sum.rb +5 -5
  40. data/lib/dry/types/version.rb +1 -1
  41. metadata +26 -45
  42. data/.codeclimate.yml +0 -15
  43. data/.gitignore +0 -11
  44. data/.rspec +0 -2
  45. data/.travis.yml +0 -27
  46. data/.yardopts +0 -9
  47. data/CONTRIBUTING.md +0 -29
  48. data/Gemfile +0 -27
  49. data/Rakefile +0 -22
  50. data/benchmarks/hash_schemas.rb +0 -55
  51. data/benchmarks/lax_schema.rb +0 -15
  52. data/benchmarks/profile_invalid_input.rb +0 -15
  53. data/benchmarks/profile_lax_schema_valid.rb +0 -16
  54. data/benchmarks/profile_valid_input.rb +0 -15
  55. data/benchmarks/schema_valid_vs_invalid.rb +0 -21
  56. data/benchmarks/setup.rb +0 -17
@@ -15,7 +15,7 @@ module Dry
15
15
  class Safe < Function
16
16
  def call(input, &block)
17
17
  @fn.(input, &block)
18
- rescue NoMethodError, TypeError, ArgumentError => e
18
+ rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
19
19
  CoercionError.handle(e, &block)
20
20
  end
21
21
  end
@@ -30,7 +30,7 @@ module Dry
30
30
  #
31
31
  # @return [Function]
32
32
  def self.call_class(method, public, safe)
33
- @cache.fetch_or_store([method, public, safe].hash) do
33
+ @cache.fetch_or_store([method, public, safe]) do
34
34
  if public
35
35
  ::Class.new(PublicCall) do
36
36
  include PublicCall.call_interface(method, safe)
@@ -53,7 +53,7 @@ module Dry
53
53
  #
54
54
  # @return [::Module]
55
55
  def self.call_interface(method, safe)
56
- @interfaces.fetch_or_store([method, safe].hash) do
56
+ @interfaces.fetch_or_store([method, safe]) do
57
57
  ::Module.new do
58
58
  if safe
59
59
  module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
@@ -65,7 +65,7 @@ module Dry
65
65
  module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
66
66
  def call(input, &block)
67
67
  @target.#{method}(input)
68
- rescue NoMethodError, TypeError, ArgumentError => error
68
+ rescue ::NoMethodError, ::TypeError, ::ArgumentError => error
69
69
  CoercionError.handle(error, &block)
70
70
  end
71
71
  RUBY
@@ -90,7 +90,7 @@ module Dry
90
90
  class PrivateSafeCall < PrivateCall
91
91
  def call(input, &block)
92
92
  @target.send(@name, input)
93
- rescue NoMethodError, TypeError, ArgumentError => e
93
+ rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
94
94
  CoercionError.handle(e, &block)
95
95
  end
96
96
  end
@@ -121,7 +121,7 @@ module Dry
121
121
  # @param [#call] fn
122
122
  # @return [Function]
123
123
  def self.[](fn)
124
- raise ArgumentError, 'Missing constructor block' if fn.nil?
124
+ raise ::ArgumentError, 'Missing constructor block' if fn.nil?
125
125
 
126
126
  if fn.is_a?(Function)
127
127
  fn
@@ -146,7 +146,7 @@ module Dry
146
146
  last_arg.equal?(:block)
147
147
  end
148
148
 
149
- include Dry::Equalizer(:fn)
149
+ include ::Dry::Equalizer(:fn, immutable: true)
150
150
 
151
151
  attr_reader :fn
152
152
 
@@ -163,36 +163,22 @@ module Dry
163
163
  # @return [Array]
164
164
  def to_ast
165
165
  if fn.is_a?(::Proc)
166
- [:id, Dry::Types::FnContainer.register(fn)]
166
+ [:id, FnContainer.register(fn)]
167
167
  else
168
168
  [:callable, fn]
169
169
  end
170
170
  end
171
171
 
172
- if RUBY_VERSION >= '2.6'
173
- # @return [Function]
174
- def >>(other)
175
- proc = other.is_a?(::Proc) ? other : other.fn
176
- Function[@fn >> proc]
177
- end
178
-
179
- # @return [Function]
180
- def <<(other)
181
- proc = other.is_a?(::Proc) ? other : other.fn
182
- Function[@fn << proc]
183
- end
184
- else
185
- # @return [Function]
186
- def >>(other)
187
- proc = other.is_a?(::Proc) ? other : other.fn
188
- Function[-> x { proc[@fn[x]] }]
189
- end
172
+ # @return [Function]
173
+ def >>(other)
174
+ f = Function[other]
175
+ Function[-> x, &b { f.(self.(x, &b), &b) }]
176
+ end
190
177
 
191
- # @return [Function]
192
- def <<(other)
193
- proc = other.is_a?(::Proc) ? other : other.fn
194
- Function[-> x { @fn[proc[x]] }]
195
- end
178
+ # @return [Function]
179
+ def <<(other)
180
+ f = Function[other]
181
+ Function[-> x, &b { self.(f.(x, &b), &b) }]
196
182
  end
197
183
  end
198
184
  end
@@ -72,7 +72,9 @@ module Dry
72
72
 
73
73
  # Register optional strict {NON_NIL} types
74
74
  NON_NIL.each_key do |name|
75
- register("optional.strict.#{name}", self["strict.#{name}"].optional)
75
+ type = self[name.to_s].optional
76
+ register("optional.strict.#{name}", type)
77
+ register("optional.#{name}", type)
76
78
  end
77
79
 
78
80
  # Register optional {COERCIBLE} types
@@ -14,7 +14,7 @@ module Dry
14
14
  attr_reader :type
15
15
 
16
16
  # @param [Type] type
17
- def initialize(type, *)
17
+ def initialize(type, *, **)
18
18
  super
19
19
  @type = type
20
20
  end
@@ -44,13 +44,6 @@ module Dry
44
44
  type.constrained?
45
45
  end
46
46
 
47
- # @return [Sum]
48
- #
49
- # @api public
50
- def optional
51
- Types['strict.nil'] | self
52
- end
53
-
54
47
  # @param [Symbol] meth
55
48
  # @param [Boolean] include_private
56
49
  #
@@ -90,7 +83,7 @@ module Dry
90
83
  # @api private
91
84
  def method_missing(meth, *args, &block)
92
85
  if type.respond_to?(meth)
93
- response = type.__send__(meth, *args, &block)
86
+ response = type.public_send(meth, *args, &block)
94
87
 
95
88
  if decorate?(response)
96
89
  __new__(response)
@@ -101,6 +94,7 @@ module Dry
101
94
  super
102
95
  end
103
96
  end
97
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
104
98
 
105
99
  # Replace underlying type
106
100
  #
@@ -10,7 +10,7 @@ module Dry
10
10
  class Default
11
11
  # @api private
12
12
  class Callable < Default
13
- include Dry::Equalizer(:type, inspect: false)
13
+ include Dry::Equalizer(:type, inspect: false, immutable: true)
14
14
 
15
15
  # Evaluates given callable
16
16
  # @return [Object]
@@ -23,7 +23,7 @@ module Dry
23
23
  include Decorator
24
24
  include Builder
25
25
  include Printable
26
- include Dry::Equalizer(:type, :value, inspect: false)
26
+ include Dry::Equalizer(:type, :value, inspect: false, immutable: true)
27
27
 
28
28
  # @return [Object]
29
29
  attr_reader :value
@@ -9,7 +9,7 @@ module Dry
9
9
  # @api public
10
10
  class Enum
11
11
  include Type
12
- include Dry::Equalizer(:type, :mapping, inspect: false)
12
+ include Dry::Equalizer(:type, :mapping, inspect: false, immutable: true)
13
13
  include Decorator
14
14
  include Builder
15
15
 
@@ -27,7 +27,7 @@ module Dry
27
27
  # @option options [Array] :values
28
28
  #
29
29
  # @api private
30
- def initialize(type, options)
30
+ def initialize(type, **options)
31
31
  super
32
32
  @mapping = options.fetch(:mapping).freeze
33
33
  @values = @mapping.keys.freeze
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Dry
4
4
  module Types
5
- extend Dry::Core::ClassAttributes
5
+ extend ::Dry::Core::ClassAttributes
6
6
 
7
7
  # @!attribute [r] namespace
8
8
  # @return [Container{String => Nominal}]
@@ -12,7 +12,7 @@ module Dry
12
12
 
13
13
  # Base class for coercion errors raise by dry-types
14
14
  #
15
- class CoercionError < StandardError
15
+ class CoercionError < ::StandardError
16
16
  # @api private
17
17
  def self.handle(exception, meta: Undefined)
18
18
  if block_given?
@@ -34,7 +34,7 @@ module Dry
34
34
  # @api private
35
35
  def initialize(message, meta: Undefined, backtrace: Undefined)
36
36
  unless message.is_a?(::String)
37
- raise ArgumentError, "message must be a string, #{message.class} given"
37
+ raise ::ArgumentError, "message must be a string, #{message.class} given"
38
38
  end
39
39
 
40
40
  super(message)
@@ -74,9 +74,9 @@ module Dry
74
74
  end
75
75
  end
76
76
 
77
- MapError = Class.new(CoercionError)
77
+ MapError = ::Class.new(CoercionError)
78
78
 
79
- SchemaKeyError = Class.new(CoercionError)
79
+ SchemaKeyError = ::Class.new(CoercionError)
80
80
  private_constant(:SchemaKeyError)
81
81
 
82
82
  class MissingKeyError < SchemaKeyError
@@ -3,3 +3,7 @@
3
3
  Dry::Types.register_extension(:maybe) do
4
4
  require 'dry/types/extensions/maybe'
5
5
  end
6
+
7
+ Dry::Types.register_extension(:monads) do
8
+ require 'dry/types/extensions/monads'
9
+ end
@@ -10,11 +10,11 @@ module Dry
10
10
  # @api public
11
11
  class Maybe
12
12
  include Type
13
- include Dry::Equalizer(:type, :options, inspect: false)
13
+ include ::Dry::Equalizer(:type, :options, inspect: false, immutable: true)
14
14
  include Decorator
15
15
  include Builder
16
16
  include Printable
17
- include Dry::Monads::Maybe::Mixin
17
+ include ::Dry::Monads::Maybe::Mixin
18
18
 
19
19
  # @param [Dry::Monads::Maybe, Object] input
20
20
  #
@@ -23,7 +23,7 @@ module Dry
23
23
  # @api private
24
24
  def call_unsafe(input = Undefined)
25
25
  case input
26
- when Dry::Monads::Maybe
26
+ when ::Dry::Monads::Maybe
27
27
  input
28
28
  when Undefined
29
29
  None()
@@ -37,14 +37,14 @@ module Dry
37
37
  # @return [Dry::Monads::Maybe]
38
38
  #
39
39
  # @api private
40
- def call_safe(input = Undefined, &block)
40
+ def call_safe(input = Undefined)
41
41
  case input
42
- when Dry::Monads::Maybe
42
+ when ::Dry::Monads::Maybe
43
43
  input
44
44
  when Undefined
45
45
  None()
46
46
  else
47
- Maybe(type.call_safe(input, &block))
47
+ Maybe(type.call_safe(input) { |output = input| return yield(output) })
48
48
  end
49
49
  end
50
50
 
@@ -54,13 +54,13 @@ module Dry
54
54
  #
55
55
  # @api public
56
56
  def try(input = Undefined)
57
- res = if input.equal?(Undefined)
58
- None()
59
- else
60
- Maybe(type[input])
61
- end
57
+ result = type.try(input)
62
58
 
63
- Result::Success.new(res)
59
+ if result.success?
60
+ Result::Success.new(Maybe(result.input))
61
+ else
62
+ result
63
+ end
64
64
  end
65
65
 
66
66
  # @return [true]
@@ -93,7 +93,7 @@ module Dry
93
93
  #
94
94
  # @api public
95
95
  def maybe
96
- Maybe.new(Types['strict.nil'] | self)
96
+ Maybe.new(Types['nil'] | self)
97
97
  end
98
98
  end
99
99
 
@@ -119,7 +119,7 @@ module Dry
119
119
 
120
120
  # Register non-coercible maybe types
121
121
  NON_NIL.each_key do |name|
122
- register("maybe.strict.#{name}", self["strict.#{name}"].maybe)
122
+ register("maybe.strict.#{name}", self[name.to_s].maybe)
123
123
  end
124
124
 
125
125
  # Register coercible maybe types
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/monads/result'
4
+
5
+ module Dry
6
+ module Types
7
+ # Monad extension for Result
8
+ #
9
+ # @api public
10
+ class Result
11
+ include Dry::Monads::Result::Mixin
12
+
13
+ # Turn result into a monad
14
+ #
15
+ # This makes result objects work with dry-monads (or anything with a compatible interface)
16
+ #
17
+ # @return [Dry::Monads::Success,Dry::Monads::Failure]
18
+ #
19
+ # @api public
20
+ def to_monad
21
+ if success?
22
+ Success(input)
23
+ else
24
+ Failure([error, input])
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -113,7 +113,7 @@ module Dry
113
113
 
114
114
  type_map.map do |map_key, type|
115
115
  name, options = key_name(map_key)
116
- key = Schema::Key.new(resolve_type(type), name, options)
116
+ key = Schema::Key.new(resolve_type(type), name, **options)
117
117
  type_transform.(key)
118
118
  end
119
119
  end
@@ -121,7 +121,8 @@ module Dry
121
121
  # @api private
122
122
  def resolve_type(type)
123
123
  case type
124
- when String, Class then Types[type]
124
+ when Type then type
125
+ when ::Class, ::String then Types[type]
125
126
  else type
126
127
  end
127
128
  end
data/lib/dry/types/lax.rb CHANGED
@@ -13,9 +13,9 @@ module Dry
13
13
  include Decorator
14
14
  include Builder
15
15
  include Printable
16
- include Dry::Equalizer(:type, inspect: false)
16
+ include Dry::Equalizer(:type, inspect: false, immutable: true)
17
17
 
18
- private :options, :constructor
18
+ undef :options, :constructor, :<<, :>>, :prepend, :append
19
19
 
20
20
  # @param [Object] input
21
21
  #
@@ -40,9 +40,6 @@ module Dry
40
40
  # @api public
41
41
  def try(input, &block)
42
42
  type.try(input, &block)
43
- rescue CoercionError => e
44
- result = failure(input, e.message)
45
- block ? yield(result) : result
46
43
  end
47
44
 
48
45
  # @see Nominal#to_ast
@@ -16,7 +16,7 @@ module Dry
16
16
  # @return [Type]
17
17
  #
18
18
  # @api public
19
- def with(options)
19
+ def with(**options)
20
20
  super(meta: @meta, **options)
21
21
  end
22
22
 
@@ -28,8 +28,8 @@ module Dry
28
28
  # @return [Type] new type with added metadata
29
29
  #
30
30
  # @api public
31
- def meta(data = nil)
32
- if !data
31
+ def meta(data = Undefined)
32
+ if Undefined.equal?(data)
33
33
  @meta
34
34
  elsif data.empty?
35
35
  self
@@ -18,10 +18,10 @@ module Dry
18
18
  #
19
19
  # @api public
20
20
  class Module < ::Module
21
- def initialize(registry, *args)
21
+ def initialize(registry, *args, **kwargs)
22
22
  @registry = registry
23
- check_parameters(*args)
24
- constants = type_constants(*args)
23
+ check_parameters(*args, **kwargs)
24
+ constants = type_constants(*args, **kwargs)
25
25
  define_constants(constants)
26
26
  extend(BuilderMethods)
27
27
 
@@ -19,7 +19,7 @@ module Dry
19
19
  include Meta
20
20
  include Builder
21
21
  include Printable
22
- include Dry::Equalizer(:primitive, :options, :meta, inspect: false)
22
+ include Dry::Equalizer(:primitive, :options, :meta, inspect: false, immutable: true)
23
23
 
24
24
  # @return [Class]
25
25
  attr_reader :primitive
@@ -55,5 +55,11 @@ module Dry
55
55
  register('params.symbol') do
56
56
  self['nominal.symbol'].constructor(Coercions::Params.method(:to_symbol))
57
57
  end
58
+
59
+ COERCIBLE.each_key do |name|
60
+ next if name.equal?(:string)
61
+
62
+ register("optional.params.#{name}", self['params.nil'] | self["params.#{name}"])
63
+ end
58
64
  end
59
65
  end