dry-types 1.2.1 → 1.5.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +405 -221
  3. data/LICENSE +1 -1
  4. data/README.md +14 -13
  5. data/dry-types.gemspec +26 -31
  6. data/lib/dry-types.rb +1 -1
  7. data/lib/dry/types.rb +55 -40
  8. data/lib/dry/types/any.rb +2 -2
  9. data/lib/dry/types/array.rb +2 -2
  10. data/lib/dry/types/array/constructor.rb +1 -1
  11. data/lib/dry/types/array/member.rb +1 -1
  12. data/lib/dry/types/builder.rb +70 -18
  13. data/lib/dry/types/builder_methods.rb +6 -3
  14. data/lib/dry/types/coercions.rb +6 -17
  15. data/lib/dry/types/coercions/json.rb +22 -5
  16. data/lib/dry/types/coercions/params.rb +21 -4
  17. data/lib/dry/types/compiler.rb +10 -10
  18. data/lib/dry/types/constrained.rb +6 -10
  19. data/lib/dry/types/constraints.rb +3 -3
  20. data/lib/dry/types/constructor.rb +40 -7
  21. data/lib/dry/types/constructor/function.rb +47 -32
  22. data/lib/dry/types/constructor/wrapper.rb +94 -0
  23. data/lib/dry/types/container.rb +1 -1
  24. data/lib/dry/types/core.rb +15 -13
  25. data/lib/dry/types/decorator.rb +2 -9
  26. data/lib/dry/types/default.rb +15 -3
  27. data/lib/dry/types/enum.rb +4 -4
  28. data/lib/dry/types/errors.rb +6 -6
  29. data/lib/dry/types/extensions.rb +2 -2
  30. data/lib/dry/types/extensions/maybe.rb +17 -17
  31. data/lib/dry/types/extensions/monads.rb +1 -1
  32. data/lib/dry/types/fn_container.rb +1 -1
  33. data/lib/dry/types/hash.rb +9 -15
  34. data/lib/dry/types/hash/constructor.rb +1 -1
  35. data/lib/dry/types/inflector.rb +1 -1
  36. data/lib/dry/types/json.rb +15 -15
  37. data/lib/dry/types/lax.rb +4 -7
  38. data/lib/dry/types/map.rb +2 -2
  39. data/lib/dry/types/meta.rb +3 -3
  40. data/lib/dry/types/module.rb +6 -6
  41. data/lib/dry/types/nominal.rb +11 -12
  42. data/lib/dry/types/params.rb +31 -28
  43. data/lib/dry/types/predicate_inferrer.rb +52 -11
  44. data/lib/dry/types/predicate_registry.rb +1 -1
  45. data/lib/dry/types/primitive_inferrer.rb +1 -1
  46. data/lib/dry/types/printer.rb +25 -25
  47. data/lib/dry/types/result.rb +3 -3
  48. data/lib/dry/types/schema.rb +26 -13
  49. data/lib/dry/types/schema/key.rb +20 -7
  50. data/lib/dry/types/spec/types.rb +65 -42
  51. data/lib/dry/types/sum.rb +6 -6
  52. data/lib/dry/types/type.rb +1 -1
  53. data/lib/dry/types/version.rb +1 -1
  54. metadata +27 -84
  55. data/.codeclimate.yml +0 -12
  56. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  57. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
  58. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  59. data/.github/workflows/custom_ci.yml +0 -76
  60. data/.github/workflows/docsite.yml +0 -34
  61. data/.github/workflows/sync_configs.yml +0 -34
  62. data/.gitignore +0 -11
  63. data/.rspec +0 -4
  64. data/.rubocop.yml +0 -89
  65. data/.yardopts +0 -9
  66. data/CODE_OF_CONDUCT.md +0 -13
  67. data/CONTRIBUTING.md +0 -29
  68. data/Gemfile +0 -32
  69. data/Rakefile +0 -22
  70. data/benchmarks/hash_schemas.rb +0 -55
  71. data/benchmarks/lax_schema.rb +0 -15
  72. data/benchmarks/profile_invalid_input.rb +0 -15
  73. data/benchmarks/profile_lax_schema_valid.rb +0 -16
  74. data/benchmarks/profile_valid_input.rb +0 -15
  75. data/benchmarks/schema_valid_vs_invalid.rb +0 -21
  76. data/benchmarks/setup.rb +0 -17
  77. data/docsite/source/array-with-member.html.md +0 -13
  78. data/docsite/source/built-in-types.html.md +0 -116
  79. data/docsite/source/constraints.html.md +0 -31
  80. data/docsite/source/custom-types.html.md +0 -93
  81. data/docsite/source/default-values.html.md +0 -91
  82. data/docsite/source/enum.html.md +0 -69
  83. data/docsite/source/extensions.html.md +0 -15
  84. data/docsite/source/extensions/maybe.html.md +0 -57
  85. data/docsite/source/extensions/monads.html.md +0 -61
  86. data/docsite/source/getting-started.html.md +0 -57
  87. data/docsite/source/hash-schemas.html.md +0 -169
  88. data/docsite/source/index.html.md +0 -156
  89. data/docsite/source/map.html.md +0 -17
  90. data/docsite/source/optional-values.html.md +0 -35
  91. data/docsite/source/sum.html.md +0 -21
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # @api public
6
+ class Constructor < Nominal
7
+ module Wrapper
8
+ # @return [Object]
9
+ #
10
+ # @api private
11
+ def call_safe(input, &block)
12
+ fn.(input, type, &block)
13
+ end
14
+
15
+ # @return [Object]
16
+ #
17
+ # @api private
18
+ def call_unsafe(input)
19
+ fn.(input, type)
20
+ end
21
+
22
+ # @param [Object] input
23
+ # @param [#call,nil] block
24
+ #
25
+ # @return [Logic::Result, Types::Result]
26
+ # @return [Object] if block given and try fails
27
+ #
28
+ # @api public
29
+ def try(input, &block)
30
+ value = fn.(input, type)
31
+ rescue CoercionError => e
32
+ failure = failure(input, e)
33
+ block_given? ? yield(failure) : failure
34
+ else
35
+ type.try(value, &block)
36
+ end
37
+
38
+ # Define a constructor for the type
39
+ #
40
+ # @param [#call,nil] constructor
41
+ # @param [Hash] options
42
+ # @param [#call,nil] block
43
+ #
44
+ # @return [Constructor]
45
+ #
46
+ # @api public
47
+ define_method(:constructor, Builder.instance_method(:constructor))
48
+ alias_method :append, :constructor
49
+ alias_method :>>, :constructor
50
+
51
+ # Build a new constructor by prepending a block to the coercion function
52
+ #
53
+ # @param [#call, nil] new_fn
54
+ # @param [Hash] options
55
+ # @param [#call, nil] block
56
+ #
57
+ # @return [Constructor]
58
+ #
59
+ # @api public
60
+ def prepend(new_fn = nil, **options, &block)
61
+ prep_fn = Function[new_fn || block]
62
+
63
+ decorated =
64
+ if prep_fn.wrapper?
65
+ type.constructor(prep_fn, **options)
66
+ else
67
+ type.prepend(prep_fn, **options)
68
+ end
69
+
70
+ __new__(decorated)
71
+ end
72
+ alias_method :<<, :prepend
73
+
74
+ # @return [Constructor]
75
+ #
76
+ # @api public
77
+ def lax
78
+ # return self back because wrapping function
79
+ # can handle failed type check
80
+ self
81
+ end
82
+
83
+ private
84
+
85
+ # Replace underlying type
86
+ #
87
+ # @api private
88
+ def __new__(type)
89
+ self.class.new(type, *@__args__.drop(1), **@options)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/container'
3
+ require "dry/container"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/any'
3
+ require "dry/types/any"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -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
@@ -81,17 +83,17 @@ module Dry
81
83
  end
