dry-types 1.6.0 → 1.7.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.
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