dry-types 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9875a9faf12df26231bd206db5e75e78cdb104277f34947b01e0d104537b63b4
4
- data.tar.gz: 8358caef2ad9e5f83b635531b512f507a9b1776b26ba39f3b69090c6edcc1e71
3
+ metadata.gz: fdd7e08f86f3137ead67bb6170e91de625227e97783be335b982e5a4c313b32e
4
+ data.tar.gz: faa51ae68eb4942cb64520eed7d6c4dbf5b46af384a91acdfc94105f32298839
5
5
  SHA512:
6
- metadata.gz: 5bab1e2bb31fc25d9eb8765177717f985d433115cf756bcda8cdedf201bfca90627c942d5183962f428c7c2d67789b08ac95b2e3e0b741c7cccbd39d13ab0ba6
7
- data.tar.gz: caa7de7d979423fd8d8b5dd0b64642104f40029dcfeec7fe81ff05ef516c76d4bef1a59e341d0580b15022429a3f0d8a89c2f6170fdab0f398c330f157e4cf43
6
+ metadata.gz: 588feadceca555b168266b6e6ee542d71b154152ea56a88f7b601c33a79b116c90b86356e03bc8ca273e4df5d38e4056277c88502387e836bba4af8409fede14
7
+ data.tar.gz: 72326eb3f32d33cbc95d1e20d6bec4f1bc40a66fa6cac818d35b8ff2214d3932f3350d5e6cf36a6a49ea3ffd862d7422a49622d5a98937a0be6f914d0f390965
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
+ ## 1.7.0 2022-11-04
4
+
5
+
6
+ ### Changed
7
+
8
+ - This version is compatible with recently released dry-rb dependencies (@flash-gordon)
9
+ - Updated to dry-core 1.0 (@flash-gordon + @solnic)
10
+ - Dependency on dry-container was dropped (@flash-gordon)
11
+
12
+ [Compare v1.6.1...v1.7.0](https://github.com/dry-rb/dry-types/compare/v1.6.1...v1.7.0)
13
+
14
+ ## 1.6.1 2022-10-15
15
+
16
+
17
+ ### Changed
18
+
19
+ - Fix issues with internal const_missing and Inflector/Module constants (@flash-gordon + @solnic)
20
+
21
+ [Compare v1.6.0...v1.6.1](https://github.com/dry-rb/dry-types/compare/v1.6.0...v1.6.1)
22
+
3
23
  ## 1.6.0 2022-10-15
4
24
 
5
25
 
data/dry-types.gemspec CHANGED
@@ -30,14 +30,12 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  # to update dependencies edit project.yml
32
32
  spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
33
- spec.add_runtime_dependency "dry-container", "~> 0.3"
34
- spec.add_runtime_dependency "dry-core", "~> 0.9", ">= 0.9"
35
- spec.add_runtime_dependency "dry-inflector", "~> 0.1", ">= 0.1.2"
36
- spec.add_runtime_dependency "dry-logic", "~> 1.3", ">= 1.3"
33
+ spec.add_runtime_dependency "dry-core", "~> 1.0", "< 2"
34
+ spec.add_runtime_dependency "dry-inflector", "~> 1.0", "< 2"
35
+ spec.add_runtime_dependency "dry-logic", ">= 1.4", "< 2"
37
36
  spec.add_runtime_dependency "zeitwerk", "~> 2.6"
38
37
 
39
38
  spec.add_development_dependency "bundler"
40
- spec.add_development_dependency "dry-monads", "~> 1.0"
41
39
  spec.add_development_dependency "rake"
42
40
  spec.add_development_dependency "rspec"
43
41
  spec.add_development_dependency "yard"
@@ -5,6 +5,7 @@ module Dry
5
5
  # Common API for building types and composition
6
6
  #
7
7
  # @api public
8
+ # rubocop:disable Metrics/ModuleLength
8
9
  module Builder
9
10
  include Dry::Core::Constants
10
11
 
@@ -30,8 +31,29 @@ module Dry
30
31
  #
31
32
  # @api private
32
33
  def |(other)
33
- klass = constrained? && other.constrained? ? Sum::Constrained : Sum
34
- klass.new(self, other)
34
+ compose(other, Sum)
35
+ end
36
+
37
+ # Compose two types into an Intersection type
38
+ #
39
+ # @param [Type] other
40
+ #
41
+ # @return [Intersection, Intersection::Constrained]
42
+ #
43
+ # @api private
44
+ def &(other)
45
+ compose(other, Intersection)
46
+ end
47
+
48
+ # Compose two types into an Implication type
49
+ #
50
+ # @param [Type] other
51
+ #
52
+ # @return [Implication, Implication::Constrained]
53
+ #
54
+ # @api private
55
+ def >(other)
56
+ compose(other, Implication)
35
57
  end
36
58
 
37
59
  # Turn a type into an optional type
@@ -179,6 +201,21 @@ module Dry
179
201
  end
180
202
  end
181
203
  end
204
+
205
+ private
206
+
207
+ # @api private
208
+ def compose(other, composition_class)
209
+ klass =
210
+ if constrained? && other.constrained?
211
+ composition_class::Constrained
212
+ else
213
+ composition_class
214
+ end
215
+
216
+ klass.new(self, other)
217
+ end
182
218
  end
219
+ # rubocop:enable Metrics/ModuleLength
183
220
  end
184
221
  end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+ require "dry/types/options"
5
+ require "dry/types/meta"
6
+
7
+ module Dry
8
+ module Types
9
+ module Composition
10
+ include Type
11
+ include Builder
12
+ include Options
13
+ include Meta
14
+ include Printable
15
+ include Dry::Equalizer(:left, :right, :options, :meta, inspect: false, immutable: true)
16
+
17
+ # @return [Type]
18
+ attr_reader :left
19
+
20
+ # @return [Type]
21
+ attr_reader :right
22
+
23
+ module Constrained
24
+ def rule
25
+ left.rule.public_send(self.class.operator, right.rule)
26
+ end
27
+
28
+ def constrained?
29
+ true
30
+ end
31
+ end
32
+
33
+ def self.included(base)
34
+ composition_name = Inflector.demodulize(base)
35
+ ast_type = Inflector.underscore(composition_name).to_sym
36
+ base.define_singleton_method(:ast_type) { ast_type }
37
+ base.define_singleton_method(:composition_name) { composition_name }
38
+ base.const_set("Constrained", Class.new(base) { include Constrained })
39
+ end
40
+
41
+ # @param [Type] left
42
+ # @param [Type] right
43
+ # @param [Hash] options
44
+ #
45
+ # @api private
46
+ def initialize(left, right, **options)
47
+ super
48
+ @left, @right = left, right
49
+ freeze
50
+ end
51
+
52
+ # @return [String]
53
+ #
54
+ # @api public
55
+ def name
56
+ [left, right].map(&:name).join(" #{self.class.operator} ")
57
+ end
58
+
59
+ # @return [false]
60
+ #
61
+ # @api public
62
+ def default?
63
+ false
64
+ end
65
+
66
+ # @return [false]
67
+ #
68
+ # @api public
69
+ def constrained?
70
+ false
71
+ end
72
+
73
+ # @return [Boolean]
74
+ #
75
+ # @api public
76
+ def optional?
77
+ false
78
+ end
79
+
80
+ # @param [Object] input
81
+ #
82
+ # @return [Object]
83
+ #
84
+ # @api private
85
+ def call_unsafe(input)
86
+ raise NotImplementedError
87
+ end
88
+
89
+ # @param [Object] input
90
+ #
91
+ # @return [Object]
92
+ #
93
+ # @api private
94
+ def call_safe(input, &block)
95
+ raise NotImplementedError
96
+ end
97
+
98
+ # @param [Object] input
99
+ #
100
+ # @api public
101
+ def try(input)
102
+ raise NotImplementedError
103
+ end
104
+
105
+ # @api private
106
+ def success(input)
107
+ result = try(input)
108
+ if result.success?
109
+ result
110
+ else
111
+ raise ArgumentError, "Invalid success value '#{input}' for #{inspect}"
112
+ end
113
+ end
114
+
115
+ # @api private
116
+ def failure(input, _error = nil)
117
+ result = try(input)
118
+ if result.failure?
119
+ result
120
+ else
121
+ raise ArgumentError, "Invalid failure value '#{input}' for #{inspect}"
122
+ end
123
+ end
124
+
125
+ # @param [Object] value
126
+ #
127
+ # @return [Boolean]
128
+ #
129
+ # @api private
130
+ def primitive?(value)
131
+ raise NotImplementedError
132
+ end
133
+
134
+ # @see Nominal#to_ast
135
+ #
136
+ # @api public
137
+ def to_ast(meta: true)
138
+ [self.class.ast_type,
139
+ [left.to_ast(meta: meta), right.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
140
+ end
141
+
142
+ # Wrap the type with a proc
143
+ #
144
+ # @return [Proc]
145
+ #
146
+ # @api public
147
+ def to_proc
148
+ proc { |value| self.(value) }
149
+ end
150
+ end
151
+ end
152
+ end
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/container"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # Internal container for the built-in types
8
6
  #
9
7
  # @api private
10
8
  class Container
11
- include Dry::Container::Mixin
9
+ include Core::Container::Mixin
12
10
  end
13
11
  end
14
12
  end
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/monads/maybe"
3
+ require "dry/monads"
4
+ require "dry/monads/version"
5
+
6
+ if Gem::Version.new(Dry::Monads::VERSION) < Gem::Version.new("1.5.0")
7
+ raise "dry-types requires dry-monads >= 1.5.0"
8
+ end
4
9
 
5
10
  module Dry
6
11
  module Types
@@ -13,7 +18,7 @@ module Dry
13
18
  include Decorator
14
19
  include Builder
15
20
  include Printable
16
- include ::Dry::Monads::Maybe::Mixin
21
+ include ::Dry::Monads[:maybe]
17
22
 
18
23
  # @param [Dry::Monads::Maybe, Object] input
19
24
  #
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/monads/result"
3
+ require "dry/monads"
4
+ require "dry/monads/version"
5
+
6
+ if Gem::Version.new(Dry::Monads::VERSION) < Gem::Version.new("1.5.0")
7
+ raise "dry-types requires dry-monads >= 1.5.0"
8
+ end
4
9
 
5
10
  module Dry
6
11
  module Types
@@ -8,7 +13,7 @@ module Dry
8
13
  #
9
14
  # @api public
10
15
  class Result
11
- include Dry::Monads::Result::Mixin
16
+ include ::Dry::Monads[:result]
12
17
 
13
18
  # Turn result into a monad
14
19
  #
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # Implication type
6
+ #
7
+ # @api public
8
+ class Implication
9
+ include Composition
10
+
11
+ def self.operator
12
+ :>
13
+ end
14
+
15
+ # @param [Object] input
16
+ #
17
+ # @return [Object]
18
+ #
19
+ # @api private
20
+ def call_unsafe(input)
21
+ if left.try(input).success?
22
+ right.call_unsafe(input)
23
+ else
24
+ input
25
+ end
26
+ end
27
+
28
+ # @param [Object] input
29
+ #
30
+ # @return [Object]
31
+ #
32
+ # @api private
33
+ def call_safe(input, &block)
34
+ if left.try(input).success?
35
+ right.call_safe(input, &block)
36
+ else
37
+ input
38
+ end
39
+ end
40
+
41
+ # @param [Object] input
42
+ #
43
+ # @api public
44
+ def try(input)
45
+ if left.try(input).success?
46
+ right.try(input)
47
+ else
48
+ Result::Success.new(input)
49
+ end
50
+ end
51
+
52
+ # @param [Object] value
53
+ #
54
+ # @return [Boolean]
55
+ #
56
+ # @api private
57
+ def primitive?(value)
58
+ if left.primitive?(value)
59
+ right.primitive?(value)
60
+ else
61
+ true
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+ require "dry/types/options"
5
+ require "dry/types/meta"
6
+
7
+ module Dry
8
+ module Types
9
+ # Intersection type
10
+ #
11
+ # @api public
12
+ class Intersection
13
+ include Composition
14
+
15
+ def self.operator
16
+ :&
17
+ end
18
+
19
+ # @param [Object] input
20
+ #
21
+ # @return [Object]
22
+ #
23
+ # @api private
24
+ def call_unsafe(input)
25
+ merge_results(left.call_unsafe(input), right.call_unsafe(input))
26
+ end
27
+
28
+ # @param [Object] input
29
+ #
30
+ # @return [Object]
31
+ #
32
+ # @api private
33
+ def call_safe(input, &block)
34
+ try_sides(input, &block).input
35
+ end
36
+
37
+ # @param [Object] input
38
+ #
39
+ # @api public
40
+ def try(input)
41
+ try_sides(input) do |failure|
42
+ if block_given?
43
+ yield(failure)
44
+ else
45
+ failure
46
+ end
47
+ end
48
+ end
49
+
50
+ # @param [Object] value
51
+ #
52
+ # @return [Boolean]
53
+ #
54
+ # @api private
55
+ def primitive?(value)
56
+ left.primitive?(value) && right.primitive?(value)
57
+ end
58
+
59
+ private
60
+
61
+ # @api private
62
+ def try_sides(input, &block)
63
+ results = []
64
+
65
+ [left, right].each do |side|
66
+ result = try_side(side, input, &block)
67
+ return result if result.failure?
68
+
69
+ results << result
70
+ end
71
+
72
+ Result::Success.new(merge_results(*results.map(&:input)))
73
+ end
74
+
75
+ # @api private
76
+ def try_side(side, input)
77
+ failure = nil
78
+
79
+ result = side.try(input) do |f|
80
+ failure = f
81
+ yield(f)
82
+ end
83
+
84
+ if result.is_a?(Result)
85
+ result
86
+ elsif failure
87
+ Result::Failure.new(result, failure)
88
+ else
89
+ Result::Success.new(result)
90
+ end
91
+ end
92
+
93
+ # @api private
94
+ def merge_results(left_result, right_result)
95
+ case left_result
96
+ when ::Array
97
+ left_result
98
+ .zip(right_result)
99
+ .map { |lhs, rhs| merge_results(lhs, rhs) }
100
+ when ::Hash
101
+ left_result.merge(right_result)
102
+ else
103
+ left_result
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry/types/builder_methods"
4
+
3
5
  module Dry
4
6
  module Types
5
7
  # Export types registered in a container as module constants.
@@ -44,7 +46,7 @@ module Dry
44
46
  elsif Undefined.equal?(default)
45
47
  default_ns = Undefined
46
48
  else
47
- default_ns = Inflector.camelize(default).to_sym
49
+ default_ns = Types::Inflector.camelize(default).to_sym
48
50
  end
49
51
 
50
52
  tree = registry_tree
@@ -52,7 +54,9 @@ module Dry
52
54
  if namespaces.empty? && aliases.empty?
53
55
  modules = tree.select { |_, v| v.is_a?(::Hash) }.map(&:first)
54
56
  else
55
- modules = (namespaces + aliases.keys).map { |n| Inflector.camelize(n).to_sym }
57
+ modules = (namespaces + aliases.keys).map { |n|
58
+ Types::Inflector.camelize(n).to_sym
59
+ }
56
60
  end
57
61
 
58
62
  tree.each_with_object({}) do |(key, value), constants|
@@ -73,7 +77,7 @@ module Dry
73
77
  @registry_tree ||= @registry.keys.each_with_object({}) { |key, tree|
74
78
  type = @registry[key]
75
79
  *modules, const_name = key.split(".").map { |part|
76
- Inflector.camelize(part).to_sym
80
+ Types::Inflector.camelize(part).to_sym
77
81
  }
78
82
  next if modules.empty?
79
83
 
@@ -15,27 +15,14 @@ module Dry
15
15
  KERNEL_RESPOND_TO = ::Kernel.instance_method(:respond_to?)
16
16
  private_constant(:KERNEL_RESPOND_TO)
17
17
 
18
- if ::UnboundMethod.method_defined?(:bind_call)
19
- # @api private
20
- def initialize(predicates = Logic::Predicates)
21
- @predicates = predicates
22
- end
23
-
24
- # @api private
25
- def key?(name)
26
- KERNEL_RESPOND_TO.bind_call(@predicates, name)
27
- end
28
- else
29
- # @api private
30
- def initialize(predicates = Logic::Predicates)
31
- @predicates = predicates
32
- @has_predicate = KERNEL_RESPOND_TO.bind(@predicates)
33
- end
18
+ # @api private
19
+ def initialize(predicates = Logic::Predicates)
20
+ @predicates = predicates
21
+ end
34
22
 
35
- # @api private
36
- def key?(name)
37
- has_predicate.(name)
38
- end
23
+ # @api private
24
+ def key?(name)
25
+ KERNEL_RESPOND_TO.bind_call(@predicates, name)
39
26
  end
40
27
 
41
28
  # @api private
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # @api private
6
+ class Printer
7
+ # @api private
8
+ class Composition
9
+ def initialize(printer, composition_class)
10
+ @printer = printer
11
+ @composition_class = composition_class
12
+ freeze
13
+ end
14
+
15
+ def visit(composition)
16
+ visit_constructors(composition) do |constructors|
17
+ @printer.visit_options(composition.options, composition.meta) do |opts|
18
+ yield "#{@composition_class.composition_name}<#{constructors}#{opts}>"
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def visit_constructors(composition)
26
+ visit_constructor(composition.left) do |left|
27
+ visit_constructor(composition.right) do |right|
28
+ yield "#{left} #{@composition_class.operator} #{right}"
29
+ end
30
+ end
31
+ end
32
+
33
+ def visit_constructor(type, &block)
34
+ case type
35
+ when @composition_class
36
+ visit_constructors(type, &block)
37
+ else
38
+ @printer.visit(type, &block)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,17 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry/types/printer/composition"
4
+
3
5
  module Dry
4
6
  # rubocop:disable Metrics/AbcSize
5
7
  # rubocop:disable Metrics/PerceivedComplexity
6
8
  module Types
7
9
  # @api private
8
10
  class Printer
9
- MAPPING = { # rubocop:disable Style/MutableConstant
11
+ MAPPING = {
10
12
  Nominal => :visit_nominal,
11
13
  Constructor => :visit_constructor,
12
- Constrained => :visit_constrained,
13
- Constrained::Coercible => :visit_constrained,
14
- Hash => :visit_hash,
14
+ [
15
+ Constrained,
16
+ Constrained::Coercible
17
+ ] => :visit_constrained,
18
+ Types::Hash => :visit_hash,
15
19
  Schema => :visit_schema,
16
20
  Schema::Key => :visit_key,
17
21
  Map => :visit_map,
@@ -19,12 +23,22 @@ module Dry
19
23
  Array::Member => :visit_array_member,
20
24
  Lax => :visit_lax,
21
25
  Enum => :visit_enum,
22
- Default => :visit_default,
23
- Default::Callable => :visit_default,
24
- Sum => :visit_sum,
25
- Sum::Constrained => :visit_sum,
26
+ [Default, Default::Callable] => :visit_default,
27
+ [
28
+ Sum,
29
+ Sum::Constrained,
30
+ Intersection,
31
+ Intersection::Constrained,
32
+ Implication,
33
+ Implication::Constrained
34
+ ] => :visit_composition,
26
35
  Any.class => :visit_any
27
- }
36
+ }.flat_map { |k, v| Array(k).map { |kk| [kk, v] } }.to_h
37
+
38
+ def initialize
39
+ @composition_printers = {}
40
+ freeze
41
+ end
28
42
 
29
43
  def call(type)
30
44
  output = "".dup
@@ -87,106 +101,10 @@ module Dry
87
101
  end
88
102
  end
89
103
 
90
- def visit_schema(schema)
91
- options = schema.options.dup
92
- size = schema.count
93
- key_fn_str = ""
94
- type_fn_str = ""
95
- strict_str = ""
96
-
97
- strict_str = "strict " if options.delete(:strict)
98
-
99
- if (key_fn = options.delete(:key_transform_fn))
100
- visit_callable(key_fn) do |fn|
101
- key_fn_str = "key_fn=#{fn} "
102
- end
103
- end
104
-
105
- if (type_fn = options.delete(:type_transform_fn))
106
- visit_callable(type_fn) do |fn|
107
- type_fn_str = "type_fn=#{fn} "
108
- end
109
- end
110
-
111
- keys = options.delete(:keys)
112
-
113
- visit_options(options, schema.meta) do |opts|
114
- opts = "#{opts[1..]} " unless opts.empty?
115
- schema_parameters = "#{key_fn_str}#{type_fn_str}#{strict_str}#{opts}"
116
-
117
- header = "Schema<#{schema_parameters}keys={"
118
-
119
- if size.zero?
120
- yield "#{header}}>"
121
- else
122
- yield header.dup << keys.map { |key|
123
- visit(key) { |type| type }
124
- }.join(" ") << "}>"
125
- end
126
- end
127
- end
128
-
129
- def visit_map(map)
130
- visit(map.key_type) do |key|
131
- visit(map.value_type) do |value|
132
- options = map.options.dup
133
- options.delete(:key_type)
134
- options.delete(:value_type)
135
-
136
- visit_options(options) do |_opts|
137
- yield "Map<#{key} => #{value}>"
138
- end
139
- end
140
- end
141
- end
142
-
143
- def visit_key(key)
144
- visit(key.type) do |type|
145
- if key.required?
146
- yield "#{key.name}: #{type}"
147
- else
148
- yield "#{key.name}?: #{type}"
149
- end
150
- end
151
- end
152
-
153
- def visit_sum(sum)
154
- visit_sum_constructors(sum) do |constructors|
155
- visit_options(sum.options, sum.meta) do |opts|
156
- yield "Sum<#{constructors}#{opts}>"
157
- end
158
- end
159
- end
160
-
161
- def visit_sum_constructors(sum)
162
- case sum.left
163
- when Sum
164
- visit_sum_constructors(sum.left) do |left|
165
- case sum.right
166
- when Sum
167
- visit_sum_constructors(sum.right) do |right|
168
- yield "#{left} | #{right}"
169
- end
170
- else
171
- visit(sum.right) do |right|
172
- yield "#{left} | #{right}"
173
- end
174
- end
175
- end
176
- else
177
- visit(sum.left) do |left|
178
- case sum.right
179
- when Sum
180
- visit_sum_constructors(sum.right) do |right|
181
- yield "#{left} | #{right}"
182
- end
183
- else
184
- visit(sum.right) do |right|
185
- yield "#{left} | #{right}"
186
- end
187
- end
188
- end
189
- end
104
+ def visit_composition(composition, &block)
105
+ klass = composition.class
106
+ @composition_printers[klass] = Composition.new(self, klass)
107
+ @composition_printers[klass].visit(composition, &block)
190
108
  end
191
109
 
192
110
  def visit_enum(enum)
@@ -234,25 +152,6 @@ module Dry
234
152
  end
235
153
  end
236
154
 
237
- def visit_hash(hash)
238
- options = hash.options.dup
239
- type_fn_str = ""
240
-
241
- if (type_fn = options.delete(:type_transform_fn))
242
- visit_callable(type_fn) do |fn|
243
- type_fn_str = "type_fn=#{fn}"
244
- end
245
- end
246
-
247
- visit_options(options, hash.meta) do |opts|
248
- if opts.empty? && type_fn_str.empty?
249
- yield "Hash"
250
- else
251
- yield "Hash<#{type_fn_str}#{opts}>"
252
- end
253
- end
254
- end
255
-
256
155
  def visit_callable(callable)
257
156
  fn = callable.is_a?(String) ? FnContainer[callable] : callable
258
157
 
@@ -288,6 +187,88 @@ module Dry
288
187
  end
289
188
  end
290
189
 
190
+ def visit_schema(schema)
191
+ options = schema.options.dup
192
+ size = schema.count
193
+ key_fn_str = ""
194
+ type_fn_str = ""
195
+ strict_str = ""
196
+
197
+ strict_str = "strict " if options.delete(:strict)
198
+
199
+ if (key_fn = options.delete(:key_transform_fn))
200
+ visit_callable(key_fn) do |fn|
201
+ key_fn_str = "key_fn=#{fn} "
202
+ end
203
+ end
204
+
205
+ if (type_fn = options.delete(:type_transform_fn))
206
+ visit_callable(type_fn) do |fn|
207
+ type_fn_str = "type_fn=#{fn} "
208
+ end
209
+ end
210
+
211
+ keys = options.delete(:keys)
212
+
213
+ visit_options(options, schema.meta) do |opts|
214
+ opts = "#{opts[1..]} " unless opts.empty?
215
+ schema_parameters = "#{key_fn_str}#{type_fn_str}#{strict_str}#{opts}"
216
+
217
+ header = "Schema<#{schema_parameters}keys={"
218
+
219
+ if size.zero?
220
+ yield "#{header}}>"
221
+ else
222
+ yield header.dup << keys.map { |key|
223
+ visit(key) { |type| type }
224
+ }.join(" ") << "}>"
225
+ end
226
+ end
227
+ end
228
+
229
+ def visit_map(map)
230
+ visit(map.key_type) do |key|
231
+ visit(map.value_type) do |value|
232
+ options = map.options.dup
233
+ options.delete(:key_type)
234
+ options.delete(:value_type)
235
+
236
+ visit_options(options) do |_opts|
237
+ yield "Map<#{key} => #{value}>"
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ def visit_key(key)
244
+ visit(key.type) do |type|
245
+ if key.required?
246
+ yield "#{key.name}: #{type}"
247
+ else
248
+ yield "#{key.name}?: #{type}"
249
+ end
250
+ end
251
+ end
252
+
253
+ def visit_hash(hash)
254
+ options = hash.options.dup
255
+ type_fn_str = ""
256
+
257
+ if (type_fn = options.delete(:type_transform_fn))
258
+ visit_callable(type_fn) do |fn|
259
+ type_fn_str = "type_fn=#{fn}"
260
+ end
261
+ end
262
+
263
+ visit_options(options, hash.meta) do |opts|
264
+ if opts.empty? && type_fn_str.empty?
265
+ yield "Hash"
266
+ else
267
+ yield "Hash<#{type_fn_str}#{opts}>"
268
+ end
269
+ end
270
+ end
271
+
291
272
  def visit_options(options, meta = EMPTY_HASH) # rubocop:disable Metrics/PerceivedComplexity
292
273
  if options.empty? && meta.empty?
293
274
  yield ""
@@ -312,7 +293,7 @@ module Dry
312
293
  end
313
294
  end
314
295
 
315
- PRINTER = Printer.new.freeze
296
+ PRINTER = Printer.new
316
297
  end
317
298
  # rubocop:enable Metrics/AbcSize
318
299
  # rubocop:enable Metrics/PerceivedComplexity
data/lib/dry/types/sum.rb CHANGED
@@ -6,62 +6,10 @@ module Dry
6
6
  #
7
7
  # @api public
8
8
  class Sum
9
- include Type
10
- include Builder
11
- include Options
12
- include Meta
13
- include Printable
14
- include Dry::Equalizer(:left, :right, :options, :meta, inspect: false, immutable: true)
9
+ include Composition
15
10
 
16
- # @return [Type]
17
- attr_reader :left
18
-
19
- # @return [Type]
20
- attr_reader :right
21
-
22
- # @api private
23
- class Constrained < Sum
24
- # @return [Dry::Logic::Operations::Or]
25
- def rule
26
- left.rule | right.rule
27
- end
28
-
29
- # @return [true]
30
- def constrained?
31
- true
32
- end
33
- end
34
-
35
- # @param [Type] left
36
- # @param [Type] right
37
- # @param [Hash] options
38
- #
39
- # @api private
40
- def initialize(left, right, **options)
41
- super
42
- @left, @right = left, right
43
- freeze
44
- end
45
-
46
- # @return [String]
47
- #
48
- # @api public
49
- def name
50
- [left, right].map(&:name).join(" | ")
51
- end
52
-
53
- # @return [false]
54
- #
55
- # @api public
56
- def default?
57
- false
58
- end
59
-
60
- # @return [false]
61
- #
62
- # @api public
63
- def constrained?
64
- false
11
+ def self.operator
12
+ :|
65
13
  end
66
14
 
67
15
  # @return [Boolean]
@@ -104,26 +52,6 @@ module Dry
104
52
  end
105
53
  end
106
54
 
107
- # @api private
108
- def success(input)
109
- if left.valid?(input)
110
- left.success(input)
111
- elsif right.valid?(input)
112
- right.success(input)
113
- else
114
- raise ArgumentError, "Invalid success value '#{input}' for #{inspect}"
115
- end
116
- end
117
-
118
- # @api private
119
- def failure(input, _error = nil)
120
- if left.valid?(input)
121
- right.failure(input, right.try(input).error)
122
- else
123
- left.failure(input, left.try(input).error)
124
- end
125
- end
126
-
127
55
  # @param [Object] value
128
56
  #
129
57
  # @return [Boolean]
@@ -149,13 +77,6 @@ module Dry
149
77
  end
150
78
  end
151
79
 
152
- # @see Nominal#to_ast
153
- #
154
- # @api public
155
- def to_ast(meta: true)
156
- [:sum, [left.to_ast(meta: meta), right.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
157
- end
158
-
159
80
  # @param [Hash] options
160
81
  #
161
82
  # @return [Constrained,Sum]
@@ -170,15 +91,6 @@ module Dry
170
91
  super
171
92
  end
172
93
  end
173
-
174
- # Wrap the type with a proc
175
- #
176
- # @return [Proc]
177
- #
178
- # @api public
179
- def to_proc
180
- proc { |value| self.(value) }
181
- end
182
94
  end
183
95
  end
184
96
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Types
5
- VERSION = "1.6.0"
5
+ VERSION = "1.7.0"
6
6
  end
7
7
  end
data/lib/dry/types.rb CHANGED
@@ -8,13 +8,16 @@ require "zeitwerk"
8
8
  require "concurrent/map"
9
9
 
10
10
  require "dry/core"
11
- require "dry/container"
12
11
  require "dry/logic"
13
12
 
14
13
  require "dry/types/constraints"
15
14
  require "dry/types/errors"
16
15
  require "dry/types/version"
17
16
 
17
+ # This must be required explicitly as it may conflict with dry-inflector
18
+ require "dry/types/inflector"
19
+ require "dry/types/module"
20
+
18
21
  module Dry
19
22
  # Main library namespace
20
23
  #
@@ -37,6 +40,7 @@ module Dry
37
40
  loader.ignore(
38
41
  "#{root}/dry-types.rb",
39
42
  "#{root}/dry/types/extensions",
43
+ "#{root}/dry/types/printer",
40
44
  "#{root}/dry/types/spec/types.rb",
41
45
  "#{root}/dry/types/{#{%w[
42
46
  compat
@@ -44,6 +48,8 @@ module Dry
44
48
  core
45
49
  errors
46
50
  extensions
51
+ inflector
52
+ module
47
53
  json
48
54
  params
49
55
  printer
@@ -141,7 +147,7 @@ module Dry
141
147
  #
142
148
  # @return [String]
143
149
  def self.identifier(klass)
144
- Inflector.underscore(klass).tr("/", ".")
150
+ Types::Inflector.underscore(klass).tr("/", ".")
145
151
  end
146
152
 
147
153
  # Cached type map
@@ -155,7 +161,7 @@ module Dry
155
161
 
156
162
  # @api private
157
163
  def self.const_missing(const)
158
- underscored = Inflector.underscore(const)
164
+ underscored = Types::Inflector.underscore(const)
159
165
 
160
166
  if container.keys.any? { |key| key.split(".")[0] == underscored }
161
167
  raise ::NameError,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-types
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-15 00:00:00.000000000 Z
11
+ date: 2022-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -24,80 +24,66 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
- - !ruby/object:Gem::Dependency
28
- name: dry-container
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '0.3'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '0.3'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: dry-core
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '0.9'
48
- - - ">="
33
+ version: '1.0'
34
+ - - "<"
49
35
  - !ruby/object:Gem::Version
50
- version: '0.9'
36
+ version: '2'
51
37
  type: :runtime
52
38
  prerelease: false
53
39
  version_requirements: !ruby/object:Gem::Requirement
54
40
  requirements:
55
41
  - - "~>"
56
42
  - !ruby/object:Gem::Version
57
- version: '0.9'
58
- - - ">="
43
+ version: '1.0'
44
+ - - "<"
59
45
  - !ruby/object:Gem::Version
60
- version: '0.9'
46
+ version: '2'
61
47
  - !ruby/object:Gem::Dependency
62
48
  name: dry-inflector
63
49
  requirement: !ruby/object:Gem::Requirement
64
50
  requirements:
65
51
  - - "~>"
66
52
  - !ruby/object:Gem::Version
67
- version: '0.1'
68
- - - ">="
53
+ version: '1.0'
54
+ - - "<"
69
55
  - !ruby/object:Gem::Version
70
- version: 0.1.2
56
+ version: '2'
71
57
  type: :runtime
72
58
  prerelease: false
73
59
  version_requirements: !ruby/object:Gem::Requirement
74
60
  requirements:
75
61
  - - "~>"
76
62
  - !ruby/object:Gem::Version
77
- version: '0.1'
78
- - - ">="
63
+ version: '1.0'
64
+ - - "<"
79
65
  - !ruby/object:Gem::Version
80
- version: 0.1.2
66
+ version: '2'
81
67
  - !ruby/object:Gem::Dependency
82
68
  name: dry-logic
83
69
  requirement: !ruby/object:Gem::Requirement
84
70
  requirements:
85
- - - "~>"
86
- - !ruby/object:Gem::Version
87
- version: '1.3'
88
71
  - - ">="
89
72
  - !ruby/object:Gem::Version
90
- version: '1.3'
73
+ version: '1.4'
74
+ - - "<"
75
+ - !ruby/object:Gem::Version
76
+ version: '2'
91
77
  type: :runtime
92
78
  prerelease: false
93
79
  version_requirements: !ruby/object:Gem::Requirement
94
80
  requirements:
95
- - - "~>"
96
- - !ruby/object:Gem::Version
97
- version: '1.3'
98
81
  - - ">="
99
82
  - !ruby/object:Gem::Version
100
- version: '1.3'
83
+ version: '1.4'
84
+ - - "<"
85
+ - !ruby/object:Gem::Version
86
+ version: '2'
101
87
  - !ruby/object:Gem::Dependency
102
88
  name: zeitwerk
103
89
  requirement: !ruby/object:Gem::Requirement
@@ -126,20 +112,6 @@ dependencies:
126
112
  - - ">="
127
113
  - !ruby/object:Gem::Version
128
114
  version: '0'
129
- - !ruby/object:Gem::Dependency
130
- name: dry-monads
131
- requirement: !ruby/object:Gem::Requirement
132
- requirements:
133
- - - "~>"
134
- - !ruby/object:Gem::Version
135
- version: '1.0'
136
- type: :development
137
- prerelease: false
138
- version_requirements: !ruby/object:Gem::Requirement
139
- requirements:
140
- - - "~>"
141
- - !ruby/object:Gem::Version
142
- version: '1.0'
143
115
  - !ruby/object:Gem::Dependency
144
116
  name: rake
145
117
  requirement: !ruby/object:Gem::Requirement
@@ -207,6 +179,7 @@ files:
207
179
  - lib/dry/types/coercions/params.rb
208
180
  - lib/dry/types/compat.rb
209
181
  - lib/dry/types/compiler.rb
182
+ - lib/dry/types/composition.rb
210
183
  - lib/dry/types/constrained.rb
211
184
  - lib/dry/types/constrained/coercible.rb
212
185
  - lib/dry/types/constraints.rb
@@ -225,7 +198,9 @@ files:
225
198
  - lib/dry/types/fn_container.rb
226
199
  - lib/dry/types/hash.rb
227
200
  - lib/dry/types/hash/constructor.rb
201
+ - lib/dry/types/implication.rb
228
202
  - lib/dry/types/inflector.rb
203
+ - lib/dry/types/intersection.rb
229
204
  - lib/dry/types/json.rb
230
205
  - lib/dry/types/lax.rb
231
206
  - lib/dry/types/map.rb
@@ -239,6 +214,7 @@ files:
239
214
  - lib/dry/types/primitive_inferrer.rb
240
215
  - lib/dry/types/printable.rb
241
216
  - lib/dry/types/printer.rb
217
+ - lib/dry/types/printer/composition.rb
242
218
  - lib/dry/types/result.rb
243
219
  - lib/dry/types/schema.rb
244
220
  - lib/dry/types/schema/key.rb