dry-types 0.14.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +15 -0
- data/.rubocop.yml +43 -0
- data/.travis.yml +12 -11
- data/CHANGELOG.md +115 -4
- data/Gemfile +2 -3
- data/benchmarks/hash_schemas.rb +5 -5
- data/dry-types.gemspec +1 -1
- data/lib/dry/types.rb +67 -45
- data/lib/dry/types/any.rb +11 -2
- data/lib/dry/types/array.rb +1 -4
- data/lib/dry/types/array/member.rb +2 -2
- data/lib/dry/types/builder.rb +23 -3
- data/lib/dry/types/builder_methods.rb +10 -11
- data/lib/dry/types/coercions/params.rb +2 -0
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +25 -33
- data/lib/dry/types/constrained.rb +5 -4
- data/lib/dry/types/constructor.rb +32 -11
- data/lib/dry/types/container.rb +2 -0
- data/lib/dry/types/core.rb +22 -12
- data/lib/dry/types/default.rb +4 -4
- data/lib/dry/types/enum.rb +10 -3
- data/lib/dry/types/errors.rb +1 -1
- data/lib/dry/types/extensions/maybe.rb +11 -1
- data/lib/dry/types/hash.rb +70 -63
- data/lib/dry/types/hash/constructor.rb +20 -0
- data/lib/dry/types/json.rb +7 -7
- data/lib/dry/types/map.rb +6 -1
- data/lib/dry/types/module.rb +115 -0
- data/lib/dry/types/{definition.rb → nominal.rb} +10 -4
- data/lib/dry/types/options.rb +2 -2
- data/lib/dry/types/params.rb +11 -11
- data/lib/dry/types/printable.rb +12 -0
- data/lib/dry/types/printer.rb +309 -0
- data/lib/dry/types/result.rb +2 -2
- data/lib/dry/types/safe.rb +4 -2
- data/lib/dry/types/schema.rb +298 -0
- data/lib/dry/types/schema/key.rb +130 -0
- data/lib/dry/types/spec/types.rb +12 -4
- data/lib/dry/types/sum.rb +14 -15
- data/lib/dry/types/version.rb +1 -1
- metadata +18 -8
- data/lib/dry/types/compat/form_types.rb +0 -27
- data/lib/dry/types/compat/int.rb +0 -14
- data/lib/dry/types/hash/schema.rb +0 -199
- data/lib/dry/types/hash/schema_builder.rb +0 -75
data/lib/dry/types/any.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module Dry
|
2
2
|
module Types
|
3
|
-
Any = Class.new(
|
3
|
+
Any = Class.new(Nominal) do
|
4
|
+
def self.name
|
5
|
+
'Any'
|
6
|
+
end
|
7
|
+
|
4
8
|
def initialize(**options)
|
5
9
|
super(::Object, options)
|
6
10
|
end
|
@@ -19,9 +23,14 @@ module Dry
|
|
19
23
|
|
20
24
|
# @param [Hash] new_options
|
21
25
|
# @return [Type]
|
22
|
-
def with(new_options)
|
26
|
+
def with(**new_options)
|
23
27
|
self.class.new(**options, meta: @meta, **new_options)
|
24
28
|
end
|
29
|
+
|
30
|
+
# @return [Array]
|
31
|
+
def to_ast(meta: true)
|
32
|
+
[:any, meta ? self.meta : EMPTY_HASH]
|
33
|
+
end
|
25
34
|
end.new
|
26
35
|
end
|
27
36
|
end
|
data/lib/dry/types/array.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
require 'dry/types/array/member'
|
2
|
-
require 'dry/core/deprecations'
|
3
2
|
|
4
3
|
module Dry
|
5
4
|
module Types
|
6
|
-
class Array <
|
7
|
-
extend Dry::Core::Deprecations[:'dry-types']
|
8
|
-
|
5
|
+
class Array < Nominal
|
9
6
|
# @param [Type] type
|
10
7
|
# @return [Array::Member]
|
11
8
|
def of(type)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Dry
|
2
2
|
module Types
|
3
|
-
class Array <
|
3
|
+
class Array < Nominal
|
4
4
|
class Member < Array
|
5
5
|
# @return [Type]
|
6
6
|
attr_reader :member
|
@@ -51,7 +51,7 @@ module Dry
|
|
51
51
|
|
52
52
|
# @api public
|
53
53
|
#
|
54
|
-
# @see
|
54
|
+
# @see Nominal#to_ast
|
55
55
|
def to_ast(meta: true)
|
56
56
|
if member.respond_to?(:to_ast)
|
57
57
|
[:array, [member.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
|
data/lib/dry/types/builder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'dry/core/deprecations'
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
5
|
module Builder
|
@@ -8,6 +10,11 @@ module Dry
|
|
8
10
|
Constrained
|
9
11
|
end
|
10
12
|
|
13
|
+
# @return [Class]
|
14
|
+
def constructor_type
|
15
|
+
Constructor
|
16
|
+
end
|
17
|
+
|
11
18
|
# @param [Type] other
|
12
19
|
# @return [Sum, Sum::Constrained]
|
13
20
|
def |(other)
|
@@ -27,13 +34,26 @@ module Dry
|
|
27
34
|
end
|
28
35
|
|
29
36
|
# @param [Object] input
|
37
|
+
# @param [Hash] options
|
30
38
|
# @param [#call,nil] block
|
31
39
|
# @raise [ConstraintError]
|
32
40
|
# @return [Default]
|
33
|
-
def default(input = Undefined, &block)
|
41
|
+
def default(input = Undefined, options = EMPTY_HASH, &block)
|
42
|
+
unless input.frozen? || options[:shared]
|
43
|
+
where = Dry::Core::Deprecations::STACK.()
|
44
|
+
Dry::Core::Deprecations.warn(
|
45
|
+
"#{input.inspect} is mutable."\
|
46
|
+
' Be careful: types will return the same instance of the default'\
|
47
|
+
' value every time. Call `.freeze` when setting the default'\
|
48
|
+
' or pass `shared: true` to discard this warning.'\
|
49
|
+
"\n#{ where }",
|
50
|
+
tag: :'dry-types'
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
34
54
|
value = input.equal?(Undefined) ? block : input
|
35
55
|
|
36
|
-
if value.
|
56
|
+
if value.respond_to?(:call) || valid?(value)
|
37
57
|
Default[value].new(self, value)
|
38
58
|
else
|
39
59
|
raise ConstraintError.new("default value #{value.inspect} violates constraints", value)
|
@@ -63,7 +83,7 @@ module Dry
|
|
63
83
|
# @param [#call,nil] block
|
64
84
|
# @return [Constructor]
|
65
85
|
def constructor(constructor = nil, **options, &block)
|
66
|
-
|
86
|
+
constructor_type.new(with(options), fn: constructor || block)
|
67
87
|
end
|
68
88
|
end
|
69
89
|
end
|
@@ -22,13 +22,12 @@ module Dry
|
|
22
22
|
|
23
23
|
# Build a hash schema
|
24
24
|
#
|
25
|
-
# @param [Symbol] schema Schema type
|
26
25
|
# @param [Hash{Symbol => Dry::Types::Type}] type_map
|
27
26
|
#
|
28
27
|
# @return [Dry::Types::Array]
|
29
28
|
# @api public
|
30
|
-
def Hash(
|
31
|
-
self::Hash.
|
29
|
+
def Hash(type_map)
|
30
|
+
self::Hash.schema(type_map)
|
32
31
|
end
|
33
32
|
|
34
33
|
# Build a type which values are instances of a given class
|
@@ -44,7 +43,7 @@ module Dry
|
|
44
43
|
# @return [Dry::Types::Type]
|
45
44
|
# @api public
|
46
45
|
def Instance(klass)
|
47
|
-
|
46
|
+
Nominal.new(klass).constrained(type: klass)
|
48
47
|
end
|
49
48
|
alias_method :Strict, :Instance
|
50
49
|
|
@@ -56,7 +55,7 @@ module Dry
|
|
56
55
|
# @return [Dry::Types::Type]
|
57
56
|
# @api public
|
58
57
|
def Value(value)
|
59
|
-
|
58
|
+
Nominal.new(value.class).constrained(eql: value)
|
60
59
|
end
|
61
60
|
|
62
61
|
# Build a type with a single value
|
@@ -67,7 +66,7 @@ module Dry
|
|
67
66
|
# @return [Dry::Types::Type]
|
68
67
|
# @api public
|
69
68
|
def Constant(object)
|
70
|
-
|
69
|
+
Nominal.new(object.class).constrained(is: object)
|
71
70
|
end
|
72
71
|
|
73
72
|
# Build a constructor type
|
@@ -80,17 +79,17 @@ module Dry
|
|
80
79
|
# @return [Dry::Types::Type]
|
81
80
|
# @api public
|
82
81
|
def Constructor(klass, cons = nil, &block)
|
83
|
-
|
82
|
+
Nominal.new(klass).constructor(cons || block || klass.method(:new))
|
84
83
|
end
|
85
84
|
|
86
|
-
# Build a
|
85
|
+
# Build a nominal type
|
87
86
|
#
|
88
87
|
# @param [Class] klass
|
89
88
|
#
|
90
89
|
# @return [Dry::Types::Type]
|
91
90
|
# @api public
|
92
|
-
def
|
93
|
-
|
91
|
+
def Nominal(klass)
|
92
|
+
Nominal.new(klass)
|
94
93
|
end
|
95
94
|
|
96
95
|
# Build a map type
|
@@ -105,7 +104,7 @@ module Dry
|
|
105
104
|
# @return [Dry::Types::Map]
|
106
105
|
# @api public
|
107
106
|
def Map(key_type, value_type)
|
108
|
-
Types['hash'].map(key_type, value_type)
|
107
|
+
Types['nominal.hash'].map(key_type, value_type)
|
109
108
|
end
|
110
109
|
end
|
111
110
|
end
|
data/lib/dry/types/compat.rb
CHANGED
data/lib/dry/types/compiler.rb
CHANGED
@@ -17,14 +17,14 @@ module Dry
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def visit_constrained(node)
|
20
|
-
|
21
|
-
Types::Constrained.new(visit(
|
20
|
+
nominal, rule, meta = node
|
21
|
+
Types::Constrained.new(visit(nominal), rule: visit_rule(rule)).meta(meta)
|
22
22
|
end
|
23
23
|
|
24
24
|
def visit_constructor(node)
|
25
|
-
|
25
|
+
nominal, fn_register_name, meta = node
|
26
26
|
fn = Dry::Types::FnContainer[fn_register_name]
|
27
|
-
primitive = visit(
|
27
|
+
primitive = visit(nominal)
|
28
28
|
Types::Constructor.new(primitive, meta: meta, fn: fn)
|
29
29
|
end
|
30
30
|
|
@@ -33,13 +33,14 @@ module Dry
|
|
33
33
|
Types::Safe.new(visit(ast), meta: meta)
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def visit_nominal(node)
|
37
37
|
type, meta = node
|
38
|
+
nominal_name = "nominal.#{ Types.identifier(type) }"
|
38
39
|
|
39
|
-
if registry.registered?(
|
40
|
-
registry[
|
40
|
+
if registry.registered?(nominal_name)
|
41
|
+
registry[nominal_name].meta(meta)
|
41
42
|
else
|
42
|
-
|
43
|
+
Nominal.new(type, meta: meta)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
@@ -55,22 +56,22 @@ module Dry
|
|
55
56
|
def visit_array(node)
|
56
57
|
member, meta = node
|
57
58
|
member = member.is_a?(Class) ? member : visit(member)
|
58
|
-
registry['array'].of(member).meta(meta)
|
59
|
+
registry['nominal.array'].of(member).meta(meta)
|
59
60
|
end
|
60
61
|
|
61
62
|
def visit_hash(node)
|
62
|
-
|
63
|
-
|
63
|
+
opts, meta = node
|
64
|
+
registry['nominal.hash'].with(opts.merge(meta: meta))
|
64
65
|
end
|
65
66
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
67
|
+
def visit_schema(node)
|
68
|
+
keys, options, meta = node
|
69
|
+
registry['nominal.hash'].schema(keys.map { |key| visit(key) }).with(options.merge(meta: meta))
|
69
70
|
end
|
70
71
|
|
71
72
|
def visit_json_hash(node)
|
72
|
-
|
73
|
-
|
73
|
+
keys, meta = node
|
74
|
+
registry['json.hash'].schema(keys.map { |key| visit(key) }, meta)
|
74
75
|
end
|
75
76
|
|
76
77
|
def visit_json_array(node)
|
@@ -79,8 +80,8 @@ module Dry
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def visit_params_hash(node)
|
82
|
-
|
83
|
-
|
83
|
+
keys, meta = node
|
84
|
+
registry['params.hash'].schema(keys.map { |key| visit(key) }, meta)
|
84
85
|
end
|
85
86
|
|
86
87
|
def visit_params_array(node)
|
@@ -88,9 +89,9 @@ module Dry
|
|
88
89
|
registry['params.array'].of(visit(member)).meta(meta)
|
89
90
|
end
|
90
91
|
|
91
|
-
def
|
92
|
-
name, type = node
|
93
|
-
|
92
|
+
def visit_key(node)
|
93
|
+
name, required, type = node
|
94
|
+
Schema::Key.new(visit(type), name, required: required)
|
94
95
|
end
|
95
96
|
|
96
97
|
def visit_enum(node)
|
@@ -100,20 +101,11 @@ module Dry
|
|
100
101
|
|
101
102
|
def visit_map(node)
|
102
103
|
key_type, value_type, meta = node
|
103
|
-
registry['hash'].map(visit(key_type), visit(value_type)).meta(meta)
|
104
|
+
registry['nominal.hash'].map(visit(key_type), visit(value_type)).meta(meta)
|
104
105
|
end
|
105
106
|
|
106
|
-
def
|
107
|
-
registry[
|
108
|
-
schema.map { |key| visit(key) }.reduce({}, :update),
|
109
|
-
constructor
|
110
|
-
)
|
111
|
-
end
|
112
|
-
|
113
|
-
def merge_with_schema(hash_id, schema)
|
114
|
-
registry[hash_id].instantiate(
|
115
|
-
schema.map { |key| visit(key) }.reduce({}, :update)
|
116
|
-
)
|
107
|
+
def visit_any(meta)
|
108
|
+
registry['any'].meta(meta)
|
117
109
|
end
|
118
110
|
end
|
119
111
|
end
|
@@ -6,9 +6,10 @@ module Dry
|
|
6
6
|
module Types
|
7
7
|
class Constrained
|
8
8
|
include Type
|
9
|
-
include Dry::Equalizer(:type, :options, :rule, :meta)
|
10
9
|
include Decorator
|
11
10
|
include Builder
|
11
|
+
include Printable
|
12
|
+
include Dry::Equalizer(:type, :options, :rule, :meta, inspect: false)
|
12
13
|
|
13
14
|
# @return [Dry::Logic::Rule]
|
14
15
|
attr_reader :rule
|
@@ -24,9 +25,9 @@ module Dry
|
|
24
25
|
# @return [Object]
|
25
26
|
# @raise [ConstraintError]
|
26
27
|
def call(input)
|
27
|
-
try(input)
|
28
|
+
try(input) { |result|
|
28
29
|
raise ConstraintError.new(result, input)
|
29
|
-
|
30
|
+
}.input
|
30
31
|
end
|
31
32
|
alias_method :[], :call
|
32
33
|
|
@@ -75,7 +76,7 @@ module Dry
|
|
75
76
|
|
76
77
|
# @api public
|
77
78
|
#
|
78
|
-
# @see
|
79
|
+
# @see Nominal#to_ast
|
79
80
|
def to_ast(meta: true)
|
80
81
|
[:constrained, [type.to_ast(meta: meta),
|
81
82
|
rule.to_ast,
|
@@ -2,8 +2,8 @@ require 'dry/types/fn_container'
|
|
2
2
|
|
3
3
|
module Dry
|
4
4
|
module Types
|
5
|
-
class Constructor <
|
6
|
-
include Dry::Equalizer(:type, :options, :meta)
|
5
|
+
class Constructor < Nominal
|
6
|
+
include Dry::Equalizer(:type, :options, :meta, inspect: false)
|
7
7
|
|
8
8
|
# @return [#call]
|
9
9
|
attr_reader :fn
|
@@ -17,7 +17,7 @@ module Dry
|
|
17
17
|
# @param [Hash] options
|
18
18
|
# @param [#call, nil] block
|
19
19
|
def self.new(input, **options, &block)
|
20
|
-
type = input.is_a?(Builder) ? input :
|
20
|
+
type = input.is_a?(Builder) ? input : Nominal.new(input)
|
21
21
|
super(type, **options, &block)
|
22
22
|
end
|
23
23
|
|
@@ -43,6 +43,7 @@ module Dry
|
|
43
43
|
type.name
|
44
44
|
end
|
45
45
|
|
46
|
+
# @return [Boolean]
|
46
47
|
def default?
|
47
48
|
type.default?
|
48
49
|
end
|
@@ -72,8 +73,10 @@ module Dry
|
|
72
73
|
left = new_fn || block
|
73
74
|
right = fn
|
74
75
|
|
75
|
-
with(options
|
76
|
+
with(**options, fn: -> input { left[right[input]] })
|
76
77
|
end
|
78
|
+
alias_method :append, :constructor
|
79
|
+
alias_method :>>, :constructor
|
77
80
|
|
78
81
|
# @param [Object] value
|
79
82
|
# @return [Boolean]
|
@@ -93,13 +96,27 @@ module Dry
|
|
93
96
|
|
94
97
|
# @api public
|
95
98
|
#
|
96
|
-
# @see
|
99
|
+
# @see Nominal#to_ast
|
97
100
|
def to_ast(meta: true)
|
98
101
|
[:constructor, [type.to_ast(meta: meta),
|
99
102
|
register_fn(fn),
|
100
103
|
meta ? self.meta : EMPTY_HASH]]
|
101
104
|
end
|
102
105
|
|
106
|
+
# @api public
|
107
|
+
#
|
108
|
+
# @param [#call, nil] new_fn
|
109
|
+
# @param [Hash] options
|
110
|
+
# @param [#call, nil] block
|
111
|
+
# @return [Constructor]
|
112
|
+
def prepend(new_fn = nil, **options, &block)
|
113
|
+
left = new_fn || block
|
114
|
+
right = fn
|
115
|
+
|
116
|
+
with(**options, fn: -> input { right[left[input]] })
|
117
|
+
end
|
118
|
+
alias_method :<<, :prepend
|
119
|
+
|
103
120
|
private
|
104
121
|
|
105
122
|
def register_fn(fn)
|
@@ -114,15 +131,15 @@ module Dry
|
|
114
131
|
end
|
115
132
|
|
116
133
|
# Delegates missing methods to {#type}
|
117
|
-
# @param [Symbol]
|
134
|
+
# @param [Symbol] method
|
118
135
|
# @param [Array] args
|
119
136
|
# @param [#call, nil] block
|
120
|
-
def method_missing(
|
121
|
-
if type.respond_to?(
|
122
|
-
response = type.__send__(
|
137
|
+
def method_missing(method, *args, &block)
|
138
|
+
if type.respond_to?(method)
|
139
|
+
response = type.__send__(method, *args, &block)
|
123
140
|
|
124
|
-
if
|
125
|
-
|
141
|
+
if composable?(response)
|
142
|
+
response.constructor_type.new(response, options)
|
126
143
|
else
|
127
144
|
response
|
128
145
|
end
|
@@ -130,6 +147,10 @@ module Dry
|
|
130
147
|
super
|
131
148
|
end
|
132
149
|
end
|
150
|
+
|
151
|
+
def composable?(value)
|
152
|
+
value.kind_of?(Builder)
|
153
|
+
end
|
133
154
|
end
|
134
155
|
end
|
135
156
|
end
|