dry-types 0.14.1 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +631 -134
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +27 -30
- data/lib/dry/types/any.rb +32 -12
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +75 -16
- data/lib/dry/types/array.rb +19 -6
- data/lib/dry/types/builder.rb +131 -15
- data/lib/dry/types/builder_methods.rb +49 -20
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +118 -31
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +56 -41
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constrained.rb +81 -32
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/constructor.rb +126 -56
- 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/maybe.rb +74 -16
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash/constructor.rb +33 -0
- data/lib/dry/types/hash.rb +86 -67
- 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/key.rb +156 -0
- data/lib/dry/types/schema.rb +408 -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
- data/lib/dry/types.rb +156 -76
- data/lib/dry-types.rb +3 -1
- metadata +65 -78
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -27
- 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/log/.gitkeep +0 -0
@@ -1,5 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
5
|
+
# Common API for building type objects in a convenient way
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# @api public
|
3
9
|
module BuilderMethods
|
4
10
|
# @api private
|
5
11
|
def included(base)
|
@@ -8,7 +14,8 @@ module Dry
|
|
8
14
|
end
|
9
15
|
|
10
16
|
# Build an array type.
|
11
|
-
#
|
17
|
+
#
|
18
|
+
# Shortcut for Array#of.
|
12
19
|
#
|
13
20
|
# @example
|
14
21
|
# Types::Strings = Types.Array(Types::String)
|
@@ -17,18 +24,16 @@ module Dry
|
|
17
24
|
#
|
18
25
|
# @return [Dry::Types::Array]
|
19
26
|
def Array(type)
|
20
|
-
|
27
|
+
Strict(::Array).of(type)
|
21
28
|
end
|
22
29
|
|
23
30
|
# Build a hash schema
|
24
31
|
#
|
25
|
-
# @param [Symbol] schema Schema type
|
26
32
|
# @param [Hash{Symbol => Dry::Types::Type}] type_map
|
27
33
|
#
|
28
34
|
# @return [Dry::Types::Array]
|
29
|
-
|
30
|
-
|
31
|
-
self::Hash.public_send(schema, type_map)
|
35
|
+
def Hash(type_map)
|
36
|
+
Strict(::Hash).schema(type_map)
|
32
37
|
end
|
33
38
|
|
34
39
|
# Build a type which values are instances of a given class
|
@@ -42,9 +47,8 @@ module Dry
|
|
42
47
|
# @param [Class,Module] klass Class or module
|
43
48
|
#
|
44
49
|
# @return [Dry::Types::Type]
|
45
|
-
# @api public
|
46
50
|
def Instance(klass)
|
47
|
-
|
51
|
+
Nominal(klass).constrained(type: klass)
|
48
52
|
end
|
49
53
|
alias_method :Strict, :Instance
|
50
54
|
|
@@ -54,9 +58,8 @@ module Dry
|
|
54
58
|
# @param [Object] value
|
55
59
|
#
|
56
60
|
# @return [Dry::Types::Type]
|
57
|
-
# @api public
|
58
61
|
def Value(value)
|
59
|
-
|
62
|
+
Nominal(value.class).constrained(eql: value)
|
60
63
|
end
|
61
64
|
|
62
65
|
# Build a type with a single value
|
@@ -65,9 +68,8 @@ module Dry
|
|
65
68
|
# @param [Object] object
|
66
69
|
#
|
67
70
|
# @return [Dry::Types::Type]
|
68
|
-
# @api public
|
69
71
|
def Constant(object)
|
70
|
-
|
72
|
+
Nominal(object.class).constrained(is: object)
|
71
73
|
end
|
72
74
|
|
73
75
|
# Build a constructor type
|
@@ -78,19 +80,31 @@ module Dry
|
|
78
80
|
# @param [#call,nil] block Value constructor
|
79
81
|
#
|
80
82
|
# @return [Dry::Types::Type]
|
81
|
-
# @api public
|
82
83
|
def Constructor(klass, cons = nil, &block)
|
83
|
-
|
84
|
+
if klass.is_a?(Type)
|
85
|
+
if cons || block
|
86
|
+
klass.constructor(cons || block)
|
87
|
+
else
|
88
|
+
klass
|
89
|
+
end
|
90
|
+
else
|
91
|
+
Nominal(klass).constructor(cons || block || klass.method(:new))
|
92
|
+
end
|
84
93
|
end
|
85
94
|
|
86
|
-
# Build a
|
95
|
+
# Build a nominal type
|
87
96
|
#
|
88
97
|
# @param [Class] klass
|
89
98
|
#
|
90
99
|
# @return [Dry::Types::Type]
|
91
|
-
|
92
|
-
|
93
|
-
|
100
|
+
def Nominal(klass)
|
101
|
+
if klass <= ::Array
|
102
|
+
Array.new(klass)
|
103
|
+
elsif klass <= ::Hash
|
104
|
+
Hash.new(klass)
|
105
|
+
else
|
106
|
+
Nominal.new(klass)
|
107
|
+
end
|
94
108
|
end
|
95
109
|
|
96
110
|
# Build a map type
|
@@ -103,9 +117,24 @@ module Dry
|
|
103
117
|
# @param [Type] value_type Value type
|
104
118
|
#
|
105
119
|
# @return [Dry::Types::Map]
|
106
|
-
# @api public
|
107
120
|
def Map(key_type, value_type)
|
108
|
-
|
121
|
+
Nominal(::Hash).map(key_type, value_type)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Builds a constrained nominal type accepting any value that
|
125
|
+
# responds to given methods
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# Types::Callable = Types.Interface(:call)
|
129
|
+
# Types::Contact = Types.Interface(:name, :address)
|
130
|
+
#
|
131
|
+
# @param methods [Array<String, Symbol>] Method names
|
132
|
+
#
|
133
|
+
# @return [Dry::Types::Contrained]
|
134
|
+
def Interface(*methods)
|
135
|
+
methods.reduce(Types["nominal.any"]) do |type, method|
|
136
|
+
type.constrained(respond_to: method)
|
137
|
+
end
|
109
138
|
end
|
110
139
|
end
|
111
140
|
end
|
@@ -1,19 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
require "bigdecimal"
|
5
|
+
require "bigdecimal/util"
|
6
|
+
require "time"
|
5
7
|
|
6
8
|
module Dry
|
7
9
|
module Types
|
8
10
|
module Coercions
|
11
|
+
# JSON-specific coercions
|
12
|
+
#
|
13
|
+
# @api public
|
9
14
|
module JSON
|
10
15
|
extend Coercions
|
11
16
|
|
17
|
+
# @param [Object] input
|
18
|
+
#
|
19
|
+
# @return [nil] if the input is nil
|
20
|
+
#
|
21
|
+
# @raise CoercionError
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def self.to_nil(input, &_block)
|
25
|
+
if input.nil?
|
26
|
+
nil
|
27
|
+
elsif block_given?
|
28
|
+
yield
|
29
|
+
else
|
30
|
+
raise CoercionError, "#{input.inspect} is not nil"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
12
34
|
# @param [#to_d, Object] input
|
35
|
+
#
|
13
36
|
# @return [BigDecimal,nil]
|
14
|
-
|
15
|
-
|
16
|
-
|
37
|
+
#
|
38
|
+
# @raise CoercionError
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def self.to_decimal(input, &_block)
|
42
|
+
if input.is_a?(::Float)
|
43
|
+
input.to_d
|
44
|
+
else
|
45
|
+
BigDecimal(input)
|
46
|
+
end
|
47
|
+
rescue ArgumentError, TypeError
|
48
|
+
if block_given?
|
49
|
+
yield
|
50
|
+
else
|
51
|
+
raise CoercionError, "#{input} cannot be coerced to decimal"
|
52
|
+
end
|
17
53
|
end
|
18
54
|
end
|
19
55
|
end
|
@@ -1,78 +1,165 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
require "bigdecimal/util"
|
3
5
|
|
4
6
|
module Dry
|
5
7
|
module Types
|
6
8
|
module Coercions
|
9
|
+
# Params-specific coercions
|
10
|
+
#
|
11
|
+
# @api public
|
7
12
|
module Params
|
8
13
|
TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES Y].freeze
|
9
14
|
FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO N].freeze
|
10
|
-
BOOLEAN_MAP = ::Hash[
|
15
|
+
BOOLEAN_MAP = ::Hash[
|
16
|
+
TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])
|
17
|
+
].merge(true => true, false => false).freeze
|
11
18
|
|
12
19
|
extend Coercions
|
13
20
|
|
21
|
+
# @param [Object] input
|
22
|
+
#
|
23
|
+
# @return [nil] if the input is an empty string or nil
|
24
|
+
#
|
25
|
+
# @raise CoercionError
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def self.to_nil(input, &_block)
|
29
|
+
if input.nil? || empty_str?(input)
|
30
|
+
nil
|
31
|
+
elsif block_given?
|
32
|
+
yield
|
33
|
+
else
|
34
|
+
raise CoercionError, "#{input.inspect} is not nil"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
14
38
|
# @param [String, Object] input
|
39
|
+
#
|
15
40
|
# @return [Boolean,Object]
|
41
|
+
#
|
16
42
|
# @see TRUE_VALUES
|
17
43
|
# @see FALSE_VALUES
|
18
|
-
|
19
|
-
|
44
|
+
#
|
45
|
+
# @raise CoercionError
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def self.to_true(input, &_block)
|
49
|
+
BOOLEAN_MAP.fetch(input.to_s) do
|
50
|
+
if block_given?
|
51
|
+
yield
|
52
|
+
else
|
53
|
+
raise CoercionError, "#{input} cannot be coerced to true"
|
54
|
+
end
|
55
|
+
end
|
20
56
|
end
|
21
57
|
|
22
58
|
# @param [String, Object] input
|
59
|
+
#
|
23
60
|
# @return [Boolean,Object]
|
61
|
+
#
|
24
62
|
# @see TRUE_VALUES
|
25
63
|
# @see FALSE_VALUES
|
26
|
-
|
27
|
-
|
64
|
+
#
|
65
|
+
# @raise CoercionError
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def self.to_false(input, &_block)
|
69
|
+
BOOLEAN_MAP.fetch(input.to_s) do
|
70
|
+
if block_given?
|
71
|
+
yield
|
72
|
+
else
|
73
|
+
raise CoercionError, "#{input} cannot be coerced to false"
|
74
|
+
end
|
75
|
+
end
|
28
76
|
end
|
29
77
|
|
30
78
|
# @param [#to_int, #to_i, Object] input
|
79
|
+
#
|
31
80
|
# @return [Integer, nil, Object]
|
32
|
-
|
33
|
-
|
34
|
-
|
81
|
+
#
|
82
|
+
# @raise CoercionError
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
def self.to_int(input, &block)
|
86
|
+
if input.is_a? String
|
87
|
+
Integer(input, 10)
|
35
88
|
else
|
36
89
|
Integer(input)
|
37
90
|
end
|
38
|
-
rescue ArgumentError, TypeError
|
39
|
-
|
91
|
+
rescue ArgumentError, TypeError => e
|
92
|
+
CoercionError.handle(e, &block)
|
40
93
|
end
|
41
94
|
|
42
95
|
# @param [#to_f, Object] input
|
96
|
+
#
|
43
97
|
# @return [Float, nil, Object]
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
rescue ArgumentError, TypeError
|
51
|
-
|
98
|
+
#
|
99
|
+
# @raise CoercionError
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
def self.to_float(input, &block)
|
103
|
+
Float(input)
|
104
|
+
rescue ArgumentError, TypeError => e
|
105
|
+
CoercionError.handle(e, &block)
|
52
106
|
end
|
53
107
|
|
54
108
|
# @param [#to_d, Object] input
|
109
|
+
#
|
55
110
|
# @return [BigDecimal, nil, Object]
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
111
|
+
#
|
112
|
+
# @raise CoercionError
|
113
|
+
#
|
114
|
+
# @api public
|
115
|
+
def self.to_decimal(input, &_block)
|
116
|
+
to_float(input) do
|
117
|
+
if block_given?
|
118
|
+
return yield
|
119
|
+
else
|
120
|
+
raise CoercionError, "#{input.inspect} cannot be coerced to decimal"
|
121
|
+
end
|
63
122
|
end
|
123
|
+
|
124
|
+
input.to_d
|
64
125
|
end
|
65
126
|
|
66
127
|
# @param [Array, String, Object] input
|
128
|
+
#
|
67
129
|
# @return [Array, Object]
|
68
|
-
|
69
|
-
|
130
|
+
#
|
131
|
+
# @raise CoercionError
|
132
|
+
#
|
133
|
+
# @api public
|
134
|
+
def self.to_ary(input, &_block)
|
135
|
+
if empty_str?(input)
|
136
|
+
[]
|
137
|
+
elsif input.is_a?(::Array)
|
138
|
+
input
|
139
|
+
elsif block_given?
|
140
|
+
yield
|
141
|
+
else
|
142
|
+
raise CoercionError, "#{input.inspect} cannot be coerced to array"
|
143
|
+
end
|
70
144
|
end
|
71
145
|
|
72
146
|
# @param [Hash, String, Object] input
|
147
|
+
#
|
73
148
|
# @return [Hash, Object]
|
74
|
-
|
75
|
-
|
149
|
+
#
|
150
|
+
# @raise CoercionError
|
151
|
+
#
|
152
|
+
# @api public
|
153
|
+
def self.to_hash(input, &_block)
|
154
|
+
if empty_str?(input)
|
155
|
+
{}
|
156
|
+
elsif input.is_a?(::Hash)
|
157
|
+
input
|
158
|
+
elsif block_given?
|
159
|
+
yield
|
160
|
+
else
|
161
|
+
raise CoercionError, "#{input.inspect} cannot be coerced to hash"
|
162
|
+
end
|
76
163
|
end
|
77
164
|
end
|
78
165
|
end
|
data/lib/dry/types/coercions.rb
CHANGED
@@ -1,50 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
5
|
+
# Common coercion functions used by the built-in `Params` and `JSON` types
|
6
|
+
#
|
7
|
+
# @api public
|
3
8
|
module Coercions
|
4
9
|
include Dry::Core::Constants
|
5
10
|
|
6
|
-
# @param [String, Object] input
|
7
|
-
# @return [nil] if the input is an empty string
|
8
|
-
# @return [Object] otherwise the input object is returned
|
9
|
-
def to_nil(input)
|
10
|
-
input unless empty_str?(input)
|
11
|
-
end
|
12
|
-
|
13
11
|
# @param [#to_str, Object] input
|
12
|
+
#
|
14
13
|
# @return [Date, Object]
|
14
|
+
#
|
15
15
|
# @see Date.parse
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def to_date(input, &block)
|
19
|
+
if input.respond_to?(:to_str)
|
20
|
+
begin
|
21
|
+
::Date.parse(input)
|
22
|
+
rescue ArgumentError, RangeError => e
|
23
|
+
CoercionError.handle(e, &block)
|
24
|
+
end
|
25
|
+
elsif input.is_a?(::Date)
|
26
|
+
input
|
27
|
+
elsif block_given?
|
28
|
+
yield
|
29
|
+
else
|
30
|
+
raise CoercionError, "#{input.inspect} is not a string"
|
31
|
+
end
|
21
32
|
end
|
22
33
|
|
23
34
|
# @param [#to_str, Object] input
|
35
|
+
#
|
24
36
|
# @return [DateTime, Object]
|
37
|
+
#
|
25
38
|
# @see DateTime.parse
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def to_date_time(input, &block)
|
42
|
+
if input.respond_to?(:to_str)
|
43
|
+
begin
|
44
|
+
::DateTime.parse(input)
|
45
|
+
rescue ArgumentError => e
|
46
|
+
CoercionError.handle(e, &block)
|
47
|
+
end
|
48
|
+
elsif input.is_a?(::DateTime)
|
49
|
+
input
|
50
|
+
elsif block_given?
|
51
|
+
yield
|
52
|
+
else
|
53
|
+
raise CoercionError, "#{input.inspect} is not a string"
|
54
|
+
end
|
31
55
|
end
|
32
56
|
|
33
57
|
# @param [#to_str, Object] input
|
58
|
+
#
|
34
59
|
# @return [Time, Object]
|
60
|
+
#
|
35
61
|
# @see Time.parse
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
62
|
+
#
|
63
|
+
# @api public
|
64
|
+
def to_time(input, &block)
|
65
|
+
if input.respond_to?(:to_str)
|
66
|
+
begin
|
67
|
+
::Time.parse(input)
|
68
|
+
rescue ArgumentError => e
|
69
|
+
CoercionError.handle(e, &block)
|
70
|
+
end
|
71
|
+
elsif input.is_a?(::Time)
|
72
|
+
input
|
73
|
+
elsif block_given?
|
74
|
+
yield
|
75
|
+
else
|
76
|
+
raise CoercionError, "#{input.inspect} is not a string"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param [#to_sym, Object] input
|
81
|
+
#
|
82
|
+
# @return [Symbol, Object]
|
83
|
+
#
|
84
|
+
# @raise CoercionError
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def to_symbol(input, &block)
|
88
|
+
input.to_sym
|
89
|
+
rescue NoMethodError => e
|
90
|
+
CoercionError.handle(e, &block)
|
41
91
|
end
|
42
92
|
|
43
93
|
private
|
44
94
|
|
45
95
|
# Checks whether String is empty
|
96
|
+
#
|
46
97
|
# @param [String, Object] value
|
98
|
+
#
|
47
99
|
# @return [Boolean]
|
100
|
+
#
|
101
|
+
# @api private
|
48
102
|
def empty_str?(value)
|
49
103
|
EMPTY_STRING.eql?(value)
|
50
104
|
end
|
data/lib/dry/types/compat.rb
CHANGED
data/lib/dry/types/compiler.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
|
1
5
|
module Dry
|
2
6
|
module Types
|
7
|
+
# @api private
|
3
8
|
class Compiler
|
9
|
+
extend ::Dry::Core::Deprecations[:'dry-types']
|
10
|
+
|
4
11
|
attr_reader :registry
|
5
12
|
|
6
13
|
def initialize(registry)
|
@@ -13,33 +20,34 @@ module Dry
|
|
13
20
|
|
14
21
|
def visit(node)
|
15
22
|
type, body = node
|
16
|
-
send(:"visit_#{
|
23
|
+
send(:"visit_#{type}", body)
|
17
24
|
end
|
18
25
|
|
19
26
|
def visit_constrained(node)
|
20
|
-
|
21
|
-
|
27
|
+
nominal, rule = node
|
28
|
+
type = visit(nominal)
|
29
|
+
type.constrained_type.new(type, rule: visit_rule(rule))
|
22
30
|
end
|
23
31
|
|
24
32
|
def visit_constructor(node)
|
25
|
-
|
26
|
-
|
27
|
-
primitive
|
28
|
-
Types::Constructor.new(primitive, meta: meta, fn: fn)
|
33
|
+
nominal, fn = node
|
34
|
+
primitive = visit(nominal)
|
35
|
+
primitive.constructor(compile_fn(fn))
|
29
36
|
end
|
30
37
|
|
31
|
-
def
|
32
|
-
|
33
|
-
Types::Safe.new(visit(ast), meta: meta)
|
38
|
+
def visit_lax(node)
|
39
|
+
Types::Lax.new(visit(node))
|
34
40
|
end
|
41
|
+
deprecate(:visit_safe, :visit_lax)
|
35
42
|
|
36
|
-
def
|
43
|
+
def visit_nominal(node)
|
37
44
|
type, meta = node
|
45
|
+
nominal_name = "nominal.#{Types.identifier(type)}"
|
38
46
|
|
39
|
-
if registry.registered?(
|
40
|
-
registry[
|
47
|
+
if registry.registered?(nominal_name)
|
48
|
+
registry[nominal_name].meta(meta)
|
41
49
|
else
|
42
|
-
|
50
|
+
Nominal.new(type, meta: meta)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
@@ -55,65 +63,72 @@ module Dry
|
|
55
63
|
def visit_array(node)
|
56
64
|
member, meta = node
|
57
65
|
member = member.is_a?(Class) ? member : visit(member)
|
58
|
-
registry[
|
66
|
+
registry["nominal.array"].of(member).meta(meta)
|
59
67
|
end
|
60
68
|
|
61
69
|
def visit_hash(node)
|
62
|
-
|
63
|
-
|
70
|
+
opts, meta = node
|
71
|
+
registry["nominal.hash"].with(**opts, meta: meta)
|
64
72
|
end
|
65
73
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
74
|
+
def visit_schema(node)
|
75
|
+
keys, options, meta = node
|
76
|
+
registry["nominal.hash"].schema(keys.map { |key| visit(key) }).with(**options, meta: meta)
|
69
77
|
end
|
70
78
|
|
71
79
|
def visit_json_hash(node)
|
72
|
-
|
73
|
-
|
80
|
+
keys, meta = node
|
81
|
+
registry["json.hash"].schema(keys.map { |key| visit(key) }, meta)
|
74
82
|
end
|
75
83
|
|
76
84
|
def visit_json_array(node)
|
77
85
|
member, meta = node
|
78
|
-
registry[
|
86
|
+
registry["json.array"].of(visit(member)).meta(meta)
|
79
87
|
end
|
80
88
|
|
81
89
|
def visit_params_hash(node)
|
82
|
-
|
83
|
-
|
90
|
+
keys, meta = node
|
91
|
+
registry["params.hash"].schema(keys.map { |key| visit(key) }, meta)
|
84
92
|
end
|
85
93
|
|
86
94
|
def visit_params_array(node)
|
87
95
|
member, meta = node
|
88
|
-
registry[
|
96
|
+
registry["params.array"].of(visit(member)).meta(meta)
|
89
97
|
end
|
90
98
|
|
91
|
-
def
|
92
|
-
name, type = node
|
93
|
-
|
99
|
+
def visit_key(node)
|
100
|
+
name, required, type = node
|
101
|
+
Schema::Key.new(visit(type), name, required: required)
|
94
102
|
end
|
95
103
|
|
96
104
|
def visit_enum(node)
|
97
|
-
type, mapping
|
98
|
-
Enum.new(visit(type), mapping: mapping
|
105
|
+
type, mapping = node
|
106
|
+
Enum.new(visit(type), mapping: mapping)
|
99
107
|
end
|
100
108
|
|
101
109
|
def visit_map(node)
|
102
110
|
key_type, value_type, meta = node
|
103
|
-
registry[
|
111
|
+
registry["nominal.hash"].map(visit(key_type), visit(value_type)).meta(meta)
|
104
112
|
end
|
105
113
|
|
106
|
-
def
|
107
|
-
registry[
|
108
|
-
schema.map { |key| visit(key) }.reduce({}, :update),
|
109
|
-
constructor
|
110
|
-
)
|
114
|
+
def visit_any(meta)
|
115
|
+
registry["any"].meta(meta)
|
111
116
|
end
|
112
117
|
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
118
|
+
def compile_fn(fn)
|
119
|
+
type, *node = fn
|
120
|
+
|
121
|
+
case type
|
122
|
+
when :id
|
123
|
+
Dry::Types::FnContainer[node.fetch(0)]
|
124
|
+
when :callable
|
125
|
+
node.fetch(0)
|
126
|
+
when :method
|
127
|
+
target, method = node
|
128
|
+
target.method(method)
|
129
|
+
else
|
130
|
+
raise ArgumentError, "Cannot build callable from #{fn.inspect}"
|
131
|
+
end
|
117
132
|
end
|
118
133
|
end
|
119
134
|
end
|