dry-types 0.13.2 → 1.5.1
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 +4 -4
- data/CHANGELOG.md +763 -233
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +28 -28
- data/lib/dry-types.rb +3 -1
- data/lib/dry/types.rb +156 -76
- data/lib/dry/types/any.rb +32 -12
- data/lib/dry/types/array.rb +19 -6
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +75 -16
- data/lib/dry/types/builder.rb +131 -15
- data/lib/dry/types/builder_methods.rb +49 -20
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +118 -31
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +56 -41
- data/lib/dry/types/constrained.rb +81 -32
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor.rb +127 -54
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +7 -0
- data/lib/dry/types/core.rb +54 -21
- data/lib/dry/types/decorator.rb +38 -17
- data/lib/dry/types/default.rb +61 -16
- data/lib/dry/types/enum.rb +43 -20
- data/lib/dry/types/errors.rb +75 -9
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/extensions/maybe.rb +74 -16
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash.rb +86 -67
- data/lib/dry/types/hash/constructor.rb +33 -0
- data/lib/dry/types/inflector.rb +3 -1
- data/lib/dry/types/json.rb +18 -16
- data/lib/dry/types/lax.rb +75 -0
- data/lib/dry/types/map.rb +76 -33
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +120 -0
- data/lib/dry/types/nominal.rb +210 -0
- data/lib/dry/types/options.rb +13 -26
- data/lib/dry/types/params.rb +39 -25
- data/lib/dry/types/predicate_inferrer.rb +238 -0
- data/lib/dry/types/predicate_registry.rb +34 -0
- data/lib/dry/types/primitive_inferrer.rb +97 -0
- data/lib/dry/types/printable.rb +16 -0
- data/lib/dry/types/printer.rb +315 -0
- data/lib/dry/types/result.rb +29 -3
- data/lib/dry/types/schema.rb +408 -0
- data/lib/dry/types/schema/key.rb +156 -0
- data/lib/dry/types/spec/types.rb +103 -33
- data/lib/dry/types/sum.rb +84 -35
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- metadata +68 -79
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -5
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -24
- data/Rakefile +0 -20
- data/benchmarks/hash_schemas.rb +0 -51
- data/lib/dry/types/compat/form_types.rb +0 -27
- data/lib/dry/types/compat/int.rb +0 -14
- data/lib/dry/types/definition.rb +0 -113
- data/lib/dry/types/hash/schema.rb +0 -199
- data/lib/dry/types/hash/schema_builder.rb +0 -75
- data/lib/dry/types/safe.rb +0 -59
data/LICENSE
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
|
1
|
+
The MIT License (MIT)
|
2
2
|
|
3
|
-
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
3
|
+
Copyright (c) 2015-2021 dry-rb team
|
10
4
|
|
11
|
-
|
12
|
-
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-types
|
2
|
-
[
|
3
|
-
[
|
4
|
-
[
|
2
|
+
[actions]: https://github.com/dry-rb/dry-types/actions
|
3
|
+
[codacy]: https://www.codacy.com/gh/dry-rb/dry-types
|
4
|
+
[chat]: https://dry-rb.zulipchat.com
|
5
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-types
|
6
6
|
|
7
|
-
# dry-types [][chat]
|
8
8
|
|
9
9
|
[][gem]
|
10
|
-
[][actions]
|
11
|
+
[][codacy]
|
12
|
+
[][codacy]
|
13
13
|
[][inchpages]
|
14
14
|
|
15
15
|
## Links
|
16
16
|
|
17
|
-
* [
|
17
|
+
* [User documentation](http://dry-rb.org/gems/dry-types)
|
18
|
+
* [API documentation](http://rubydoc.info/gems/dry-types)
|
18
19
|
|
19
|
-
##
|
20
|
+
## Supported Ruby versions
|
20
21
|
|
21
|
-
|
22
|
+
This library officially supports the following Ruby versions:
|
22
23
|
|
23
|
-
|
24
|
+
* MRI >= `2.5`
|
25
|
+
* jruby >= `9.2`
|
24
26
|
|
25
|
-
##
|
27
|
+
## License
|
26
28
|
|
27
|
-
|
29
|
+
See `LICENSE` file.
|
data/dry-types.gemspec
CHANGED
@@ -1,42 +1,42 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# this file is managed by dry-rb/devtools project
|
3
|
+
|
4
|
+
lib = File.expand_path('lib', __dir__)
|
2
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
6
|
require 'dry/types/version'
|
4
7
|
|
5
8
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
7
|
-
spec.version = Dry::Types::VERSION.dup
|
9
|
+
spec.name = 'dry-types'
|
8
10
|
spec.authors = ["Piotr Solnica"]
|
9
11
|
spec.email = ["piotr.solnica@gmail.com"]
|
10
12
|
spec.license = 'MIT'
|
13
|
+
spec.version = Dry::Types::VERSION.dup
|
11
14
|
|
12
|
-
spec.summary =
|
15
|
+
spec.summary = "Type system for Ruby supporting coercions, constraints and complex types like structs, value objects, enums etc"
|
13
16
|
spec.description = spec.summary
|
14
|
-
spec.homepage =
|
17
|
+
spec.homepage = 'https://dry-rb.org/gems/dry-types'
|
18
|
+
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-types.gemspec", "lib/**/*"]
|
19
|
+
spec.bindir = 'bin'
|
20
|
+
spec.executables = []
|
21
|
+
spec.require_paths = ['lib']
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
else
|
21
|
-
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
-
end
|
23
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
24
|
+
spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md'
|
25
|
+
spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-types'
|
26
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-types/issues'
|
23
27
|
|
24
|
-
spec.
|
25
|
-
spec.bindir = "exe"
|
26
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
-
spec.require_paths = ["lib"]
|
28
|
-
spec.required_ruby_version = ">= 2.2.0"
|
28
|
+
spec.required_ruby_version = ">= 2.5.0"
|
29
29
|
|
30
|
-
|
31
|
-
spec.add_runtime_dependency
|
32
|
-
spec.add_runtime_dependency
|
33
|
-
spec.add_runtime_dependency
|
34
|
-
spec.add_runtime_dependency
|
35
|
-
spec.add_runtime_dependency
|
30
|
+
# to update dependencies edit project.yml
|
31
|
+
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
32
|
+
spec.add_runtime_dependency "dry-container", "~> 0.3"
|
33
|
+
spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
|
34
|
+
spec.add_runtime_dependency "dry-inflector", "~> 0.1", ">= 0.1.2"
|
35
|
+
spec.add_runtime_dependency "dry-logic", "~> 1.0", ">= 1.0.2"
|
36
36
|
|
37
|
-
spec.add_development_dependency "bundler"
|
38
|
-
spec.add_development_dependency "
|
39
|
-
spec.add_development_dependency "
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency
|
37
|
+
spec.add_development_dependency "bundler"
|
38
|
+
spec.add_development_dependency "dry-monads", "~> 1.0"
|
39
|
+
spec.add_development_dependency "rake"
|
40
|
+
spec.add_development_dependency "rspec"
|
41
|
+
spec.add_development_dependency "yard"
|
42
42
|
end
|
data/lib/dry-types.rb
CHANGED
data/lib/dry/types.rb
CHANGED
@@ -1,81 +1,94 @@
|
|
1
|
-
|
2
|
-
require 'date'
|
3
|
-
require 'set'
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
require
|
3
|
+
require "bigdecimal"
|
4
|
+
require "date"
|
5
|
+
require "set"
|
6
6
|
|
7
|
-
require
|
8
|
-
require 'dry-equalizer'
|
9
|
-
require 'dry/core/extensions'
|
10
|
-
require 'dry/core/constants'
|
11
|
-
require 'dry/core/class_attributes'
|
7
|
+
require "concurrent/map"
|
12
8
|
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require 'dry/types/definition'
|
18
|
-
require 'dry/types/constructor'
|
19
|
-
require 'dry/types/builder_methods'
|
9
|
+
require "dry/container"
|
10
|
+
require "dry/core/extensions"
|
11
|
+
require "dry/core/constants"
|
12
|
+
require "dry/core/class_attributes"
|
20
13
|
|
21
|
-
require
|
14
|
+
require "dry/types/version"
|
15
|
+
require "dry/types/container"
|
16
|
+
require "dry/types/inflector"
|
17
|
+
require "dry/types/type"
|
18
|
+
require "dry/types/printable"
|
19
|
+
require "dry/types/nominal"
|
20
|
+
require "dry/types/constructor"
|
21
|
+
require "dry/types/module"
|
22
|
+
|
23
|
+
require "dry/types/errors"
|
22
24
|
|
23
25
|
module Dry
|
26
|
+
# Main library namespace
|
27
|
+
#
|
28
|
+
# @api public
|
24
29
|
module Types
|
25
30
|
extend Dry::Core::Extensions
|
26
31
|
extend Dry::Core::ClassAttributes
|
32
|
+
extend Dry::Core::Deprecations[:'dry-types']
|
27
33
|
include Dry::Core::Constants
|
28
34
|
|
29
|
-
|
30
|
-
# @return [Container{String => Definition}]
|
31
|
-
defines :namespace
|
32
|
-
|
33
|
-
namespace self
|
34
|
-
|
35
|
-
TYPE_SPEC_REGEX = %r[(.+)<(.+)>].freeze
|
35
|
+
TYPE_SPEC_REGEX = /(.+)<(.+)>/.freeze
|
36
36
|
|
37
|
-
# @
|
38
|
-
def self.module
|
39
|
-
|
40
|
-
define_constants(namespace, type_keys)
|
41
|
-
namespace.extend(BuilderMethods)
|
42
|
-
namespace
|
37
|
+
# @see Dry.Types
|
38
|
+
def self.module(*namespaces, default: :nominal, **aliases)
|
39
|
+
::Module.new(container, *namespaces, default: default, **aliases)
|
43
40
|
end
|
41
|
+
deprecate_class_method :module, message: <<~DEPRECATION
|
42
|
+
Use Dry.Types() instead. Beware, it exports strict types by default, for old behavior use Dry.Types(default: :nominal). See more options in the changelog
|
43
|
+
DEPRECATION
|
44
44
|
|
45
|
-
# @
|
46
|
-
def self.
|
47
|
-
|
48
|
-
' do `include Dry::Types.module` in places where you want to have access'\
|
49
|
-
' to built-in types'
|
50
|
-
|
51
|
-
define_constants(self.namespace, type_keys)
|
45
|
+
# @api private
|
46
|
+
def self.included(*)
|
47
|
+
raise "Import Dry.Types, not Dry::Types"
|
52
48
|
end
|
53
49
|
|
54
|
-
#
|
50
|
+
# Return container with registered built-in type objects
|
51
|
+
#
|
52
|
+
# @return [Container{String => Nominal}]
|
53
|
+
#
|
54
|
+
# @api private
|
55
55
|
def self.container
|
56
56
|
@container ||= Container.new
|
57
57
|
end
|
58
58
|
|
59
|
+
# Check if a give type is registered
|
60
|
+
#
|
61
|
+
# @return [Boolean]
|
62
|
+
#
|
59
63
|
# @api private
|
60
64
|
def self.registered?(class_or_identifier)
|
61
65
|
container.key?(identifier(class_or_identifier))
|
62
66
|
end
|
63
67
|
|
68
|
+
# Register a new built-in type
|
69
|
+
#
|
64
70
|
# @param [String] name
|
65
71
|
# @param [Type] type
|
66
72
|
# @param [#call,nil] block
|
67
|
-
#
|
73
|
+
#
|
74
|
+
# @return [Container{String => Nominal}]
|
75
|
+
#
|
68
76
|
# @api private
|
69
77
|
def self.register(name, type = nil, &block)
|
70
78
|
container.register(name, type || block.call)
|
71
79
|
end
|
72
80
|
|
81
|
+
# Get a built-in type by its name
|
82
|
+
#
|
73
83
|
# @param [String,Class] name
|
84
|
+
#
|
74
85
|
# @return [Type,Class]
|
86
|
+
#
|
87
|
+
# @api public
|
75
88
|
def self.[](name)
|
76
89
|
type_map.fetch_or_store(name) do
|
77
90
|
case name
|
78
|
-
when String
|
91
|
+
when ::String
|
79
92
|
result = name.match(TYPE_SPEC_REGEX)
|
80
93
|
|
81
94
|
if result
|
@@ -84,7 +97,12 @@ module Dry
|
|
84
97
|
else
|
85
98
|
container[name]
|
86
99
|
end
|
87
|
-
when Class
|
100
|
+
when ::Class
|
101
|
+
warn(<<~DEPRECATION)
|
102
|
+
Using Dry::Types.[] with a class is deprecated, please use string identifiers: Dry::Types[Integer] -> Dry::Types['integer'].
|
103
|
+
If you're using dry-struct this means changing `attribute :counter, Integer` to `attribute :counter, Dry::Types['integer']` or to `attribute :counter, 'integer'`.
|
104
|
+
DEPRECATION
|
105
|
+
|
88
106
|
type_name = identifier(name)
|
89
107
|
|
90
108
|
if container.key?(type_name)
|
@@ -96,58 +114,120 @@ module Dry
|
|
96
114
|
end
|
97
115
|
end
|
98
116
|
|
99
|
-
#
|
100
|
-
#
|
101
|
-
# @return [<Definition>]
|
102
|
-
def self.define_constants(namespace, identifiers)
|
103
|
-
names = identifiers.map do |id|
|
104
|
-
parts = id.split('.')
|
105
|
-
[Inflector.camelize(parts.pop), parts.map(&Inflector.method(:camelize))]
|
106
|
-
end
|
107
|
-
|
108
|
-
names.map do |(klass, parts)|
|
109
|
-
mod = parts.reduce(namespace) do |a, e|
|
110
|
-
a.constants.include?(e.to_sym) ? a.const_get(e) : a.const_set(e, Module.new)
|
111
|
-
end
|
112
|
-
|
113
|
-
mod.const_set(klass, self[identifier((parts + [klass]).join('::'))])
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
+
# Infer a type identifier from the provided class
|
118
|
+
#
|
117
119
|
# @param [#to_s] klass
|
120
|
+
#
|
118
121
|
# @return [String]
|
119
122
|
def self.identifier(klass)
|
120
|
-
Inflector.underscore(klass).tr(
|
123
|
+
Inflector.underscore(klass).tr("/", ".")
|
121
124
|
end
|
122
125
|
|
126
|
+
# Cached type map
|
127
|
+
#
|
123
128
|
# @return [Concurrent::Map]
|
129
|
+
#
|
130
|
+
# @api private
|
124
131
|
def self.type_map
|
125
|
-
@type_map ||= Concurrent::Map.new
|
132
|
+
@type_map ||= ::Concurrent::Map.new
|
126
133
|
end
|
127
134
|
|
128
|
-
# List of type keys defined in {Dry::Types.container}
|
129
|
-
# @return [<String>]
|
130
|
-
def self.type_keys
|
131
|
-
container.keys
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
135
|
# @api private
|
137
136
|
def self.const_missing(const)
|
138
137
|
underscored = Inflector.underscore(const)
|
139
138
|
|
140
|
-
if
|
141
|
-
raise NameError,
|
142
|
-
|
143
|
-
'You can access the predefined types with [], e.g. Dry::Types["
|
144
|
-
|
139
|
+
if container.keys.any? { |key| key.split(".")[0] == underscored }
|
140
|
+
raise ::NameError,
|
141
|
+
"dry-types does not define constants for default types. "\
|
142
|
+
'You can access the predefined types with [], e.g. Dry::Types["integer"] '\
|
143
|
+
"or generate a module with types using Dry.Types()"
|
145
144
|
else
|
146
145
|
super
|
147
146
|
end
|
148
147
|
end
|
148
|
+
|
149
|
+
# Add a new type builder method. This is a public API for defining custom
|
150
|
+
# type constructors
|
151
|
+
#
|
152
|
+
# @example simple custom type constructor
|
153
|
+
# Dry::Types.define_builder(:or_nil) do |type|
|
154
|
+
# type.optional.fallback(nil)
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# Dry::Types["integer"].or_nil.("foo") # => nil
|
158
|
+
#
|
159
|
+
# @example fallback alias
|
160
|
+
# Dry::Types.define_builder(:or) do |type, fallback|
|
161
|
+
# type.fallback(fallback)
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# Dry::Types["integer"].or(100).("foo") # => 100
|
165
|
+
#
|
166
|
+
# @param [Symbol] method
|
167
|
+
# @param [#call] block
|
168
|
+
#
|
169
|
+
# @api public
|
170
|
+
def self.define_builder(method, &block)
|
171
|
+
Builder.define_method(method) do |*args|
|
172
|
+
block.(self, *args)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Export registered types as a module with constants
|
178
|
+
#
|
179
|
+
# @example no options
|
180
|
+
#
|
181
|
+
# module Types
|
182
|
+
# # imports all types as constants, uses modules for namespaces
|
183
|
+
# include Dry::Types()
|
184
|
+
# end
|
185
|
+
# # strict types are exported by default
|
186
|
+
# Types::Integer
|
187
|
+
# # => #<Dry::Types[Constrained<Nominal<Integer> rule=[type?(Integer)]>]>
|
188
|
+
# Types::Nominal::Integer
|
189
|
+
# # => #<Dry::Types[Nominal<Integer>]>
|
190
|
+
#
|
191
|
+
# @example changing default types
|
192
|
+
#
|
193
|
+
# module Types
|
194
|
+
# include Dry::Types(default: :nominal)
|
195
|
+
# end
|
196
|
+
# Types::Integer
|
197
|
+
# # => #<Dry::Types[Nominal<Integer>]>
|
198
|
+
#
|
199
|
+
# @example cherry-picking namespaces
|
200
|
+
#
|
201
|
+
# module Types
|
202
|
+
# include Dry::Types(:strict, :coercible)
|
203
|
+
# end
|
204
|
+
# # cherry-picking discards default types,
|
205
|
+
# # provide the :default option along with the list of
|
206
|
+
# # namespaces if you want the to be exported
|
207
|
+
# Types.constants # => [:Coercible, :Strict]
|
208
|
+
#
|
209
|
+
# @example custom names
|
210
|
+
# module Types
|
211
|
+
# include Dry::Types(coercible: :Kernel)
|
212
|
+
# end
|
213
|
+
# Types::Kernel::Integer
|
214
|
+
# # => #<Dry::Types[Constructor<Nominal<Integer> fn=Kernel.Integer>]>
|
215
|
+
#
|
216
|
+
# @param [Array<Symbol>] namespaces List of type namespaces to export
|
217
|
+
# @param [Symbol] default Default namespace to export
|
218
|
+
# @param [Hash{Symbol => Symbol}] aliases Optional renamings, like strict: :Draconian
|
219
|
+
#
|
220
|
+
# @return [Dry::Types::Module]
|
221
|
+
#
|
222
|
+
# @see Dry::Types::Module
|
223
|
+
#
|
224
|
+
# @api public
|
225
|
+
#
|
226
|
+
def self.Types(*namespaces, default: Types::Undefined, **aliases)
|
227
|
+
Types::Module.new(Types.container, *namespaces, default: default, **aliases)
|
149
228
|
end
|
150
229
|
end
|
151
230
|
|
152
|
-
require
|
153
|
-
require
|
231
|
+
require "dry/types/core" # load built-in types
|
232
|
+
require "dry/types/extensions"
|
233
|
+
require "dry/types/printer"
|