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
data/lib/dry/types/hash.rb
CHANGED
@@ -3,6 +3,11 @@ require 'dry/types/hash/schema'
|
|
3
3
|
module Dry
|
4
4
|
module Types
|
5
5
|
class Hash < Definition
|
6
|
+
# @param [{Symbol => Definition}] type_map
|
7
|
+
# @param [Class] klass
|
8
|
+
# {Schema} or one of its subclasses ({Weak}, {Permissive}, {Strict},
|
9
|
+
# {StrictWithDefaults}, {Symbolized})
|
10
|
+
# @return [Schema]
|
6
11
|
def schema(type_map, klass = Schema)
|
7
12
|
member_types = type_map.each_with_object({}) { |(name, type), result|
|
8
13
|
result[name] =
|
@@ -15,28 +20,41 @@ module Dry
|
|
15
20
|
klass.new(primitive, options.merge(member_types: member_types))
|
16
21
|
end
|
17
22
|
|
23
|
+
# @param [{Symbol => Definition}] type_map
|
24
|
+
# @return [Weak]
|
18
25
|
def weak(type_map)
|
19
26
|
schema(type_map, Weak)
|
20
27
|
end
|
21
28
|
|
29
|
+
# @param [{Symbol => Definition}] type_map
|
30
|
+
# @return [Permissive]
|
22
31
|
def permissive(type_map)
|
23
32
|
schema(type_map, Permissive)
|
24
33
|
end
|
25
34
|
|
35
|
+
# @param [{Symbol => Definition}] type_map
|
36
|
+
# @return [Strict]
|
26
37
|
def strict(type_map)
|
27
38
|
schema(type_map, Strict)
|
28
39
|
end
|
29
40
|
|
41
|
+
# @param [{Symbol => Definition}] type_map
|
42
|
+
# @return [StrictWithDefaults]
|
30
43
|
def strict_with_defaults(type_map)
|
31
44
|
schema(type_map, StrictWithDefaults)
|
32
45
|
end
|
33
46
|
|
47
|
+
# @param [{Symbol => Definition}] type_map
|
48
|
+
# @return [Symbolized]
|
34
49
|
def symbolized(type_map)
|
35
50
|
schema(type_map, Symbolized)
|
36
51
|
end
|
37
52
|
|
38
53
|
private
|
39
54
|
|
55
|
+
# @param [Hash] _result
|
56
|
+
# @param [Symbol] _key
|
57
|
+
# @param [Definition] _type
|
40
58
|
def resolve_missing_value(_result, _key, _type)
|
41
59
|
# noop
|
42
60
|
end
|
@@ -1,19 +1,38 @@
|
|
1
1
|
module Dry
|
2
2
|
module Types
|
3
3
|
class Hash < Definition
|
4
|
+
# The built-in Hash type has constructors that you can use to define
|
5
|
+
# hashes with explicit schemas and coercible values using the built-in types.
|
6
|
+
#
|
7
|
+
# Basic {Schema} evaluates default values for keys missing in input hash
|
8
|
+
# (see {Schema#resolve_missing_value})
|
9
|
+
#
|
10
|
+
# @see Dry::Types::Default#evaluate
|
11
|
+
# @see Dry::Types::Default::Callable#evaluate
|
4
12
|
class Schema < Hash
|
13
|
+
# @return [Hash{Symbol => Definition}]
|
5
14
|
attr_reader :member_types
|
6
15
|
|
16
|
+
# @param [Class] _primitive
|
17
|
+
# @param [Hash] options
|
18
|
+
# @option options [Hash{Symbol => Definition}] :member_types
|
7
19
|
def initialize(_primitive, options)
|
8
20
|
@member_types = options.fetch(:member_types)
|
9
21
|
super
|
10
22
|
end
|
11
23
|
|
24
|
+
# @param [Hash] hash
|
25
|
+
# @return [Hash{Symbol => Object}]
|
12
26
|
def call(hash)
|
13
27
|
coerce(hash)
|
14
28
|
end
|
15
29
|
alias_method :[], :call
|
16
30
|
|
31
|
+
# @param [Hash] hash
|
32
|
+
# @param [#call] block
|
33
|
+
# @yieldparam [Failure] failure
|
34
|
+
# @yieldreturn [Result]
|
35
|
+
# @return [Result]
|
17
36
|
def try(hash, &block)
|
18
37
|
success = true
|
19
38
|
output = {}
|
@@ -40,12 +59,16 @@ module Dry
|
|
40
59
|
|
41
60
|
private
|
42
61
|
|
62
|
+
# @param [Hash] hash
|
63
|
+
# @return [Hash{Symbol => Object}]
|
43
64
|
def try_coerce(hash)
|
44
65
|
resolve(hash) do |type, key, value|
|
45
66
|
yield(key, type.try(value))
|
46
67
|
end
|
47
68
|
end
|
48
69
|
|
70
|
+
# @param [Hash] hash
|
71
|
+
# @return [Hash{Symbol => Object}]
|
49
72
|
def coerce(hash)
|
50
73
|
resolve(hash) do |type, key, value|
|
51
74
|
begin
|
@@ -56,6 +79,8 @@ module Dry
|
|
56
79
|
end
|
57
80
|
end
|
58
81
|
|
82
|
+
# @param [Hash] hash
|
83
|
+
# @return [Hash{Symbol => Object}]
|
59
84
|
def resolve(hash)
|
60
85
|
result = {}
|
61
86
|
member_types.each do |key, type|
|
@@ -68,6 +93,12 @@ module Dry
|
|
68
93
|
result
|
69
94
|
end
|
70
95
|
|
96
|
+
# @param [Hash] result
|
97
|
+
# @param [Symbol] key
|
98
|
+
# @param [Definition] type
|
99
|
+
# @return [Object]
|
100
|
+
# @see Dry::Types::Default#evaluate
|
101
|
+
# @see Dry::Types::Default::Callable#evaluate
|
71
102
|
def resolve_missing_value(result, key, type)
|
72
103
|
if type.default?
|
73
104
|
result[key] = type.evaluate
|
@@ -77,17 +108,33 @@ module Dry
|
|
77
108
|
end
|
78
109
|
end
|
79
110
|
|
111
|
+
# Permissive schema raises a {MissingKeyError} if the given key is missing
|
112
|
+
# in provided hash.
|
80
113
|
class Permissive < Schema
|
81
114
|
private
|
82
115
|
|
116
|
+
# @param [Symbol] key
|
117
|
+
# @raise [MissingKeyError] when key is missing in given input
|
83
118
|
def resolve_missing_value(_, key, _)
|
84
119
|
raise MissingKeyError, key
|
85
120
|
end
|
86
121
|
end
|
87
122
|
|
123
|
+
# Strict hash will raise errors when keys are missing or value types are incorrect.
|
124
|
+
# Strict schema raises a {UnknownKeysError} if there are any unexpected
|
125
|
+
# keys in given hash, and raises a {MissingKeyError} if any key is missing
|
126
|
+
# in it.
|
127
|
+
# @example
|
128
|
+
# hash = Types::Hash.strict(name: Types::String, age: Types::Coercible::Int)
|
129
|
+
# hash[email: 'jane@doe.org', name: 'Jane', age: 21]
|
130
|
+
# # => Dry::Types::SchemaKeyError: :email is missing in Hash input
|
88
131
|
class Strict < Permissive
|
89
132
|
private
|
90
133
|
|
134
|
+
# @param [Hash] hash
|
135
|
+
# @return [Hash{Symbol => Object}]
|
136
|
+
# @raise [UnknownKeysError]
|
137
|
+
# if there any unexpected key in given hash
|
91
138
|
def resolve(hash)
|
92
139
|
unexpected = hash.keys - member_types.keys
|
93
140
|
raise UnknownKeysError.new(*unexpected) unless unexpected.empty?
|
@@ -100,9 +147,20 @@ module Dry
|
|
100
147
|
end
|
101
148
|
end
|
102
149
|
|
150
|
+
# {StrictWithDefaults} checks that there are no extra keys
|
151
|
+
# (raises {UnknownKeysError} otherwise) and there a no missing keys
|
152
|
+
# without default values given (raises {MissingKeyError} otherwise).
|
153
|
+
# @see Default#evaluate
|
154
|
+
# @see Default::Callable#evaluate
|
103
155
|
class StrictWithDefaults < Strict
|
104
156
|
private
|
105
157
|
|
158
|
+
# @param [Hash] result
|
159
|
+
# @param [Symbol] key
|
160
|
+
# @param [Definition] type
|
161
|
+
# @return [Object]
|
162
|
+
# @see Dry::Types::Default#evaluate
|
163
|
+
# @see Dry::Types::Default::Callable#evaluate
|
106
164
|
def resolve_missing_value(result, key, type)
|
107
165
|
if type.default?
|
108
166
|
result[key] = type.evaluate
|
@@ -112,7 +170,12 @@ module Dry
|
|
112
170
|
end
|
113
171
|
end
|
114
172
|
|
173
|
+
# Weak schema provides safe types for every type given in schema hash
|
174
|
+
# @see Safe
|
115
175
|
class Weak < Schema
|
176
|
+
# @param [Class] primitive
|
177
|
+
# @param [Hash] options
|
178
|
+
# @see #initialize
|
116
179
|
def self.new(primitive, options)
|
117
180
|
member_types = options.
|
118
181
|
fetch(:member_types).
|
@@ -121,6 +184,11 @@ module Dry
|
|
121
184
|
super(primitive, options.merge(member_types: member_types))
|
122
185
|
end
|
123
186
|
|
187
|
+
# @param [Hash] hash
|
188
|
+
# @param [#call] block
|
189
|
+
# @yieldparam [Failure] failure
|
190
|
+
# @yieldreturn [Result]
|
191
|
+
# @return [Result]
|
124
192
|
def try(hash, &block)
|
125
193
|
if hash.is_a?(::Hash)
|
126
194
|
super
|
@@ -131,6 +199,7 @@ module Dry
|
|
131
199
|
end
|
132
200
|
end
|
133
201
|
|
202
|
+
# {Symbolized} hash will turn string key names into symbols.
|
134
203
|
class Symbolized < Weak
|
135
204
|
private
|
136
205
|
|
data/lib/dry/types/options.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
module Dry
|
2
2
|
module Types
|
3
3
|
module Options
|
4
|
+
# @return [Hash]
|
4
5
|
attr_reader :options
|
5
6
|
|
7
|
+
# @see Definition#initialize
|
6
8
|
def initialize(*args, **options)
|
7
9
|
@__args__ = args
|
8
10
|
@options = options
|
9
11
|
@meta = options.fetch(:meta, {})
|
10
12
|
end
|
11
13
|
|
14
|
+
# @param [Hash] new_options
|
15
|
+
# @return [Definition]
|
12
16
|
def with(new_options)
|
13
17
|
self.class.new(*@__args__, options.merge(new_options))
|
14
18
|
end
|
15
19
|
|
20
|
+
# @param [Hash] data
|
21
|
+
# @return [Hash, Definition]
|
16
22
|
def meta(data = nil)
|
17
23
|
data ? with(meta: @meta.merge(data)) : @meta
|
18
24
|
end
|
data/lib/dry/types/result.rb
CHANGED
@@ -5,17 +5,21 @@ module Dry
|
|
5
5
|
class Result
|
6
6
|
include Dry::Equalizer(:input)
|
7
7
|
|
8
|
+
# @return [Object]
|
8
9
|
attr_reader :input
|
9
10
|
|
11
|
+
# @param [Object] input
|
10
12
|
def initialize(input)
|
11
13
|
@input = input
|
12
14
|
end
|
13
15
|
|
14
16
|
class Success < Result
|
17
|
+
# @return [true]
|
15
18
|
def success?
|
16
19
|
true
|
17
20
|
end
|
18
21
|
|
22
|
+
# @return [false]
|
19
23
|
def failure?
|
20
24
|
false
|
21
25
|
end
|
@@ -24,21 +28,27 @@ module Dry
|
|
24
28
|
class Failure < Result
|
25
29
|
include Dry::Equalizer(:input, :error)
|
26
30
|
|
31
|
+
# @return [#to_s]
|
27
32
|
attr_reader :error
|
28
33
|
|
34
|
+
# @param [Object] input
|
35
|
+
# @param [#to_s] error
|
29
36
|
def initialize(input, error)
|
30
37
|
super(input)
|
31
38
|
@error = error
|
32
39
|
end
|
33
40
|
|
41
|
+
# @return [String]
|
34
42
|
def to_s
|
35
43
|
error.to_s
|
36
44
|
end
|
37
45
|
|
46
|
+
# @return [false]
|
38
47
|
def success?
|
39
48
|
false
|
40
49
|
end
|
41
50
|
|
51
|
+
# @return [true]
|
42
52
|
def failure?
|
43
53
|
true
|
44
54
|
end
|
data/lib/dry/types/safe.rb
CHANGED
@@ -7,6 +7,8 @@ module Dry
|
|
7
7
|
include Decorator
|
8
8
|
include Builder
|
9
9
|
|
10
|
+
# @param [Object] input
|
11
|
+
# @return [Object]
|
10
12
|
def call(input)
|
11
13
|
result = try(input)
|
12
14
|
|
@@ -18,6 +20,11 @@ module Dry
|
|
18
20
|
end
|
19
21
|
alias_method :[], :call
|
20
22
|
|
23
|
+
# @param [Object] input
|
24
|
+
# @param [#call] block
|
25
|
+
# @yieldparam [Failure] failure
|
26
|
+
# @yieldreturn [Result]
|
27
|
+
# @return [Result]
|
21
28
|
def try(input, &block)
|
22
29
|
type.try(input, &block)
|
23
30
|
rescue TypeError, ArgumentError => e
|
@@ -27,6 +34,8 @@ module Dry
|
|
27
34
|
|
28
35
|
private
|
29
36
|
|
37
|
+
# @param [Object, Dry::Types::Constructor] response
|
38
|
+
# @return [Boolean]
|
30
39
|
def decorate?(response)
|
31
40
|
super || response.kind_of?(Constructor)
|
32
41
|
end
|
data/lib/dry/types/sum.rb
CHANGED
@@ -7,19 +7,26 @@ module Dry
|
|
7
7
|
include Builder
|
8
8
|
include Options
|
9
9
|
|
10
|
+
# @return [Definition]
|
10
11
|
attr_reader :left
|
11
12
|
|
13
|
+
# @return [Definition]
|
12
14
|
attr_reader :right
|
13
15
|
|
14
16
|
class Constrained < Sum
|
17
|
+
# @return [Dry::Logic::Rule]
|
15
18
|
def rule
|
16
19
|
left.rule | right.rule
|
17
20
|
end
|
18
21
|
|
22
|
+
# @return [true]
|
19
23
|
def constrained?
|
20
24
|
true
|
21
25
|
end
|
22
26
|
|
27
|
+
# @param [Object] input
|
28
|
+
# @return [Object]
|
29
|
+
# @raise [ConstraintError] if given +input+ not passing {#try}
|
23
30
|
def call(input)
|
24
31
|
try(input) do |result|
|
25
32
|
raise ConstraintError.new(result, input)
|
@@ -28,28 +35,37 @@ module Dry
|
|
28
35
|
alias_method :[], :call
|
29
36
|
end
|
30
37
|
|
38
|
+
# @param [Definition] left
|
39
|
+
# @param [Definition] right
|
40
|
+
# @param [Hash] options
|
31
41
|
def initialize(left, right, options = {})
|
32
42
|
super
|
33
43
|
@left, @right = left, right
|
34
44
|
freeze
|
35
45
|
end
|
36
46
|
|
47
|
+
# @return [String]
|
37
48
|
def name
|
38
49
|
[left, right].map(&:name).join(' | ')
|
39
50
|
end
|
40
51
|
|
52
|
+
# @return [false]
|
41
53
|
def default?
|
42
54
|
false
|
43
55
|
end
|
44
56
|
|
57
|
+
# @return [false]
|
45
58
|
def maybe?
|
46
59
|
false
|
47
60
|
end
|
48
61
|
|
62
|
+
# @return [false]
|
49
63
|
def constrained?
|
50
64
|
false
|
51
65
|
end
|
52
66
|
|
67
|
+
# @param [Object] input
|
68
|
+
# @return [Object]
|
53
69
|
def call(input)
|
54
70
|
try(input).input
|
55
71
|
end
|
@@ -69,10 +85,14 @@ module Dry
|
|
69
85
|
end
|
70
86
|
end
|
71
87
|
|
88
|
+
# @param [Object] value
|
89
|
+
# @return [Boolean]
|
72
90
|
def primitive?(value)
|
73
91
|
left.primitive?(value) || right.primitive?(value)
|
74
92
|
end
|
75
93
|
|
94
|
+
# @param [Object] value
|
95
|
+
# @return [Boolean]
|
76
96
|
def valid?(value)
|
77
97
|
left.valid?(value) || right.valid?(value)
|
78
98
|
end
|
data/lib/dry/types/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-types
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -31,6 +31,9 @@ dependencies:
|
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0.2'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 0.2.1
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -38,6 +41,9 @@ dependencies:
|
|
38
41
|
- - "~>"
|
39
42
|
- !ruby/object:Gem::Version
|
40
43
|
version: '0.2'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.2.1
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: dry-container
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -176,6 +182,20 @@ dependencies:
|
|
176
182
|
- - "~>"
|
177
183
|
- !ruby/object:Gem::Version
|
178
184
|
version: '0.2'
|
185
|
+
- !ruby/object:Gem::Dependency
|
186
|
+
name: yard
|
187
|
+
requirement: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - "~>"
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: 0.9.5
|
192
|
+
type: :development
|
193
|
+
prerelease: false
|
194
|
+
version_requirements: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - "~>"
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: 0.9.5
|
179
199
|
description: Type system for Ruby supporting coercions, constraints and complex types
|
180
200
|
like structs, value objects, enums etc.
|
181
201
|
email:
|
@@ -187,6 +207,7 @@ files:
|
|
187
207
|
- ".gitignore"
|
188
208
|
- ".rspec"
|
189
209
|
- ".travis.yml"
|
210
|
+
- ".yardopts"
|
190
211
|
- CHANGELOG.md
|
191
212
|
- Gemfile
|
192
213
|
- LICENSE
|