dry-types 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -6
- data/.yardopts +5 -0
- data/CHANGELOG.md +9 -1
- data/Gemfile +4 -1
- data/Rakefile +4 -0
- data/dry-types.gemspec +2 -1
- data/lib/dry/types.rb +25 -0
- data/lib/dry/types/array.rb +2 -0
- data/lib/dry/types/array/member.rb +16 -2
- data/lib/dry/types/builder.rb +17 -2
- data/lib/dry/types/coercions.rb +14 -2
- data/lib/dry/types/coercions/form.rb +18 -0
- data/lib/dry/types/coercions/json.rb +2 -0
- data/lib/dry/types/constrained.rb +21 -0
- data/lib/dry/types/constrained/coercible.rb +5 -0
- data/lib/dry/types/constraints.rb +3 -0
- data/lib/dry/types/constructor.rb +28 -0
- data/lib/dry/types/decorator.rb +19 -0
- data/lib/dry/types/default.rb +14 -0
- data/lib/dry/types/definition.rb +31 -4
- data/lib/dry/types/enum.rb +10 -1
- data/lib/dry/types/errors.rb +14 -2
- data/lib/dry/types/extensions/maybe.rb +19 -2
- data/lib/dry/types/hash.rb +18 -0
- data/lib/dry/types/hash/schema.rb +69 -0
- data/lib/dry/types/options.rb +6 -0
- data/lib/dry/types/result.rb +10 -0
- data/lib/dry/types/safe.rb +9 -0
- data/lib/dry/types/sum.rb +20 -0
- data/lib/dry/types/version.rb +1 -1
- metadata +23 -2
@@ -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] block
|
7
|
+
# @yieldparam [Failure] failure
|
8
|
+
# @yieldreturn [Result]
|
9
|
+
# @return [Result]
|
5
10
|
def try(input, &block)
|
6
11
|
result = type.try(input)
|
7
12
|
|
@@ -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
|
@@ -5,36 +5,54 @@ module Dry
|
|
5
5
|
class Constructor < Definition
|
6
6
|
include Dry::Equalizer(:type)
|
7
7
|
|
8
|
+
# @return [#call]
|
8
9
|
attr_reader :fn
|
9
10
|
|
11
|
+
# @return [Definition]
|
10
12
|
attr_reader :type
|
11
13
|
|
14
|
+
# @param [Builder, Object] input
|
15
|
+
# @param [Hash] options
|
16
|
+
# @param [#call] block
|
12
17
|
def self.new(input, options = {}, &block)
|
13
18
|
type = input.is_a?(Builder) ? input : Definition.new(input)
|
14
19
|
super(type, options, &block)
|
15
20
|
end
|
16
21
|
|
22
|
+
# @param [Definition] type
|
23
|
+
# @param [Hash] options
|
24
|
+
# @param [#proc] block
|
17
25
|
def initialize(type, options = {}, &block)
|
18
26
|
@type = type
|
19
27
|
@fn = options.fetch(:fn, block)
|
20
28
|
super
|
21
29
|
end
|
22
30
|
|
31
|
+
# @return [Class]
|
23
32
|
def primitive
|
24
33
|
type.primitive
|
25
34
|
end
|
26
35
|
|
36
|
+
# @param [Object] input
|
37
|
+
# @return [Object]
|
27
38
|
def call(input)
|
28
39
|
type[fn[input]]
|
29
40
|
end
|
30
41
|
alias_method :[], :call
|
31
42
|
|
43
|
+
# @param [Object] input
|
44
|
+
# @param [#call] block
|
45
|
+
# @return [Result]
|
32
46
|
def try(input, &block)
|
33
47
|
type.try(fn[input], &block)
|
34
48
|
rescue TypeError => e
|
35
49
|
failure(input, e.message)
|
36
50
|
end
|
37
51
|
|
52
|
+
# @param [#call, nil] new_fn
|
53
|
+
# @param [Hash] options
|
54
|
+
# @param [#call] block
|
55
|
+
# @return [Constructor]
|
38
56
|
def constructor(new_fn = nil, **options, &block)
|
39
57
|
left = new_fn || block
|
40
58
|
right = fn
|
@@ -42,20 +60,30 @@ module Dry
|
|
42
60
|
with(options.merge(fn: -> input { left[right[input]] }))
|
43
61
|
end
|
44
62
|
|
63
|
+
# @param [Object] value
|
64
|
+
# @return [Boolean]
|
45
65
|
def valid?(value)
|
46
66
|
super && type.valid?(value)
|
47
67
|
end
|
48
68
|
|
69
|
+
# @return [Class]
|
49
70
|
def constrained_type
|
50
71
|
Constrained::Coercible
|
51
72
|
end
|
52
73
|
|
53
74
|
private
|
54
75
|
|
76
|
+
# @param [Symbol] meth
|
77
|
+
# @param [Boolean] include_private
|
78
|
+
# @return [Boolean]
|
55
79
|
def respond_to_missing?(meth, include_private = false)
|
56
80
|
super || type.respond_to?(meth)
|
57
81
|
end
|
58
82
|
|
83
|
+
# Delegates missing methods to {#type}
|
84
|
+
# @param [Symbol] meth
|
85
|
+
# @param [Array] args
|
86
|
+
# @param [#call] block
|
59
87
|
def method_missing(meth, *args, &block)
|
60
88
|
if type.respond_to?(meth)
|
61
89
|
response = type.__send__(meth, *args, &block)
|
data/lib/dry/types/decorator.rb
CHANGED
@@ -5,43 +5,62 @@ module Dry
|
|
5
5
|
module Decorator
|
6
6
|
include Options
|
7
7
|
|
8
|
+
# @return [Definition]
|
8
9
|
attr_reader :type
|
9
10
|
|
11
|
+
# @param [Definition] type
|
10
12
|
def initialize(type, *)
|
11
13
|
super
|
12
14
|
@type = type
|
13
15
|
end
|
14
16
|
|
17
|
+
# @return [Constructor]
|
15
18
|
def constructor
|
16
19
|
type.constructor
|
17
20
|
end
|
18
21
|
|
22
|
+
# @param [Object] input
|
23
|
+
# @param [#call] block
|
24
|
+
# @return [Result]
|
19
25
|
def try(input, &block)
|
20
26
|
type.try(input, &block)
|
21
27
|
end
|
22
28
|
|
29
|
+
# @param [Object] value
|
30
|
+
# @return [Boolean]
|
23
31
|
def valid?(value)
|
24
32
|
type.valid?(value)
|
25
33
|
end
|
26
34
|
|
35
|
+
# @return [Boolean]
|
27
36
|
def default?
|
28
37
|
type.default?
|
29
38
|
end
|
30
39
|
|
40
|
+
# @return [Boolean]
|
31
41
|
def constrained?
|
32
42
|
type.constrained?
|
33
43
|
end
|
34
44
|
|
45
|
+
# @param [Symbol] meth
|
46
|
+
# @param [Boolean] include_private
|
47
|
+
# @return [Boolean]
|
35
48
|
def respond_to_missing?(meth, include_private = false)
|
36
49
|
super || type.respond_to?(meth)
|
37
50
|
end
|
38
51
|
|
39
52
|
private
|
40
53
|
|
54
|
+
# @param [Object] response
|
55
|
+
# @return [Boolean]
|
41
56
|
def decorate?(response)
|
42
57
|
response.kind_of?(type.class)
|
43
58
|
end
|
44
59
|
|
60
|
+
# Delegates missing methods to {#type}
|
61
|
+
# @param [Symbol] meth
|
62
|
+
# @param [Array] args
|
63
|
+
# @param [#call] block
|
45
64
|
def method_missing(meth, *args, &block)
|
46
65
|
if type.respond_to?(meth)
|
47
66
|
response = type.__send__(meth, *args, &block)
|
data/lib/dry/types/default.rb
CHANGED
@@ -10,15 +10,20 @@ module Dry
|
|
10
10
|
class Callable < Default
|
11
11
|
include Dry::Equalizer(:type, :options)
|
12
12
|
|
13
|
+
# Evaluates given callable
|
14
|
+
# @return [Object]
|
13
15
|
def evaluate
|
14
16
|
value.call
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
20
|
+
# @return [Object]
|
18
21
|
attr_reader :value
|
19
22
|
|
20
23
|
alias_method :evaluate, :value
|
21
24
|
|
25
|
+
# @param [Object, #call] value
|
26
|
+
# @return [Default, Dry::Types::Default::Callable]
|
22
27
|
def self.[](value)
|
23
28
|
if value.respond_to?(:call)
|
24
29
|
Callable
|
@@ -27,23 +32,32 @@ module Dry
|
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
35
|
+
# @param [Definition] type
|
36
|
+
# @param [Object] value
|
30
37
|
def initialize(type, value, *)
|
31
38
|
super
|
32
39
|
@value = value
|
33
40
|
end
|
34
41
|
|
42
|
+
# @param [Array] args see {Dry::Types::Builder#constrained}
|
43
|
+
# @return [Default]
|
35
44
|
def constrained(*args)
|
36
45
|
type.constrained(*args).default(value)
|
37
46
|
end
|
38
47
|
|
48
|
+
# @return [true]
|
39
49
|
def default?
|
40
50
|
true
|
41
51
|
end
|
42
52
|
|
53
|
+
# @param [Object] input
|
54
|
+
# @return [Success]
|
43
55
|
def try(input)
|
44
56
|
success(call(input))
|
45
57
|
end
|
46
58
|
|
59
|
+
# @param [Object] input
|
60
|
+
# @return [Object] value passed through {#type} or {#default} value
|
47
61
|
def call(input)
|
48
62
|
if input.nil?
|
49
63
|
evaluate
|
data/lib/dry/types/definition.rb
CHANGED
@@ -9,10 +9,14 @@ module Dry
|
|
9
9
|
include Options
|
10
10
|
include Builder
|
11
11
|
|
12
|
+
# @return [Hash]
|
12
13
|
attr_reader :options
|
13
14
|
|
15
|
+
# @return [Class]
|
14
16
|
attr_reader :primitive
|
15
17
|
|
18
|
+
# @param [Class] primitive
|
19
|
+
# @return [Definition]
|
16
20
|
def self.[](primitive)
|
17
21
|
if primitive == ::Array
|
18
22
|
Types::Array
|
@@ -23,29 +27,41 @@ module Dry
|
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
30
|
+
# @param [Class] primitive
|
31
|
+
# @param [Hash] options
|
26
32
|
def initialize(primitive, options = {})
|
27
33
|
super
|
28
34
|
@primitive = primitive
|
29
35
|
freeze
|
30
36
|
end
|
31
37
|
|
38
|
+
# @return [String]
|
32
39
|
def name
|
33
40
|
primitive.name
|
34
41
|
end
|
35
42
|
|
43
|
+
# @return [false]
|
36
44
|
def default?
|
37
45
|
false
|
38
46
|
end
|
39
47
|
|
48
|
+
# @return [false]
|
40
49
|
def constrained?
|
41
50
|
false
|
42
51
|
end
|
43
52
|
|
53
|
+
# @param [Object] input
|
54
|
+
# @return [Object]
|
44
55
|
def call(input)
|
45
56
|
input
|
46
57
|
end
|
47
58
|
alias_method :[], :call
|
48
59
|
|
60
|
+
# @param [Object] input
|
61
|
+
# @param [#call] block
|
62
|
+
# @yieldparam [Failure] failure
|
63
|
+
# @yieldreturn [Result]
|
64
|
+
# @return [Result]
|
49
65
|
def try(input, &block)
|
50
66
|
if valid?(input)
|
51
67
|
success(input)
|
@@ -55,18 +71,29 @@ module Dry
|
|
55
71
|
end
|
56
72
|
end
|
57
73
|
|
58
|
-
|
59
|
-
|
74
|
+
# @param (see Dry::Types::Success#initialize)
|
75
|
+
# @return [Success]
|
76
|
+
def success(input)
|
77
|
+
Result::Success.new(input)
|
60
78
|
end
|
61
79
|
|
62
|
-
|
63
|
-
|
80
|
+
|
81
|
+
# @param (see Failure#initialize)
|
82
|
+
# @return [Failure]
|
83
|
+
def failure(input, error)
|
84
|
+
Result::Failure.new(input, error)
|
64
85
|
end
|
65
86
|
|
87
|
+
# @param [Object] klass class of the result instance
|
88
|
+
# @param [Array] args arguments for the +klass#initialize+ method
|
89
|
+
# @return [Object] new instance of the given +klass+
|
66
90
|
def result(klass, *args)
|
67
91
|
klass.new(*args)
|
68
92
|
end
|
69
93
|
|
94
|
+
# Checks whether value is of a #primitive class
|
95
|
+
# @param [Object] value
|
96
|
+
# @return [Boolean]
|
70
97
|
def primitive?(value)
|
71
98
|
value.is_a?(primitive)
|
72
99
|
end
|
data/lib/dry/types/enum.rb
CHANGED
@@ -6,8 +6,15 @@ module Dry
|
|
6
6
|
include Dry::Equalizer(:type, :options, :values)
|
7
7
|
include Decorator
|
8
8
|
|
9
|
-
|
9
|
+
# @return [Array]
|
10
|
+
attr_reader :values
|
10
11
|
|
12
|
+
# @return [Hash]
|
13
|
+
attr_reader :mapping
|
14
|
+
|
15
|
+
# @param [Definition] type
|
16
|
+
# @param [Hash] options
|
17
|
+
# @option options [Array] :values
|
11
18
|
def initialize(type, options)
|
12
19
|
super
|
13
20
|
@values = options.fetch(:values).freeze
|
@@ -15,6 +22,8 @@ module Dry
|
|
15
22
|
@mapping = values.each_with_object({}) { |v, h| h[values.index(v)] = v }.freeze
|
16
23
|
end
|
17
24
|
|
25
|
+
# @param [Object] input
|
26
|
+
# @return [Object]
|
18
27
|
def call(input)
|
19
28
|
value =
|
20
29
|
if values.include?(input)
|
data/lib/dry/types/errors.rb
CHANGED
@@ -2,9 +2,13 @@ module Dry
|
|
2
2
|
module Types
|
3
3
|
extend Dry::Configurable
|
4
4
|
|
5
|
+
# @!attribute [r] namespace
|
6
|
+
# @return [Container{String => Definition}]
|
5
7
|
setting :namespace, self
|
6
8
|
|
7
9
|
class SchemaError < TypeError
|
10
|
+
# @param [String] key
|
11
|
+
# @param [Object] value
|
8
12
|
def initialize(key, value)
|
9
13
|
super("#{value.inspect} (#{value.class}) has invalid type for :#{key}")
|
10
14
|
end
|
@@ -14,20 +18,27 @@ module Dry
|
|
14
18
|
private_constant(:SchemaKeyError)
|
15
19
|
|
16
20
|
class MissingKeyError < SchemaKeyError
|
21
|
+
# @param [String] key
|
17
22
|
def initialize(key)
|
18
23
|
super(":#{key} is missing in Hash input")
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
22
27
|
class UnknownKeysError < SchemaKeyError
|
28
|
+
# @param [<String, Symbol>] keys
|
23
29
|
def initialize(*keys)
|
24
30
|
super("unexpected keys #{keys.inspect} in Hash input")
|
25
31
|
end
|
26
32
|
end
|
27
33
|
|
28
|
-
ConstraintError
|
29
|
-
|
34
|
+
class ConstraintError < TypeError
|
35
|
+
# @return [String, #to_s]
|
36
|
+
attr_reader :result
|
37
|
+
# @return [Object]
|
38
|
+
attr_reader :input
|
30
39
|
|
40
|
+
# @param [String, #to_s] result
|
41
|
+
# @param [Object] input
|
31
42
|
def initialize(result, input)
|
32
43
|
@result = result
|
33
44
|
@input = input
|
@@ -39,6 +50,7 @@ module Dry
|
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
53
|
+
# @return [String]
|
42
54
|
def to_s
|
43
55
|
"#{input.inspect} violates constraints (#{result} failed)"
|
44
56
|
end
|
@@ -9,19 +9,27 @@ module Dry
|
|
9
9
|
include Builder
|
10
10
|
include Dry::Monads::Maybe::Mixin
|
11
11
|
|
12
|
+
# @param [Dry::Monads::Maybe, Object] input
|
13
|
+
# @return [Dry::Monads::Maybe]
|
12
14
|
def call(input)
|
13
15
|
input.is_a?(Dry::Monads::Maybe) ? input : Maybe(type[input])
|
14
16
|
end
|
15
17
|
alias_method :[], :call
|
16
18
|
|
19
|
+
# @param [Object] input
|
20
|
+
# @return [Result::Success]
|
17
21
|
def try(input)
|
18
22
|
Result::Success.new(Maybe(type[input]))
|
19
23
|
end
|
20
24
|
|
25
|
+
# @return [true]
|
21
26
|
def maybe?
|
22
27
|
true
|
23
28
|
end
|
24
29
|
|
30
|
+
# @param [Object] value
|
31
|
+
# @see Dry::Types::Builder#default
|
32
|
+
# @raise [ArgumentError] if nil provided as default value
|
25
33
|
def default(value)
|
26
34
|
if value.nil?
|
27
35
|
raise ArgumentError, "nil cannot be used as a default of a maybe type"
|
@@ -32,6 +40,7 @@ module Dry
|
|
32
40
|
end
|
33
41
|
|
34
42
|
module Builder
|
43
|
+
# @return [Maybe]
|
35
44
|
def maybe
|
36
45
|
Maybe.new(Types['strict.nil'] | self)
|
37
46
|
end
|
@@ -39,6 +48,9 @@ module Dry
|
|
39
48
|
|
40
49
|
class Hash
|
41
50
|
module MaybeTypes
|
51
|
+
# @param [Hash] result
|
52
|
+
# @param [Symbol] key
|
53
|
+
# @param [Definition] type
|
42
54
|
def resolve_missing_value(result, key, type)
|
43
55
|
if type.respond_to?(:maybe?) && type.maybe?
|
44
56
|
result[key] = type[nil]
|
@@ -48,8 +60,13 @@ module Dry
|
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
51
|
-
StrictWithDefaults
|
52
|
-
|
63
|
+
class StrictWithDefaults < Strict
|
64
|
+
include MaybeTypes
|
65
|
+
end
|
66
|
+
|
67
|
+
class Schema < Hash
|
68
|
+
include MaybeTypes
|
69
|
+
end
|
53
70
|
end
|
54
71
|
|
55
72
|
# Register non-coercible maybe types
|