dry-types 0.9.2 → 0.9.3
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/.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
|