dry-types 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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