dry-types 0.9.0 → 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 +5 -5
- data/.codeclimate.yml +15 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +43 -0
- data/.travis.yml +15 -14
- data/.yardopts +5 -0
- data/CHANGELOG.md +494 -88
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +7 -6
- data/README.md +1 -3
- data/Rakefile +8 -3
- data/benchmarks/hash_schemas.rb +7 -7
- data/dry-types.gemspec +11 -9
- data/lib/dry/types/any.rb +36 -0
- data/lib/dry/types/array/member.rb +29 -4
- data/lib/dry/types/array.rb +6 -4
- data/lib/dry/types/builder.rb +48 -6
- data/lib/dry/types/builder_methods.rb +111 -0
- data/lib/dry/types/coercions/json.rb +3 -0
- data/lib/dry/types/coercions/{form.rb → params.rb} +23 -3
- data/lib/dry/types/coercions.rb +16 -3
- data/lib/dry/types/compat.rb +0 -0
- data/lib/dry/types/compiler.rb +66 -39
- data/lib/dry/types/constrained/coercible.rb +7 -1
- data/lib/dry/types/constrained.rb +42 -3
- data/lib/dry/types/constraints.rb +3 -0
- data/lib/dry/types/constructor.rb +98 -16
- data/lib/dry/types/container.rb +2 -0
- data/lib/dry/types/core.rb +30 -14
- data/lib/dry/types/decorator.rb +31 -5
- data/lib/dry/types/default.rb +34 -8
- data/lib/dry/types/enum.rb +71 -14
- data/lib/dry/types/errors.rb +23 -6
- data/lib/dry/types/extensions/maybe.rb +35 -16
- data/lib/dry/types/fn_container.rb +34 -0
- data/lib/dry/types/hash/constructor.rb +20 -0
- data/lib/dry/types/hash.rb +103 -23
- data/lib/dry/types/inflector.rb +7 -0
- data/lib/dry/types/json.rb +7 -7
- data/lib/dry/types/map.rb +98 -0
- data/lib/dry/types/module.rb +115 -0
- data/lib/dry/types/nominal.rb +119 -0
- data/lib/dry/types/options.rb +29 -7
- data/lib/dry/types/params.rb +53 -0
- data/lib/dry/types/printable.rb +12 -0
- data/lib/dry/types/printer.rb +309 -0
- data/lib/dry/types/result.rb +12 -2
- data/lib/dry/types/safe.rb +27 -1
- data/lib/dry/types/schema/key.rb +130 -0
- data/lib/dry/types/schema.rb +298 -0
- data/lib/dry/types/spec/types.rb +102 -0
- data/lib/dry/types/sum.rb +75 -13
- data/lib/dry/types/type.rb +6 -0
- data/lib/dry/types/version.rb +1 -1
- data/lib/dry/types.rb +104 -38
- data/log/.gitkeep +0 -0
- metadata +81 -50
- data/lib/dry/types/definition.rb +0 -79
- data/lib/dry/types/form.rb +0 -53
- data/lib/dry/types/hash/schema.rb +0 -156
- data/lib/spec/dry/types.rb +0 -56
data/lib/dry/types/compiler.rb
CHANGED
|
@@ -11,74 +11,101 @@ module Dry
|
|
|
11
11
|
visit(ast)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def visit(node
|
|
15
|
-
|
|
14
|
+
def visit(node)
|
|
15
|
+
type, body = node
|
|
16
|
+
send(:"visit_#{ type }", body)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def visit_constrained(node)
|
|
20
|
+
nominal, rule, meta = node
|
|
21
|
+
Types::Constrained.new(visit(nominal), rule: visit_rule(rule)).meta(meta)
|
|
16
22
|
end
|
|
17
23
|
|
|
18
24
|
def visit_constructor(node)
|
|
19
|
-
|
|
20
|
-
Types::
|
|
25
|
+
nominal, fn_register_name, meta = node
|
|
26
|
+
fn = Dry::Types::FnContainer[fn_register_name]
|
|
27
|
+
primitive = visit(nominal)
|
|
28
|
+
Types::Constructor.new(primitive, meta: meta, fn: fn)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def visit_safe(node)
|
|
32
|
+
ast, meta = node
|
|
33
|
+
Types::Safe.new(visit(ast), meta: meta)
|
|
21
34
|
end
|
|
22
35
|
|
|
23
|
-
def
|
|
24
|
-
type,
|
|
25
|
-
|
|
36
|
+
def visit_nominal(node)
|
|
37
|
+
type, meta = node
|
|
38
|
+
nominal_name = "nominal.#{ Types.identifier(type) }"
|
|
26
39
|
|
|
27
|
-
if
|
|
28
|
-
|
|
40
|
+
if registry.registered?(nominal_name)
|
|
41
|
+
registry[nominal_name].meta(meta)
|
|
29
42
|
else
|
|
30
|
-
|
|
43
|
+
Nominal.new(type, meta: meta)
|
|
31
44
|
end
|
|
32
45
|
end
|
|
33
46
|
|
|
47
|
+
def visit_rule(node)
|
|
48
|
+
Dry::Types.rule_compiler.([node])[0]
|
|
49
|
+
end
|
|
50
|
+
|
|
34
51
|
def visit_sum(node)
|
|
35
|
-
|
|
52
|
+
*types, meta = node
|
|
53
|
+
types.map { |type| visit(type) }.reduce(:|).meta(meta)
|
|
36
54
|
end
|
|
37
55
|
|
|
38
56
|
def visit_array(node)
|
|
39
|
-
|
|
57
|
+
member, meta = node
|
|
58
|
+
member = member.is_a?(Class) ? member : visit(member)
|
|
59
|
+
registry['nominal.array'].of(member).meta(meta)
|
|
40
60
|
end
|
|
41
61
|
|
|
42
|
-
def
|
|
43
|
-
|
|
62
|
+
def visit_hash(node)
|
|
63
|
+
opts, meta = node
|
|
64
|
+
registry['nominal.hash'].with(opts.merge(meta: meta))
|
|
44
65
|
end
|
|
45
66
|
|
|
46
|
-
def
|
|
47
|
-
|
|
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))
|
|
48
70
|
end
|
|
49
71
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
def visit_json_hash(node)
|
|
73
|
+
keys, meta = node
|
|
74
|
+
registry['json.hash'].schema(keys.map { |key| visit(key) }, meta)
|
|
53
75
|
end
|
|
54
76
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
merge_with('form.hash', constructor, schema)
|
|
59
|
-
else
|
|
60
|
-
registry['form.hash']
|
|
61
|
-
end
|
|
77
|
+
def visit_json_array(node)
|
|
78
|
+
member, meta = node
|
|
79
|
+
registry['json.array'].of(visit(member)).meta(meta)
|
|
62
80
|
end
|
|
63
81
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
82
|
+
def visit_params_hash(node)
|
|
83
|
+
keys, meta = node
|
|
84
|
+
registry['params.hash'].schema(keys.map { |key| visit(key) }, meta)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def visit_params_array(node)
|
|
88
|
+
member, meta = node
|
|
89
|
+
registry['params.array'].of(visit(member)).meta(meta)
|
|
71
90
|
end
|
|
72
91
|
|
|
73
92
|
def visit_key(node)
|
|
74
|
-
name,
|
|
75
|
-
|
|
93
|
+
name, required, type = node
|
|
94
|
+
Schema::Key.new(visit(type), name, required: required)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def visit_enum(node)
|
|
98
|
+
type, mapping, meta = node
|
|
99
|
+
Enum.new(visit(type), mapping: mapping, meta: meta)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def visit_map(node)
|
|
103
|
+
key_type, value_type, meta = node
|
|
104
|
+
registry['nominal.hash'].map(visit(key_type), visit(value_type)).meta(meta)
|
|
76
105
|
end
|
|
77
106
|
|
|
78
|
-
def
|
|
79
|
-
registry[
|
|
80
|
-
constructor, schema.map { |key| visit(key) }.reduce({}, :merge)
|
|
81
|
-
)
|
|
107
|
+
def visit_any(meta)
|
|
108
|
+
registry['any'].meta(meta)
|
|
82
109
|
end
|
|
83
110
|
end
|
|
84
111
|
end
|
|
@@ -2,6 +2,11 @@ module Dry
|
|
|
2
2
|
module Types
|
|
3
3
|
class Constrained
|
|
4
4
|
class Coercible < Constrained
|
|
5
|
+
# @param [Object] input
|
|
6
|
+
# @param [#call,nil] block
|
|
7
|
+
# @yieldparam [Failure] failure
|
|
8
|
+
# @yieldreturn [Result]
|
|
9
|
+
# @return [Result,nil]
|
|
5
10
|
def try(input, &block)
|
|
6
11
|
result = type.try(input)
|
|
7
12
|
|
|
@@ -11,7 +16,8 @@ module Dry
|
|
|
11
16
|
if validation.success?
|
|
12
17
|
result
|
|
13
18
|
else
|
|
14
|
-
|
|
19
|
+
failure = failure(result.input, validation)
|
|
20
|
+
block ? yield(failure) : failure
|
|
15
21
|
end
|
|
16
22
|
else
|
|
17
23
|
block ? yield(result) : result
|
|
@@ -5,24 +5,38 @@ require 'dry/types/constrained/coercible'
|
|
|
5
5
|
module Dry
|
|
6
6
|
module Types
|
|
7
7
|
class Constrained
|
|
8
|
-
include
|
|
8
|
+
include Type
|
|
9
9
|
include Decorator
|
|
10
10
|
include Builder
|
|
11
|
+
include Printable
|
|
12
|
+
include Dry::Equalizer(:type, :options, :rule, :meta, inspect: false)
|
|
11
13
|
|
|
14
|
+
# @return [Dry::Logic::Rule]
|
|
12
15
|
attr_reader :rule
|
|
13
16
|
|
|
17
|
+
# @param [Type] type
|
|
18
|
+
# @param [Hash] options
|
|
14
19
|
def initialize(type, options)
|
|
15
20
|
super
|
|
16
21
|
@rule = options.fetch(:rule)
|
|
17
22
|
end
|
|
18
23
|
|
|
24
|
+
# @param [Object] input
|
|
25
|
+
# @return [Object]
|
|
26
|
+
# @raise [ConstraintError]
|
|
19
27
|
def call(input)
|
|
20
|
-
try(input)
|
|
28
|
+
try(input) { |result|
|
|
21
29
|
raise ConstraintError.new(result, input)
|
|
22
|
-
|
|
30
|
+
}.input
|
|
23
31
|
end
|
|
24
32
|
alias_method :[], :call
|
|
25
33
|
|
|
34
|
+
# @param [Object] input
|
|
35
|
+
# @param [#call,nil] block
|
|
36
|
+
# @yieldparam [Failure] failure
|
|
37
|
+
# @yieldreturn [Result]
|
|
38
|
+
# @return [Logic::Result, Result]
|
|
39
|
+
# @return [Object] if block given and try fails
|
|
26
40
|
def try(input, &block)
|
|
27
41
|
result = rule.(input)
|
|
28
42
|
|
|
@@ -34,20 +48,45 @@ module Dry
|
|
|
34
48
|
end
|
|
35
49
|
end
|
|
36
50
|
|
|
51
|
+
# @param [Object] value
|
|
52
|
+
# @return [Boolean]
|
|
37
53
|
def valid?(value)
|
|
38
54
|
rule.(value).success? && type.valid?(value)
|
|
39
55
|
end
|
|
40
56
|
|
|
57
|
+
# @param [Hash] options
|
|
58
|
+
# The options hash provided to {Types.Rule} and combined
|
|
59
|
+
# using {&} with previous {#rule}
|
|
60
|
+
# @return [Constrained]
|
|
61
|
+
# @see Dry::Logic::Operators#and
|
|
41
62
|
def constrained(options)
|
|
42
63
|
with(rule: rule & Types.Rule(options))
|
|
43
64
|
end
|
|
44
65
|
|
|
66
|
+
# @return [true]
|
|
45
67
|
def constrained?
|
|
46
68
|
true
|
|
47
69
|
end
|
|
48
70
|
|
|
71
|
+
# @param [Object] value
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
def ===(value)
|
|
74
|
+
valid?(value)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @api public
|
|
78
|
+
#
|
|
79
|
+
# @see Nominal#to_ast
|
|
80
|
+
def to_ast(meta: true)
|
|
81
|
+
[:constrained, [type.to_ast(meta: meta),
|
|
82
|
+
rule.to_ast,
|
|
83
|
+
meta ? self.meta : EMPTY_HASH]]
|
|
84
|
+
end
|
|
85
|
+
|
|
49
86
|
private
|
|
50
87
|
|
|
88
|
+
# @param [Object] response
|
|
89
|
+
# @return [Boolean]
|
|
51
90
|
def decorate?(response)
|
|
52
91
|
super || response.kind_of?(Constructor)
|
|
53
92
|
end
|
|
@@ -4,12 +4,15 @@ require 'dry/logic/rule/predicate'
|
|
|
4
4
|
|
|
5
5
|
module Dry
|
|
6
6
|
module Types
|
|
7
|
+
# @param [Hash] options
|
|
8
|
+
# @return [Dry::Logic::Rule]
|
|
7
9
|
def self.Rule(options)
|
|
8
10
|
rule_compiler.(
|
|
9
11
|
options.map { |key, val| Logic::Rule::Predicate.new(Logic::Predicates[:"#{key}?"]).curry(val).to_ast }
|
|
10
12
|
).reduce(:and)
|
|
11
13
|
end
|
|
12
14
|
|
|
15
|
+
# @return [Dry::Logic::RuleCompiler]
|
|
13
16
|
def self.rule_compiler
|
|
14
17
|
@rule_compiler ||= Logic::RuleCompiler.new(Logic::Predicates)
|
|
15
18
|
end
|
|
@@ -1,67 +1,145 @@
|
|
|
1
|
-
require 'dry/types/
|
|
1
|
+
require 'dry/types/fn_container'
|
|
2
2
|
|
|
3
3
|
module Dry
|
|
4
4
|
module Types
|
|
5
|
-
class Constructor <
|
|
6
|
-
include Dry::Equalizer(:type)
|
|
5
|
+
class Constructor < Nominal
|
|
6
|
+
include Dry::Equalizer(:type, :options, :meta, inspect: false)
|
|
7
7
|
|
|
8
|
+
# @return [#call]
|
|
8
9
|
attr_reader :fn
|
|
9
10
|
|
|
11
|
+
# @return [Type]
|
|
10
12
|
attr_reader :type
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
undef :constrained?
|
|
15
|
+
|
|
16
|
+
# @param [Builder, Object] input
|
|
17
|
+
# @param [Hash] options
|
|
18
|
+
# @param [#call, nil] block
|
|
19
|
+
def self.new(input, **options, &block)
|
|
20
|
+
type = input.is_a?(Builder) ? input : Nominal.new(input)
|
|
21
|
+
super(type, **options, &block)
|
|
15
22
|
end
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
# @param [Type] type
|
|
25
|
+
# @param [Hash] options
|
|
26
|
+
# @param [#call, nil] block
|
|
27
|
+
def initialize(type, **options, &block)
|
|
18
28
|
@type = type
|
|
19
29
|
@fn = options.fetch(:fn, block)
|
|
20
|
-
|
|
30
|
+
|
|
31
|
+
raise ArgumentError, 'Missing constructor block' if fn.nil?
|
|
32
|
+
|
|
33
|
+
super(type, **options, fn: fn)
|
|
21
34
|
end
|
|
22
35
|
|
|
36
|
+
# @return [Class]
|
|
23
37
|
def primitive
|
|
24
38
|
type.primitive
|
|
25
39
|
end
|
|
26
40
|
|
|
41
|
+
# @return [String]
|
|
42
|
+
def name
|
|
43
|
+
type.name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Boolean]
|
|
47
|
+
def default?
|
|
48
|
+
type.default?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @param [Object] input
|
|
52
|
+
# @return [Object]
|
|
27
53
|
def call(input)
|
|
28
54
|
type[fn[input]]
|
|
29
55
|
end
|
|
30
56
|
alias_method :[], :call
|
|
31
57
|
|
|
58
|
+
# @param [Object] input
|
|
59
|
+
# @param [#call,nil] block
|
|
60
|
+
# @return [Logic::Result, Types::Result]
|
|
61
|
+
# @return [Object] if block given and try fails
|
|
32
62
|
def try(input, &block)
|
|
33
63
|
type.try(fn[input], &block)
|
|
34
|
-
rescue TypeError => e
|
|
64
|
+
rescue TypeError, ArgumentError => e
|
|
35
65
|
failure(input, e.message)
|
|
36
66
|
end
|
|
37
67
|
|
|
68
|
+
# @param [#call, nil] new_fn
|
|
69
|
+
# @param [Hash] options
|
|
70
|
+
# @param [#call, nil] block
|
|
71
|
+
# @return [Constructor]
|
|
38
72
|
def constructor(new_fn = nil, **options, &block)
|
|
39
73
|
left = new_fn || block
|
|
40
74
|
right = fn
|
|
41
75
|
|
|
42
|
-
with(options
|
|
76
|
+
with(**options, fn: -> input { left[right[input]] })
|
|
43
77
|
end
|
|
78
|
+
alias_method :append, :constructor
|
|
79
|
+
alias_method :>>, :constructor
|
|
44
80
|
|
|
81
|
+
# @param [Object] value
|
|
82
|
+
# @return [Boolean]
|
|
45
83
|
def valid?(value)
|
|
46
|
-
|
|
84
|
+
constructed_value = fn[value]
|
|
85
|
+
rescue NoMethodError, TypeError, ArgumentError
|
|
86
|
+
false
|
|
87
|
+
else
|
|
88
|
+
type.valid?(constructed_value)
|
|
47
89
|
end
|
|
90
|
+
alias_method :===, :valid?
|
|
48
91
|
|
|
92
|
+
# @return [Class]
|
|
49
93
|
def constrained_type
|
|
50
94
|
Constrained::Coercible
|
|
51
95
|
end
|
|
52
96
|
|
|
97
|
+
# @api public
|
|
98
|
+
#
|
|
99
|
+
# @see Nominal#to_ast
|
|
100
|
+
def to_ast(meta: true)
|
|
101
|
+
[:constructor, [type.to_ast(meta: meta),
|
|
102
|
+
register_fn(fn),
|
|
103
|
+
meta ? self.meta : EMPTY_HASH]]
|
|
104
|
+
end
|
|
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
|
+
|
|
53
120
|
private
|
|
54
121
|
|
|
122
|
+
def register_fn(fn)
|
|
123
|
+
Dry::Types::FnContainer.register(fn)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @param [Symbol] meth
|
|
127
|
+
# @param [Boolean] include_private
|
|
128
|
+
# @return [Boolean]
|
|
55
129
|
def respond_to_missing?(meth, include_private = false)
|
|
56
130
|
super || type.respond_to?(meth)
|
|
57
131
|
end
|
|
58
132
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
133
|
+
# Delegates missing methods to {#type}
|
|
134
|
+
# @param [Symbol] method
|
|
135
|
+
# @param [Array] args
|
|
136
|
+
# @param [#call, nil] block
|
|
137
|
+
def method_missing(method, *args, &block)
|
|
138
|
+
if type.respond_to?(method)
|
|
139
|
+
response = type.__send__(method, *args, &block)
|
|
62
140
|
|
|
63
|
-
if
|
|
64
|
-
|
|
141
|
+
if composable?(response)
|
|
142
|
+
response.constructor_type.new(response, options)
|
|
65
143
|
else
|
|
66
144
|
response
|
|
67
145
|
end
|
|
@@ -69,6 +147,10 @@ module Dry
|
|
|
69
147
|
super
|
|
70
148
|
end
|
|
71
149
|
end
|
|
150
|
+
|
|
151
|
+
def composable?(value)
|
|
152
|
+
value.kind_of?(Builder)
|
|
153
|
+
end
|
|
72
154
|
end
|
|
73
155
|
end
|
|
74
156
|
end
|
data/lib/dry/types/container.rb
CHANGED
data/lib/dry/types/core.rb
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
require 'dry/types/any'
|
|
2
|
+
|
|
1
3
|
module Dry
|
|
2
4
|
module Types
|
|
5
|
+
# Primitives with {Kernel} coercion methods
|
|
3
6
|
COERCIBLE = {
|
|
4
7
|
string: String,
|
|
5
|
-
|
|
8
|
+
integer: Integer,
|
|
6
9
|
float: Float,
|
|
7
10
|
decimal: BigDecimal,
|
|
8
11
|
array: ::Array,
|
|
9
12
|
hash: ::Hash
|
|
10
13
|
}.freeze
|
|
11
14
|
|
|
15
|
+
# Primitives that are non-coercible through {Kernel} methods
|
|
12
16
|
NON_COERCIBLE = {
|
|
13
17
|
nil: NilClass,
|
|
14
18
|
symbol: Symbol,
|
|
@@ -17,44 +21,56 @@ module Dry
|
|
|
17
21
|
false: FalseClass,
|
|
18
22
|
date: Date,
|
|
19
23
|
date_time: DateTime,
|
|
20
|
-
time: Time
|
|
24
|
+
time: Time,
|
|
25
|
+
range: Range
|
|
21
26
|
}.freeze
|
|
22
27
|
|
|
28
|
+
# All built-in primitives
|
|
23
29
|
ALL_PRIMITIVES = COERCIBLE.merge(NON_COERCIBLE).freeze
|
|
24
30
|
|
|
31
|
+
# All built-in primitives except {NilClass}
|
|
25
32
|
NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
|
|
26
33
|
|
|
27
|
-
# Register
|
|
34
|
+
# Register generic types for {ALL_PRIMITIVES}
|
|
28
35
|
ALL_PRIMITIVES.each do |name, primitive|
|
|
29
|
-
|
|
36
|
+
type = Nominal[primitive].new(primitive)
|
|
37
|
+
register("nominal.#{name}", type)
|
|
30
38
|
end
|
|
31
39
|
|
|
32
|
-
# Register strict
|
|
40
|
+
# Register strict types for {ALL_PRIMITIVES}
|
|
33
41
|
ALL_PRIMITIVES.each do |name, primitive|
|
|
34
|
-
|
|
42
|
+
type = self["nominal.#{name}"].constrained(type: primitive)
|
|
43
|
+
register(name.to_s, type)
|
|
44
|
+
register("strict.#{name}", type)
|
|
35
45
|
end
|
|
36
46
|
|
|
37
|
-
# Register
|
|
47
|
+
# Register {COERCIBLE} types
|
|
38
48
|
COERCIBLE.each do |name, primitive|
|
|
39
|
-
register("coercible.#{name}", self[name
|
|
49
|
+
register("coercible.#{name}", self["nominal.#{name}"].constructor(Kernel.method(primitive.name)))
|
|
40
50
|
end
|
|
41
51
|
|
|
42
|
-
# Register
|
|
52
|
+
# Register optional strict {NON_NIL} types
|
|
43
53
|
NON_NIL.each_key do |name|
|
|
44
54
|
register("optional.strict.#{name}", self["strict.#{name}"].optional)
|
|
45
55
|
end
|
|
46
56
|
|
|
47
|
-
# Register
|
|
57
|
+
# Register optional {COERCIBLE} types
|
|
48
58
|
COERCIBLE.each_key do |name|
|
|
49
59
|
register("optional.coercible.#{name}", self["coercible.#{name}"].optional)
|
|
50
60
|
end
|
|
51
61
|
|
|
52
|
-
# Register
|
|
53
|
-
register("bool", self["true"] | self["false"])
|
|
54
|
-
|
|
62
|
+
# Register `:bool` since it's common and not a built-in Ruby type :(
|
|
63
|
+
register("nominal.bool", self["nominal.true"] | self["nominal.false"])
|
|
64
|
+
bool = self["strict.true"] | self["strict.false"]
|
|
65
|
+
register("strict.bool", bool)
|
|
66
|
+
register("bool", bool)
|
|
67
|
+
|
|
68
|
+
register("any", Any)
|
|
69
|
+
register("nominal.any", Any)
|
|
70
|
+
register("strict.any", Any)
|
|
55
71
|
end
|
|
56
72
|
end
|
|
57
73
|
|
|
58
74
|
require 'dry/types/coercions'
|
|
59
|
-
require 'dry/types/
|
|
75
|
+
require 'dry/types/params'
|
|
60
76
|
require 'dry/types/json'
|
data/lib/dry/types/decorator.rb
CHANGED
|
@@ -5,49 +5,70 @@ module Dry
|
|
|
5
5
|
module Decorator
|
|
6
6
|
include Options
|
|
7
7
|
|
|
8
|
+
# @return [Type]
|
|
8
9
|
attr_reader :type
|
|
9
10
|
|
|
11
|
+
# @param [Type] type
|
|
10
12
|
def initialize(type, *)
|
|
11
13
|
super
|
|
12
14
|
@type = type
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
# @param [Object] input
|
|
18
|
+
# @param [#call, nil] block
|
|
19
|
+
# @return [Result,Logic::Result]
|
|
20
|
+
# @return [Object] if block given and try fails
|
|
19
21
|
def try(input, &block)
|
|
20
22
|
type.try(input, &block)
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
# @param [Object] value
|
|
26
|
+
# @return [Boolean]
|
|
23
27
|
def valid?(value)
|
|
24
28
|
type.valid?(value)
|
|
25
29
|
end
|
|
30
|
+
alias_method :===, :valid?
|
|
26
31
|
|
|
32
|
+
# @return [Boolean]
|
|
27
33
|
def default?
|
|
28
34
|
type.default?
|
|
29
35
|
end
|
|
30
36
|
|
|
37
|
+
# @return [Boolean]
|
|
31
38
|
def constrained?
|
|
32
39
|
type.constrained?
|
|
33
40
|
end
|
|
34
41
|
|
|
42
|
+
# @return [Sum]
|
|
43
|
+
def optional
|
|
44
|
+
Types['strict.nil'] | self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param [Symbol] meth
|
|
48
|
+
# @param [Boolean] include_private
|
|
49
|
+
# @return [Boolean]
|
|
35
50
|
def respond_to_missing?(meth, include_private = false)
|
|
36
51
|
super || type.respond_to?(meth)
|
|
37
52
|
end
|
|
38
53
|
|
|
39
54
|
private
|
|
40
55
|
|
|
56
|
+
# @param [Object] response
|
|
57
|
+
# @return [Boolean]
|
|
41
58
|
def decorate?(response)
|
|
42
59
|
response.kind_of?(type.class)
|
|
43
60
|
end
|
|
44
61
|
|
|
62
|
+
# Delegates missing methods to {#type}
|
|
63
|
+
# @param [Symbol] meth
|
|
64
|
+
# @param [Array] args
|
|
65
|
+
# @param [#call, nil] block
|
|
45
66
|
def method_missing(meth, *args, &block)
|
|
46
67
|
if type.respond_to?(meth)
|
|
47
68
|
response = type.__send__(meth, *args, &block)
|
|
48
69
|
|
|
49
70
|
if decorate?(response)
|
|
50
|
-
|
|
71
|
+
__new__(response)
|
|
51
72
|
else
|
|
52
73
|
response
|
|
53
74
|
end
|
|
@@ -55,6 +76,11 @@ module Dry
|
|
|
55
76
|
super
|
|
56
77
|
end
|
|
57
78
|
end
|
|
79
|
+
|
|
80
|
+
# Replace underlying type
|
|
81
|
+
def __new__(type)
|
|
82
|
+
self.class.new(type, options)
|
|
83
|
+
end
|
|
58
84
|
end
|
|
59
85
|
end
|
|
60
86
|
end
|