82
84
 
83
85
  # Register `:bool` since it's common and not a built-in Ruby type :(
84
- register('nominal.bool', self['nominal.true'] | self['nominal.false'])
85
- bool = self['strict.true'] | self['strict.false']
86
- register('strict.bool', bool)
87
- register('bool', bool)
88
-
89
- register('any', Any)
90
- register('nominal.any', Any)
91
- register('strict.any', Any)
86
+ register("nominal.bool", self["nominal.true"] | self["nominal.false"])
87
+ bool = self["strict.true"] | self["strict.false"]
88
+ register("strict.bool", bool)
89
+ register("bool", bool)
90
+
91
+ register("any", Any)
92
+ register("nominal.any", Any)
93
+ register("strict.any", Any)
92
94
  end
93
95
  end
94
96
 
95
- require 'dry/types/coercions'
96
- require 'dry/types/params'
97
- require 'dry/types/json'
97
+ require "dry/types/coercions"
98
+ require "dry/types/params"
99
+ require "dry/types/json"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/options'
3
+ require "dry/types/options"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -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
  #
@@ -107,7 +100,7 @@ module Dry
107
100
  #
108
101
  # @api private
109
102
  def __new__(type)
110
- self.class.new(type, *@__args__[1..-1], **@options)
103
+ self.class.new(type, *@__args__.drop(1), **@options)
111
104
  end
112
105
  end
113
106
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/decorator'
3
+ require "dry/types/decorator"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -10,20 +10,25 @@ 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]
17
17
  def evaluate
18
18
  value.call(type)
19
19
  end
20
+
21
+ # @return [true]
22
+ def callable?
23
+ true
24
+ end
20
25
  end
21
26
 
22
27
  include Type
23
28
  include Decorator
24
29
  include Builder
25
30
  include Printable
26
- include Dry::Equalizer(:type, :value, inspect: false)
31
+ include Dry::Equalizer(:type, :value, inspect: false, immutable: true)
27
32
 
