dry-types 1.6.1 → 1.7.1

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: c4ee8c4beedab353a6e3998fb6bf9bfe5cc61989acede9bb9dd5f5f1005d92d3
4
- data.tar.gz: 1814b031b9e3de53b1a6162da3935bae8dacd04826e158b91dbcd32f367c9078
3
+ metadata.gz: de2177cef33e440a52114ce67514c41a77b8efabb21d6c02c225160da1aebe13
4
+ data.tar.gz: 29767eb8a1d0b1f776dbc780734972038fa649f27a8d864f798c738ef103ea6c
5
5
  SHA512:
6
- metadata.gz: a5ab795414ca2f9ffa143e274dd7b9c606a923038422d72ebe996b20e37ff3bac29d994cf59d60d2ab6dc22e70378f461b414f945c598e8cbb7d2ece93912035
7
- data.tar.gz: 5d482e5ffb8e3ec65e2651b07bb430bf596dc3cfa8bff91f8342bb332f844dbb2d083a9eb2bfdcc70c9d5785731806319c424695560d4a2713d8445d338ab5ea
6
+ metadata.gz: b9a87a1ba9e5897272e9a6beb699a13548acf94175cf25c080548ea50e92f84b9feef6d8cbdb94965fb2d839d9fd9ec1db17a1352f879a929b2143ecd265aa4e
7
+ data.tar.gz: 74453f830111bb02b2a2067c8c2f6104691d4946a8fe84b9ad6234fdc67c66697c4937a8fd722af92781c6c9d111b38b092ecb0583421b6c979cfb1cf08bf6aa
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
+ ## 1.7.1 2023-02-17
4
+
5
+
6
+ ### Fixed
7
+
8
+ - Warning from jruby about overwritten keyword (@flash-gordon + @klobuczek in #454)
9
+
10
+
11
+ [Compare v1.7.0...v1.7.1](https://github.com/dry-rb/dry-types/compare/v1.7.0...v1.7.1)
12
+
13
+ ## 1.7.0 2022-11-04
14
+
15
+
16
+ ### Changed
17
+
18
+ - This version is compatible with recently released dry-rb dependencies (@flash-gordon)
19
+ - Updated to dry-core 1.0 (@flash-gordon + @solnic)
20
+ - Dependency on dry-container was dropped (@flash-gordon)
21
+
22
+ [Compare v1.6.1...v1.7.0](https://github.com/dry-rb/dry-types/compare/v1.6.1...v1.7.0)
23
+
3
24
  ## 1.6.1 2022-10-15
4
25
 
5
26
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2022 dry-rb team
3
+ Copyright (c) 2015-2023 dry-rb team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -1,29 +1,21 @@
1
1
  <!--- this file is synced from dry-rb/template-gem project -->
2
2
  [gem]: https://rubygems.org/gems/dry-types
3
3
  [actions]: https://github.com/dry-rb/dry-types/actions
4
- [codacy]: https://www.codacy.com/gh/dry-rb/dry-types
5
- [chat]: https://dry-rb.zulipchat.com
6
- [inchpages]: http://inch-ci.org/github/dry-rb/dry-types
7
4
 
8
- # dry-types [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
9
-
10
- [![Gem Version](https://badge.fury.io/rb/dry-types.svg)][gem]
11
- [![CI Status](https://github.com/dry-rb/dry-types/workflows/ci/badge.svg)][actions]
12
- [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f2d71613195f4da993acb9ac9d6ea336)][codacy]
13
- [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/f2d71613195f4da993acb9ac9d6ea336)][codacy]
14
- [![Inline docs](http://inch-ci.org/github/dry-rb/dry-types.svg?branch=main)][inchpages]
5
+ # dry-types [![Gem Version](https://badge.fury.io/rb/dry-types.svg)][gem] [![CI Status](https://github.com/dry-rb/dry-types/workflows/ci/badge.svg)][actions]
15
6
 
16
7
  ## Links
17
8
 
18
9
  * [User documentation](https://dry-rb.org/gems/dry-types)
19
10
  * [API documentation](http://rubydoc.info/gems/dry-types)
11
+ * [Forum](https://discourse.dry-rb.org)
20
12
 
21
13
  ## Supported Ruby versions
22
14
 
23
15
  This library officially supports the following Ruby versions:
24
16
 
25
17
  * MRI `>= 2.7.0`
26
- * jruby `>= 9.3` (postponed until 2.7 is supported)
18
+ * jruby `>= 9.4` (not tested on CI)
27
19
 
28
20
  ## License
29
21
 
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"
34
+ spec.add_runtime_dependency "dry-inflector", "~> 1.0"
35
+ spec.add_runtime_dependency "dry-logic", "~> 1.4"
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
@@ -22,9 +22,9 @@ module Dry
22
22
  # @param [#call, nil] block
23
23
  #
24
24
  # @api public
25
- def self.new(input, **options, &block)
25
+ def self.new(input, fn: Undefined, **options, &block)
26
26
  type = input.is_a?(Builder) ? input : Nominal.new(input)
27
- super(type, **options, fn: Function[options.fetch(:fn, block)])
27
+ super(type, **options, fn: Function[Undefined.default(fn, block)])
28
28
  end
29
29
 
30
30
  # @param [Builder, Object] input
@@ -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
  #
@@ -49,7 +49,7 @@ module Dry
49
49
  # @api private
50
50
  def weak(*)
51
51
  raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
52
- "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
52
+ "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/main/CHANGELOG.md"
53
53
  end
54
54
  alias_method :permissive, :weak
55
55
  alias_method :strict, :weak
@@ -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
@@ -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.1"
5
+ VERSION = "1.7.1"
6
6
  end
7
7
  end
data/lib/dry/types.rb CHANGED
@@ -8,7 +8,6 @@ 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"
@@ -41,6 +40,7 @@ module Dry
41
40
  loader.ignore(
42
41
  "#{root}/dry-types.rb",
43
42
  "#{root}/dry/types/extensions",
43
+ "#{root}/dry/types/printer",
44
44
  "#{root}/dry/types/spec/types.rb",
45
45
  "#{root}/dry/types/{#{%w[
46
46
  compat
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.1
4
+ version: 1.7.1
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: 2023-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -24,80 +24,48 @@ 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
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: '0.9'
33
+ version: '1.0'
51
34
  type: :runtime
52
35
  prerelease: false
53
36
  version_requirements: !ruby/object:Gem::Requirement
54
37
  requirements:
55
38
  - - "~>"
56
39
  - !ruby/object:Gem::Version
57
- version: '0.9'
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: '0.9'
40
+ version: '1.0'
61
41
  - !ruby/object:Gem::Dependency
62
42
  name: dry-inflector
63
43
  requirement: !ruby/object:Gem::Requirement
64
44
  requirements:
65
45
  - - "~>"
66
46
  - !ruby/object:Gem::Version
67
- version: '0.1'
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: 0.1.2
47
+ version: '1.0'
71
48
  type: :runtime
72
49
  prerelease: false
73
50
  version_requirements: !ruby/object:Gem::Requirement
74
51
  requirements:
75
52
  - - "~>"
76
53
  - !ruby/object:Gem::Version
77
- version: '0.1'
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 0.1.2
54
+ version: '1.0'
81
55
  - !ruby/object:Gem::Dependency
82
56
  name: dry-logic
83
57
  requirement: !ruby/object:Gem::Requirement
84
58
  requirements:
85
59
  - - "~>"
86
60
  - !ruby/object:Gem::Version
87
- version: '1.3'
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- version: '1.3'
61
+ version: '1.4'
91
62
  type: :runtime
92
63
  prerelease: false
93
64
  version_requirements: !ruby/object:Gem::Requirement
94
65
  requirements:
95
66
  - - "~>"
96
67
  - !ruby/object:Gem::Version
97
- version: '1.3'
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- version: '1.3'
68
+ version: '1.4'
101
69
  - !ruby/object:Gem::Dependency
102
70
  name: zeitwerk
103
71
  requirement: !ruby/object:Gem::Requirement
@@ -126,20 +94,6 @@ dependencies:
126
94
  - - ">="
127
95
  - !ruby/object:Gem::Version
128
96
  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
97
  - !ruby/object:Gem::Dependency
144
98
  name: rake
145
99
  requirement: !ruby/object:Gem::Requirement
@@ -207,6 +161,7 @@ files:
207
161
  - lib/dry/types/coercions/params.rb
208
162
  - lib/dry/types/compat.rb
209
163
  - lib/dry/types/compiler.rb
164
+ - lib/dry/types/composition.rb
210
165
  - lib/dry/types/constrained.rb
211
166
  - lib/dry/types/constrained/coercible.rb
212
167
  - lib/dry/types/constraints.rb
@@ -225,7 +180,9 @@ files:
225
180
  - lib/dry/types/fn_container.rb
226
181
  - lib/dry/types/hash.rb
227
182
  - lib/dry/types/hash/constructor.rb
183
+ - lib/dry/types/implication.rb
228
184
  - lib/dry/types/inflector.rb
185
+ - lib/dry/types/intersection.rb
229
186
  - lib/dry/types/json.rb
230
187
  - lib/dry/types/lax.rb
231
188
  - lib/dry/types/map.rb
@@ -239,6 +196,7 @@ files:
239
196
  - lib/dry/types/primitive_inferrer.rb
240
197
  - lib/dry/types/printable.rb
241
198
  - lib/dry/types/printer.rb
199
+ - lib/dry/types/printer/composition.rb
242
200
  - lib/dry/types/result.rb
243
201
  - lib/dry/types/schema.rb
244
202
  - lib/dry/types/schema/key.rb
@@ -269,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
269
227
  - !ruby/object:Gem::Version
270
228
  version: '0'
271
229
  requirements: []
272
- rubygems_version: 3.1.6
230
+ rubygems_version: 3.3.26
273
231
  signing_key:
274
232
  specification_version: 4
275
233
  summary: Type system for Ruby supporting coercions, constraints and complex types