super-smart-tool 0.0.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 +7 -0
- data/dry-types-1.9.1/CHANGELOG.md +1145 -0
- data/dry-types-1.9.1/LICENSE +20 -0
- data/dry-types-1.9.1/README.md +17 -0
- data/dry-types-1.9.1/dry-types.gemspec +45 -0
- data/dry-types-1.9.1/lib/dry/types/any.rb +41 -0
- data/dry-types-1.9.1/lib/dry/types/array/constructor.rb +24 -0
- data/dry-types-1.9.1/lib/dry/types/array/member.rb +118 -0
- data/dry-types-1.9.1/lib/dry/types/array.rb +32 -0
- data/dry-types-1.9.1/lib/dry/types/builder.rb +221 -0
- data/dry-types-1.9.1/lib/dry/types/builder_methods.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/coercions/json.rb +57 -0
- data/dry-types-1.9.1/lib/dry/types/coercions/params.rb +168 -0
- data/dry-types-1.9.1/lib/dry/types/coercions.rb +105 -0
- data/dry-types-1.9.1/lib/dry/types/compat.rb +1 -0
- data/dry-types-1.9.1/lib/dry/types/compiler.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/composition.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/constrained/coercible.rb +59 -0
- data/dry-types-1.9.1/lib/dry/types/constrained.rb +146 -0
- data/dry-types-1.9.1/lib/dry/types/constraints.rb +30 -0
- data/dry-types-1.9.1/lib/dry/types/constructor/function.rb +203 -0
- data/dry-types-1.9.1/lib/dry/types/constructor/wrapper.rb +88 -0
- data/dry-types-1.9.1/lib/dry/types/constructor.rb +195 -0
- data/dry-types-1.9.1/lib/dry/types/container.rb +12 -0
- data/dry-types-1.9.1/lib/dry/types/core.rb +106 -0
- data/dry-types-1.9.1/lib/dry/types/decorator.rb +94 -0
- data/dry-types-1.9.1/lib/dry/types/default.rb +122 -0
- data/dry-types-1.9.1/lib/dry/types/enum.rb +121 -0
- data/dry-types-1.9.1/lib/dry/types/errors.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/extensions/maybe.rb +128 -0
- data/dry-types-1.9.1/lib/dry/types/extensions/monads.rb +34 -0
- data/dry-types-1.9.1/lib/dry/types/extensions.rb +9 -0
- data/dry-types-1.9.1/lib/dry/types/fn_container.rb +37 -0
- data/dry-types-1.9.1/lib/dry/types/hash/constructor.rb +25 -0
- data/dry-types-1.9.1/lib/dry/types/hash.rb +134 -0
- data/dry-types-1.9.1/lib/dry/types/implication.rb +64 -0
- data/dry-types-1.9.1/lib/dry/types/inflector.rb +9 -0
- data/dry-types-1.9.1/lib/dry/types/intersection.rb +102 -0
- data/dry-types-1.9.1/lib/dry/types/json.rb +35 -0
- data/dry-types-1.9.1/lib/dry/types/lax.rb +66 -0
- data/dry-types-1.9.1/lib/dry/types/map.rb +134 -0
- data/dry-types-1.9.1/lib/dry/types/meta.rb +53 -0
- data/dry-types-1.9.1/lib/dry/types/module.rb +127 -0
- data/dry-types-1.9.1/lib/dry/types/nominal.rb +201 -0
- data/dry-types-1.9.1/lib/dry/types/options.rb +34 -0
- data/dry-types-1.9.1/lib/dry/types/params.rb +38 -0
- data/dry-types-1.9.1/lib/dry/types/predicate_inferrer.rb +235 -0
- data/dry-types-1.9.1/lib/dry/types/predicate_registry.rb +32 -0
- data/dry-types-1.9.1/lib/dry/types/primitive_inferrer.rb +75 -0
- data/dry-types-1.9.1/lib/dry/types/printable.rb +14 -0
- data/dry-types-1.9.1/lib/dry/types/printer/composition.rb +44 -0
- data/dry-types-1.9.1/lib/dry/types/printer.rb +313 -0
- data/dry-types-1.9.1/lib/dry/types/result.rb +72 -0
- data/dry-types-1.9.1/lib/dry/types/schema/key.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/schema.rb +408 -0
- data/dry-types-1.9.1/lib/dry/types/spec/types.rb +164 -0
- data/dry-types-1.9.1/lib/dry/types/sum.rb +105 -0
- data/dry-types-1.9.1/lib/dry/types/type.rb +53 -0
- data/dry-types-1.9.1/lib/dry/types/version.rb +7 -0
- data/dry-types-1.9.1/lib/dry/types.rb +259 -0
- data/dry-types-1.9.1/lib/dry-types.rb +3 -0
- data/super-smart-tool.gemspec +11 -0
- metadata +101 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2026 Hanakai team
|
|
4
|
+
|
|
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:
|
|
11
|
+
|
|
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.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!--- This file is synced from hanakai-rb/repo-sync -->
|
|
2
|
+
|
|
3
|
+
[rubygem]: https://rubygems.org/gems/dry-types
|
|
4
|
+
[actions]: https://github.com/dry-rb/dry-types/actions
|
|
5
|
+
|
|
6
|
+
# dry-types [][rubygem] [][actions]
|
|
7
|
+
|
|
8
|
+
## Links
|
|
9
|
+
|
|
10
|
+
- [User documentation](https://dry-rb.org/gems/dry-types)
|
|
11
|
+
- [API documentation](http://rubydoc.info/gems/dry-types)
|
|
12
|
+
- [Forum](https://discourse.dry-rb.org)
|
|
13
|
+
|
|
14
|
+
## License
|
|
15
|
+
|
|
16
|
+
See `LICENSE` file.
|
|
17
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This file is synced from hanakai-rb/repo-sync. To update it, edit repo-sync.yml.
|
|
4
|
+
|
|
5
|
+
lib = File.expand_path("lib", __dir__)
|
|
6
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
7
|
+
require "dry/types/version"
|
|
8
|
+
|
|
9
|
+
Gem::Specification.new do |spec|
|
|
10
|
+
spec.name = "dry-types"
|
|
11
|
+
spec.authors = ["Hanakai team"]
|
|
12
|
+
spec.email = ["info@hanakai.org"]
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
spec.version = Dry::Types::VERSION.dup
|
|
15
|
+
|
|
16
|
+
spec.summary = "Type system for Ruby supporting coercions, constraints and complex types like structs, value objects, enums etc"
|
|
17
|
+
spec.description = spec.summary
|
|
18
|
+
spec.homepage = "https://dry-rb.org/gems/dry-types"
|
|
19
|
+
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-types.gemspec", "lib/**/*"]
|
|
20
|
+
spec.bindir = "exe"
|
|
21
|
+
spec.executables = Dir["exe/*"].map { |f| File.basename(f) }
|
|
22
|
+
spec.require_paths = ["lib"]
|
|
23
|
+
|
|
24
|
+
spec.extra_rdoc_files = ["README.md", "CHANGELOG.md", "LICENSE"]
|
|
25
|
+
|
|
26
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
27
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-types/blob/main/CHANGELOG.md"
|
|
28
|
+
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-types"
|
|
29
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-types/issues"
|
|
30
|
+
spec.metadata["funding_uri"] = "https://github.com/sponsors/hanami"
|
|
31
|
+
|
|
32
|
+
spec.required_ruby_version = ">= 3.2"
|
|
33
|
+
|
|
34
|
+
spec.add_runtime_dependency "bigdecimal", ">= 3.0"
|
|
35
|
+
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
|
36
|
+
spec.add_runtime_dependency "dry-core", "~> 1.0"
|
|
37
|
+
spec.add_runtime_dependency "dry-inflector", "~> 1.0"
|
|
38
|
+
spec.add_runtime_dependency "dry-logic", "~> 1.4"
|
|
39
|
+
spec.add_runtime_dependency "zeitwerk", "~> 2.6"
|
|
40
|
+
spec.add_development_dependency "bundler"
|
|
41
|
+
spec.add_development_dependency "rake"
|
|
42
|
+
spec.add_development_dependency "rspec"
|
|
43
|
+
spec.add_development_dependency "yard"
|
|
44
|
+
end
|
|
45
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# Any is a nominal type that defines Object as the primitive class
|
|
6
|
+
#
|
|
7
|
+
# This type is useful in places where you can't be specific about the type
|
|
8
|
+
# and anything is acceptable.
|
|
9
|
+
#
|
|
10
|
+
# @api public
|
|
11
|
+
class AnyClass < Nominal
|
|
12
|
+
def self.name = "Any"
|
|
13
|
+
|
|
14
|
+
# @api private
|
|
15
|
+
def initialize(**options)
|
|
16
|
+
super(::Object, **options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [String]
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
def name = "Any"
|
|
23
|
+
|
|
24
|
+
# @param [Hash] new_options
|
|
25
|
+
#
|
|
26
|
+
# @return [Type]
|
|
27
|
+
#
|
|
28
|
+
# @api public
|
|
29
|
+
def with(**new_options)
|
|
30
|
+
self.class.new(**options, meta: @meta, **new_options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Array]
|
|
34
|
+
#
|
|
35
|
+
# @api public
|
|
36
|
+
def to_ast(meta: true) = [:any, meta_ast(meta)]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Any = AnyClass.new
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# @api public
|
|
6
|
+
class Array < Nominal
|
|
7
|
+
# @api private
|
|
8
|
+
class Constructor < ::Dry::Types::Constructor
|
|
9
|
+
# @api private
|
|
10
|
+
def constructor_type = ::Dry::Types::Array::Constructor
|
|
11
|
+
|
|
12
|
+
# @return [Lax]
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
def lax = Lax.new(type.lax.constructor(fn, meta: meta))
|
|
16
|
+
|
|
17
|
+
# @see Dry::Types::Array#of
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
def of(member) = type.of(member).constructor(fn, meta: meta)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
class Array < Nominal
|
|
6
|
+
# Member arrays define their member type that is applied to each element
|
|
7
|
+
#
|
|
8
|
+
# @api public
|
|
9
|
+
class Member < Array
|
|
10
|
+
# @return [Type]
|
|
11
|
+
attr_reader :member
|
|
12
|
+
|
|
13
|
+
# @param [Class] primitive
|
|
14
|
+
# @param [Hash] options
|
|
15
|
+
#
|
|
16
|
+
# @option options [Type] :member
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
def initialize(primitive, **options)
|
|
20
|
+
@member = options.fetch(:member)
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param [Object] input
|
|
25
|
+
#
|
|
26
|
+
# @return [Array]
|
|
27
|
+
#
|
|
28
|
+
# @api private
|
|
29
|
+
def call_unsafe(input)
|
|
30
|
+
if primitive?(input)
|
|
31
|
+
input.each_with_object([]) do |el, output|
|
|
32
|
+
coerced = member.call_unsafe(el)
|
|
33
|
+
|
|
34
|
+
output << coerced unless Undefined.equal?(coerced)
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param [Object] input
|
|
42
|
+
# @return [Array]
|
|
43
|
+
#
|
|
44
|
+
# @api private
|
|
45
|
+
def call_safe(input, &block)
|
|
46
|
+
if primitive?(input)
|
|
47
|
+
failed = false
|
|
48
|
+
|
|
49
|
+
result = input.each_with_object([]) do |el, output|
|
|
50
|
+
coerced = member.call_safe(el) { |out = el|
|
|
51
|
+
failed = true
|
|
52
|
+
out
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
output << coerced unless Undefined.equal?(coerced)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
failed ? block.call(result) : result
|
|
59
|
+
else
|
|
60
|
+
block.call
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param [Array, Object] input
|
|
65
|
+
# @param [#call,nil] block
|
|
66
|
+
#
|
|
67
|
+
# @yieldparam [Failure] failure
|
|
68
|
+
# @yieldreturn [Result]
|
|
69
|
+
#
|
|
70
|
+
# @return [Result,Logic::Result]
|
|
71
|
+
#
|
|
72
|
+
# @api public
|
|
73
|
+
def try(input, &block) # rubocop:disable Metrics/PerceivedComplexity
|
|
74
|
+
if primitive?(input)
|
|
75
|
+
output = []
|
|
76
|
+
|
|
77
|
+
result = input.map { |el| member.try(el) }
|
|
78
|
+
result.each do |r|
|
|
79
|
+
output << r.input unless Undefined.equal?(r.input)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if result.all?(&:success?)
|
|
83
|
+
success(output)
|
|
84
|
+
else
|
|
85
|
+
error = result.find(&:failure?).error
|
|
86
|
+
failure = failure(output, error)
|
|
87
|
+
block ? yield(failure) : failure
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
failure = failure(input, CoercionError.new("#{input} is not an array"))
|
|
91
|
+
block ? yield(failure) : failure
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Build a lax type
|
|
96
|
+
#
|
|
97
|
+
# @return [Lax]
|
|
98
|
+
#
|
|
99
|
+
# @api public
|
|
100
|
+
def lax = Lax.new(Member.new(primitive, **options, member: member.lax, meta: meta))
|
|
101
|
+
|
|
102
|
+
# @see Nominal#to_ast
|
|
103
|
+
#
|
|
104
|
+
# @api public
|
|
105
|
+
def to_ast(meta: true)
|
|
106
|
+
if member.respond_to?(:to_ast)
|
|
107
|
+
[:array, [member.to_ast(meta: meta), meta_ast(meta)]]
|
|
108
|
+
else
|
|
109
|
+
[:array, [member, meta_ast(meta)]]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @api private
|
|
114
|
+
def constructor_type = ::Dry::Types::Array::Constructor
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# Array type can be used to define an array with optional member type
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
class Array < Nominal
|
|
9
|
+
# Build an array type with a member type
|
|
10
|
+
#
|
|
11
|
+
# @param [Type,#call] type
|
|
12
|
+
#
|
|
13
|
+
# @return [Array::Member]
|
|
14
|
+
#
|
|
15
|
+
# @api public
|
|
16
|
+
def of(type)
|
|
17
|
+
member =
|
|
18
|
+
case type
|
|
19
|
+
when ::String then Types[type]
|
|
20
|
+
else type
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Array::Member.new(primitive, **options, member: member)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
def constructor_type
|
|
28
|
+
::Dry::Types::Array::Constructor
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# Common API for building types and composition
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
module Builder
|
|
9
|
+
include ::Dry::Core::Constants
|
|
10
|
+
|
|
11
|
+
# @return [Class]
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
def constrained_type = Constrained
|
|
15
|
+
|
|
16
|
+
# @return [Class]
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
def constructor_type = Constructor
|
|
20
|
+
|
|
21
|
+
# Compose two types into a Sum type
|
|
22
|
+
#
|
|
23
|
+
# @param [Type] other
|
|
24
|
+
#
|
|
25
|
+
# @return [Sum, Sum::Constrained]
|
|
26
|
+
#
|
|
27
|
+
# @api private
|
|
28
|
+
def |(other) = compose(other, Sum)
|
|
29
|
+
|
|
30
|
+
# Compose two types into an Intersection type
|
|
31
|
+
#
|
|
32
|
+
# @param [Type] other
|
|
33
|
+
#
|
|
34
|
+
# @return [Intersection, Intersection::Constrained]
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
def &(other) = compose(other, Intersection)
|
|
38
|
+
|
|
39
|
+
# Compose two types into an Implication type
|
|
40
|
+
#
|
|
41
|
+
# @param [Type] other
|
|
42
|
+
#
|
|
43
|
+
# @return [Implication, Implication::Constrained]
|
|
44
|
+
#
|
|
45
|
+
# @api private
|
|
46
|
+
def >(other) = compose(other, Implication)
|
|
47
|
+
|
|
48
|
+
# Turn a type into an optional type
|
|
49
|
+
#
|
|
50
|
+
# @return [Sum]
|
|
51
|
+
#
|
|
52
|
+
# @api public
|
|
53
|
+
def optional
|
|
54
|
+
nil_type =
|
|
55
|
+
if Types.use_namespaced_optionals && respond_to?(:namespace) && namespace
|
|
56
|
+
Types["#{namespace}.nil"]
|
|
57
|
+
else
|
|
58
|
+
Types["nil"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
nil_type | self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Turn a type into a constrained type
|
|
65
|
+
#
|
|
66
|
+
# @param [Hash] options constraining rule (see {Types.Rule})
|
|
67
|
+
#
|
|
68
|
+
# @return [Constrained]
|
|
69
|
+
#
|
|
70
|
+
# @api public
|
|
71
|
+
def constrained(...)
|
|
72
|
+
constrained_type.new(self, rule: Types.Rule(...))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Turn a type into a type with a default value
|
|
76
|
+
#
|
|
77
|
+
# @param [Object] input
|
|
78
|
+
# @option [Boolean] shared Whether it's safe to share the value across type applications
|
|
79
|
+
# @param [#call,nil] block
|
|
80
|
+
#
|
|
81
|
+
# @raise [ConstraintError]
|
|
82
|
+
#
|
|
83
|
+
# @return [Default]
|
|
84
|
+
#
|
|
85
|
+
# @api public
|
|
86
|
+
def default(input = Undefined, options = EMPTY_HASH, &block)
|
|
87
|
+
unless input.frozen? || options[:shared]
|
|
88
|
+
where = Core::Deprecations::STACK.()
|
|
89
|
+
|
|
90
|
+
# There is a weird behaviour in JRuby where the source_location is mutated upon
|
|
91
|
+
# calling `inspect` on proc (see: https://github.com/jruby/jruby/issues/9110)
|
|
92
|
+
# To bypass this, we call inspect on the clone (but only for proc and JRuby).
|
|
93
|
+
# This should be fixes in JRuby 10.0.3.0.
|
|
94
|
+
obj = input.is_a?(Proc) && RUBY_ENGINE == "jruby" ? input.dup : input
|
|
95
|
+
|
|
96
|
+
Core::Deprecations.warn(
|
|
97
|
+
"#{obj.inspect} is mutable. " \
|
|
98
|
+
"Be careful: types will return the same instance of the default " \
|
|
99
|
+
"value every time. Call `.freeze` when setting the default " \
|
|
100
|
+
"or pass `shared: true` to discard this warning." \
|
|
101
|
+
"\n#{where}",
|
|
102
|
+
tag: :"dry-types"
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
value = Undefined.default(input, block)
|
|
107
|
+
type = Default[value].new(self, value)
|
|
108
|
+
|
|
109
|
+
if !type.callable? && !valid?(value)
|
|
110
|
+
raise ConstraintError.new(
|
|
111
|
+
"default value #{value.inspect} violates constraints",
|
|
112
|
+
value
|
|
113
|
+
)
|
|
114
|
+
else
|
|
115
|
+
type
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Define an enum on top of the existing type
|
|
120
|
+
#
|
|
121
|
+
# @param [Array] values
|
|
122
|
+
#
|
|
123
|
+
# @return [Enum]
|
|
124
|
+
#
|
|
125
|
+
# @api public
|
|
126
|
+
def enum(*values)
|
|
127
|
+
mapping =
|
|
128
|
+
if values.length == 1 && values[0].is_a?(::Hash)
|
|
129
|
+
values[0]
|
|
130
|
+
else
|
|
131
|
+
values.zip(values).to_h
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Turn a type into a lax type that will rescue from type-errors and
|
|
138
|
+
# return the original input
|
|
139
|
+
#
|
|
140
|
+
# @return [Lax]
|
|
141
|
+
#
|
|
142
|
+
# @api public
|
|
143
|
+
def lax = Lax.new(self)
|
|
144
|
+
|
|
145
|
+
# Define a constructor for the type
|
|
146
|
+
#
|
|
147
|
+
# @param [#call,nil] constructor
|
|
148
|
+
# @param [Hash] options
|
|
149
|
+
# @param [#call,nil] block
|
|
150
|
+
#
|
|
151
|
+
# @return [Constructor]
|
|
152
|
+
#
|
|
153
|
+
# @api public
|
|
154
|
+
def constructor(constructor = nil, **options, &block)
|
|
155
|
+
constructor_type[with(**options), fn: constructor || block]
|
|
156
|
+
end
|
|
157
|
+
alias_method :append, :constructor
|
|
158
|
+
alias_method :prepend, :constructor
|
|
159
|
+
alias_method :>>, :constructor
|
|
160
|
+
alias_method :<<, :constructor
|
|
161
|
+
|
|
162
|
+
# Use the given value on type mismatch
|
|
163
|
+
#
|
|
164
|
+
# @param [Object] value
|
|
165
|
+
# @option [Boolean] shared Whether it's safe to share the value across type applications
|
|
166
|
+
# @param [#call,nil] fallback
|
|
167
|
+
#
|
|
168
|
+
# @return [Constructor]
|
|
169
|
+
#
|
|
170
|
+
# @api public
|
|
171
|
+
def fallback(value = Undefined, shared: false, &_fallback) # rubocop:disable Metrics/PerceivedComplexity
|
|
172
|
+
if Undefined.equal?(value) && !block_given?
|
|
173
|
+
raise ::ArgumentError, "fallback value or a block must be given"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if !block_given? && !valid?(value)
|
|
177
|
+
raise ConstraintError.new(
|
|
178
|
+
"fallback value #{value.inspect} violates constraints",
|
|
179
|
+
value
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
unless value.frozen? || shared
|
|
184
|
+
where = Core::Deprecations::STACK.()
|
|
185
|
+
Core::Deprecations.warn(
|
|
186
|
+
"#{value.inspect} is mutable. " \
|
|
187
|
+
"Be careful: types will return the same instance of the fallback " \
|
|
188
|
+
"value every time. Call `.freeze` when setting the fallback " \
|
|
189
|
+
"or pass `shared: true` to discard this warning." \
|
|
190
|
+
"\n#{where}",
|
|
191
|
+
tag: :"dry-types"
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
constructor do |input, type, &|
|
|
196
|
+
type.(input) do |output = input|
|
|
197
|
+
if block_given?
|
|
198
|
+
yield(output)
|
|
199
|
+
else
|
|
200
|
+
value
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
private
|
|
207
|
+
|
|
208
|
+
# @api private
|
|
209
|
+
def compose(other, composition_class)
|
|
210
|
+
klass =
|
|
211
|
+
if constrained? && other.constrained?
|
|
212
|
+
composition_class::Constrained
|
|
213
|
+
else
|
|
214
|
+
composition_class
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
klass.new(self, other)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# Common API for building type objects in a convenient way
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
# @api public
|
|
9
|
+
module BuilderMethods
|
|
10
|
+
# @api private
|
|
11
|
+
def included(base)
|
|
12
|
+
super
|
|
13
|
+
base.extend(BuilderMethods)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Build an array type.
|
|
17
|
+
#
|
|
18
|
+
# Shortcut for Array#of.
|
|
19
|
+
#
|
|
20
|
+
# @example
|
|
21
|
+
# Types::Strings = Types.Array(Types::String)
|
|
22
|
+
#
|
|
23
|
+
# @param [Dry::Types::Type] type
|
|
24
|
+
#
|
|
25
|
+
# @return [Dry::Types::Array]
|
|
26
|
+
def Array(type) = Strict(::Array).of(type)
|
|
27
|
+
|
|
28
|
+
# Build a hash schema
|
|
29
|
+
#
|
|
30
|
+
# @param [Hash{Symbol => Dry::Types::Type}] type_map
|
|
31
|
+
#
|
|
32
|
+
# @return [Dry::Types::Array]
|
|
33
|
+
def Hash(type_map) = Strict(::Hash).schema(type_map)
|
|
34
|
+
|
|
35
|
+
# Build a type which values are instances of a given class
|
|
36
|
+
# Values are checked using `is_a?` call
|
|
37
|
+
#
|
|
38
|
+
# @example
|
|
39
|
+
# Types::Error = Types.Instance(StandardError)
|
|
40
|
+
# Types::Error = Types.Strict(StandardError)
|
|
41
|
+
# Types.Strict(Integer) == Types::Strict::Int # => true
|
|
42
|
+
#
|
|
43
|
+
# @param [Class,Module] klass Class or module
|
|
44
|
+
#
|
|
45
|
+
# @return [Dry::Types::Type]
|
|
46
|
+
def Instance(klass)
|
|
47
|
+
unless klass.is_a?(::Module)
|
|
48
|
+
raise ::ArgumentError, "Expected a class or module, got #{klass.inspect}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Nominal(klass).constrained(type: klass)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
alias_method :Strict, :Instance
|
|
55
|
+
|
|
56
|
+
# Build a type with a single value
|
|
57
|
+
# The equality check done with `eql?`
|
|
58
|
+
#
|
|
59
|
+
# @param [Object] value
|
|
60
|
+
#
|
|
61
|
+
# @return [Dry::Types::Type]
|
|
62
|
+
def Value(value) = Nominal(value.class).constrained(eql: value)
|
|
63
|
+
|
|
64
|
+
# Build a type with a single value
|
|
65
|
+
# The equality check done with `equal?`
|
|
66
|
+
#
|
|
67
|
+
# @param [Object] object
|
|
68
|
+
#
|
|
69
|
+
# @return [Dry::Types::Type]
|
|
70
|
+
def Constant(object) = Nominal(object.class).constrained(is: object)
|
|
71
|
+
|
|
72
|
+
# Build a constructor type
|
|
73
|
+
# If no constructor block given it uses .new method
|
|
74
|
+
#
|
|
75
|
+
# @param [Class] klass
|
|
76
|
+
# @param [#call,nil] cons Value constructor
|
|
77
|
+
# @param [#call,nil] block Value constructor
|
|
78
|
+
#
|
|
79
|
+
# @return [Dry::Types::Type]
|
|
80
|
+
def Constructor(klass, cons = nil, &block) # rubocop:disable Metrics/PerceivedComplexity:
|
|
81
|
+
if klass.is_a?(Type)
|
|
82
|
+
if cons || block
|
|
83
|
+
klass.constructor(cons || block)
|
|
84
|
+
else
|
|
85
|
+
klass
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
Nominal(klass).constructor(cons || block || klass.method(:new))
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Build a nominal type
|
|
93
|
+
#
|
|
94
|
+
# @param [Class] klass
|
|
95
|
+
#
|
|
96
|
+
# @return [Dry::Types::Type]
|
|
97
|
+
def Nominal(klass)
|
|
98
|
+
if klass <= ::Array
|
|
99
|
+
Array.new(klass)
|
|
100
|
+
elsif klass <= ::Hash
|
|
101
|
+
Hash.new(klass)
|
|
102
|
+
else
|
|
103
|
+
Nominal.new(klass)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Build a map type
|
|
108
|
+
#
|
|
109
|
+
# @example
|
|
110
|
+
# Types::IntMap = Types.Map(Types::Strict::Integer, 'any')
|
|
111
|
+
# Types::IntStringMap = Types.Map(Types::Strict::Integer, Types::Strict::String)
|
|
112
|
+
#
|
|
113
|
+
# @param [Type] key_type Key type
|
|
114
|
+
# @param [Type] value_type Value type
|
|
115
|
+
#
|
|
116
|
+
# @return [Dry::Types::Map]
|
|
117
|
+
def Map(key_type, value_type)
|
|
118
|
+
Nominal(::Hash).map(key_type, value_type)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Builds a constrained nominal type accepting any value that
|
|
122
|
+
# responds to given methods
|
|
123
|
+
#
|
|
124
|
+
# @example
|
|
125
|
+
# Types::Callable = Types.Interface(:call)
|
|
126
|
+
# Types::Contact = Types.Interface(:name, :address)
|
|
127
|
+
#
|
|
128
|
+
# @param methods [Array<String, Symbol>] Method names
|
|
129
|
+
#
|
|
130
|
+
# @return [Dry::Types::Contrained]
|
|
131
|
+
def Interface(*methods)
|
|
132
|
+
methods.reduce(Types["nominal.any"]) do |type, method|
|
|
133
|
+
type.constrained(respond_to: method)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|