28
33
  # @return [Object]
29
34
  attr_reader :value
@@ -111,6 +116,13 @@ module Dry
111
116
  Undefined.default(type.call_safe(input, &block)) { evaluate }
112
117
  end
113
118
  end
119
+
120
+ # @return [false]
121
+ #
122
+ # @api private
123
+ def callable?
124
+ false
125
+ end
114
126
  end
115
127
  end
116
128
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/decorator'
3
+ require "dry/types/decorator"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -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
 
@@ -58,8 +58,8 @@ module Dry
58
58
 
59
59
  # @api private
60
60
  def default(*)
61
- raise '.enum(*values).default(value) is not supported. Call '\
62
- '.default(value).enum(*values) instead'
61
+ raise ".enum(*values).default(value) is not supported. Call "\
62
+ ".default(value).enum(*values) instead"
63
63
  end
64
64
 
65
65
  # Check whether a value is in the enum
@@ -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)
@@ -56,7 +56,7 @@ module Dry
56
56
 
57
57
  # @return string
58
58
  def message
59
- errors.map(&:message).join(', ')
59
+ errors.map(&:message).join(", ")
60
60
  end
61
61
 
62
62
  # @return [Array]
@@ -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
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Dry::Types.register_extension(:maybe) do
4
- require 'dry/types/extensions/maybe'
4
+ require "dry/types/extensions/maybe"
5
5
  end
6
6
 
7
7
  Dry::Types.register_extension(:monads) do
8
- require 'dry/types/extensions/monads'
8
+ require "dry/types/extensions/monads"
9
9
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/maybe'
4
- require 'dry/types/decorator'
3
+ require "dry/monads/maybe"
4
+ require "dry/types/decorator"
5
5
 
6
6
  module Dry
7
7
  module Types
@@ -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]
@@ -79,7 +79,7 @@ module Dry
79
79
  # @api public
80
80
  def default(value)
81
81
  if value.nil?
82
- raise ArgumentError, 'nil cannot be used as a default of a maybe type'
82
+ raise ArgumentError, "nil cannot be used as a default of a maybe type"
83
83
  else
84
84
  super
85
85
  end
@@ -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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/result'
3
+ require "dry/monads/result"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/container'
3
+ require "dry/types/container"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/hash/constructor'
3
+ require "dry/types/hash/constructor"
4
4
 
5
5
  module Dry
6
6
  module Types
@@ -8,7 +8,7 @@ module Dry
8
8
  #
9
9
  # @api public
10
10
  class Hash < Nominal
11
- NOT_REQUIRED = { required: false }.freeze
11
+ NOT_REQUIRED = {required: false}.freeze
12
12
 
13
13
  # @overload schema(type_map, meta = EMPTY_HASH)
14
14
  # @param [{Symbol => Dry::Types::Nominal}] type_map
@@ -50,8 +50,8 @@ module Dry
50
50
 
51
51
  # @api private
52
52
  def weak(*)
53
- raise 'Support for old hash schemas was removed, please refer to the CHANGELOG '\
54
- 'on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md'
53
+ raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
54
+ "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
55
55
  end
56
56
  alias_method :permissive, :weak
57
57
  alias_method :strict, :weak
@@ -69,7 +69,7 @@ module Dry
69
69
  def with_type_transform(proc = nil, &block)
70
70
  fn = proc || block
71
71
 
72
- raise ArgumentError, 'a block or callable argument is required' if fn.nil?
72
+ raise ArgumentError, "a block or callable argument is required" if fn.nil?
73
73
 
74
74
  handle = Dry::Types::FnContainer.register(fn)
75
75
  with(type_transform_fn: handle)
@@ -95,13 +95,7 @@ module Dry
95
95
  #
96
96
  # @api public
97
97
  def to_ast(meta: true)
98
- opts = if RUBY_VERSION >= '2.5'
99
- options.slice(:type_transform_fn)
100
- else
101
- options.select { |k, _| k == :type_transform_fn }
102
- end
103
-
104
- [:hash, [opts, meta ? self.meta : EMPTY_HASH]]
98
+ [:hash, [options.slice(:type_transform_fn), meta ? self.meta : EMPTY_HASH]]
105
99
  end
106
100
 
107
101
  private
@@ -129,7 +123,7 @@ module Dry
129
123
 
130
124
  # @api private
131
125
  def key_name(key)
132
- if key.to_s.end_with?('?')
126
+ if key.to_s.end_with?("?")
133
127
  [key.to_s.chop.to_sym, NOT_REQUIRED]
134
128
  else
135
129
  [key, EMPTY_HASH]
@@ -139,5 +133,5 @@ module Dry
139
133
  end
140
134
  end
141
135
 
142
- require 'dry/types/schema/key'
143
- require 'dry/types/schema'
136
+ require "dry/types/schema/key"
137
+ require "dry/types/schema"