dry-types 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +15 -0
- data/.rubocop.yml +43 -0
- data/.travis.yml +2 -3
- data/CHANGELOG.md +119 -1
- data/Gemfile +2 -2
- data/benchmarks/hash_schemas.rb +5 -5
- data/dry-types.gemspec +2 -2
- 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 +33 -12
- 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 +22 -12
- 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/spec/types.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
RSpec.shared_examples_for 'Dry::Types::
|
1
|
+
RSpec.shared_examples_for 'Dry::Types::Nominal without primitive' do
|
2
2
|
def be_boolean
|
3
3
|
satisfy { |x| x == true || x == false }
|
4
4
|
end
|
@@ -38,9 +38,17 @@ RSpec.shared_examples_for 'Dry::Types::Definition without primitive' do
|
|
38
38
|
expect(type.optional?).to be_boolean
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
describe '#to_s' do
|
43
|
+
it 'returns a custom string representation' do
|
44
|
+
if type.class.name.start_with?('Dry::Types')
|
45
|
+
expect(type.to_s).to start_with('#<Dry::Types')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
41
49
|
end
|
42
50
|
|
43
|
-
RSpec.shared_examples_for 'Dry::Types::
|
51
|
+
RSpec.shared_examples_for 'Dry::Types::Nominal#meta' do
|
44
52
|
describe '#meta' do
|
45
53
|
it 'allows setting meta information' do
|
46
54
|
with_meta = type.meta(foo: :bar).meta(baz: '1')
|
@@ -75,8 +83,8 @@ RSpec.shared_examples_for 'Dry::Types::Definition#meta' do
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
|
78
|
-
RSpec.shared_examples_for Dry::Types::
|
79
|
-
it_behaves_like 'Dry::Types::
|
86
|
+
RSpec.shared_examples_for Dry::Types::Nominal do
|
87
|
+
it_behaves_like 'Dry::Types::Nominal without primitive'
|
80
88
|
|
81
89
|
describe '#primitive' do
|
82
90
|
it 'returns a class' do
|
data/lib/dry/types/sum.rb
CHANGED
@@ -4,9 +4,10 @@ module Dry
|
|
4
4
|
module Types
|
5
5
|
class Sum
|
6
6
|
include Type
|
7
|
-
include Dry::Equalizer(:left, :right, :options, :meta)
|
8
7
|
include Builder
|
9
8
|
include Options
|
9
|
+
include Printable
|
10
|
+
include Dry::Equalizer(:left, :right, :options, :meta, inspect: false)
|
10
11
|
|
11
12
|
# @return [Type]
|
12
13
|
attr_reader :left
|
@@ -29,9 +30,9 @@ module Dry
|
|
29
30
|
# @return [Object]
|
30
31
|
# @raise [ConstraintError] if given +input+ not passing {#try}
|
31
32
|
def call(input)
|
32
|
-
try(input)
|
33
|
+
try(input) { |result|
|
33
34
|
raise ConstraintError.new(result, input)
|
34
|
-
|
35
|
+
}.input
|
35
36
|
end
|
36
37
|
alias_method :[], :call
|
37
38
|
end
|
@@ -62,7 +63,7 @@ module Dry
|
|
62
63
|
|
63
64
|
# @return [Boolean]
|
64
65
|
def optional?
|
65
|
-
|
66
|
+
primitive?(nil)
|
66
67
|
end
|
67
68
|
|
68
69
|
# @param [Object] input
|
@@ -73,16 +74,14 @@ module Dry
|
|
73
74
|
alias_method :[], :call
|
74
75
|
|
75
76
|
def try(input, &block)
|
76
|
-
|
77
|
-
right.try(input)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
else
|
85
|
-
result
|
77
|
+
left.try(input) do
|
78
|
+
right.try(input) do |failure|
|
79
|
+
if block_given?
|
80
|
+
yield(failure)
|
81
|
+
else
|
82
|
+
failure
|
83
|
+
end
|
84
|
+
end
|
86
85
|
end
|
87
86
|
end
|
88
87
|
|
@@ -119,7 +118,7 @@ module Dry
|
|
119
118
|
|
120
119
|
# @api public
|
121
120
|
#
|
122
|
-
# @see
|
121
|
+
# @see Nominal#to_ast
|
123
122
|
def to_ast(meta: true)
|
124
123
|
[:sum, [left.to_ast(meta: meta), right.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
|
125
124
|
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.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -85,6 +85,9 @@ dependencies:
|
|
85
85
|
- - "~>"
|
86
86
|
- !ruby/object:Gem::Version
|
87
87
|
version: '0.2'
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.2.2
|
88
91
|
type: :runtime
|
89
92
|
prerelease: false
|
90
93
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -92,6 +95,9 @@ dependencies:
|
|
92
95
|
- - "~>"
|
93
96
|
- !ruby/object:Gem::Version
|
94
97
|
version: '0.2'
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 0.2.2
|
95
101
|
- !ruby/object:Gem::Dependency
|
96
102
|
name: dry-logic
|
97
103
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,16 +122,16 @@ dependencies:
|
|
116
122
|
name: bundler
|
117
123
|
requirement: !ruby/object:Gem::Requirement
|
118
124
|
requirements:
|
119
|
-
- - "
|
125
|
+
- - ">="
|
120
126
|
- !ruby/object:Gem::Version
|
121
|
-
version: '
|
127
|
+
version: '0'
|
122
128
|
type: :development
|
123
129
|
prerelease: false
|
124
130
|
version_requirements: !ruby/object:Gem::Requirement
|
125
131
|
requirements:
|
126
|
-
- - "
|
132
|
+
- - ">="
|
127
133
|
- !ruby/object:Gem::Version
|
128
|
-
version: '
|
134
|
+
version: '0'
|
129
135
|
- !ruby/object:Gem::Dependency
|
130
136
|
name: rake
|
131
137
|
requirement: !ruby/object:Gem::Requirement
|
@@ -190,8 +196,10 @@ executables: []
|
|
190
196
|
extensions: []
|
191
197
|
extra_rdoc_files: []
|
192
198
|
files:
|
199
|
+
- ".codeclimate.yml"
|
193
200
|
- ".gitignore"
|
194
201
|
- ".rspec"
|
202
|
+
- ".rubocop.yml"
|
195
203
|
- ".travis.yml"
|
196
204
|
- ".yardopts"
|
197
205
|
- CHANGELOG.md
|
@@ -213,8 +221,6 @@ files:
|
|
213
221
|
- lib/dry/types/coercions/json.rb
|
214
222
|
- lib/dry/types/coercions/params.rb
|
215
223
|
- lib/dry/types/compat.rb
|
216
|
-
- lib/dry/types/compat/form_types.rb
|
217
|
-
- lib/dry/types/compat/int.rb
|
218
224
|
- lib/dry/types/compiler.rb
|
219
225
|
- lib/dry/types/constrained.rb
|
220
226
|
- lib/dry/types/constrained/coercible.rb
|
@@ -224,22 +230,26 @@ files:
|
|
224
230
|
- lib/dry/types/core.rb
|
225
231
|
- lib/dry/types/decorator.rb
|
226
232
|
- lib/dry/types/default.rb
|
227
|
-
- lib/dry/types/definition.rb
|
228
233
|
- lib/dry/types/enum.rb
|
229
234
|
- lib/dry/types/errors.rb
|
230
235
|
- lib/dry/types/extensions.rb
|
231
236
|
- lib/dry/types/extensions/maybe.rb
|
232
237
|
- lib/dry/types/fn_container.rb
|
233
238
|
- lib/dry/types/hash.rb
|
234
|
-
- lib/dry/types/hash/
|
235
|
-
- lib/dry/types/hash/schema_builder.rb
|
239
|
+
- lib/dry/types/hash/constructor.rb
|
236
240
|
- lib/dry/types/inflector.rb
|
237
241
|
- lib/dry/types/json.rb
|
238
242
|
- lib/dry/types/map.rb
|
243
|
+
- lib/dry/types/module.rb
|
244
|
+
- lib/dry/types/nominal.rb
|
239
245
|
- lib/dry/types/options.rb
|
240
246
|
- lib/dry/types/params.rb
|
247
|
+
- lib/dry/types/printable.rb
|
248
|
+
- lib/dry/types/printer.rb
|
241
249
|
- lib/dry/types/result.rb
|
242
250
|
- lib/dry/types/safe.rb
|
251
|
+
- lib/dry/types/schema.rb
|
252
|
+
- lib/dry/types/schema/key.rb
|
243
253
|
- lib/dry/types/spec/types.rb
|
244
254
|
- lib/dry/types/sum.rb
|
245
255
|
- lib/dry/types/type.rb
|
@@ -268,7 +278,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
278
|
- !ruby/object:Gem::Version
|
269
279
|
version: '0'
|
270
280
|
requirements: []
|
271
|
-
rubygems_version: 3.0.
|
281
|
+
rubygems_version: 3.0.3
|
272
282
|
signing_key:
|
273
283
|
specification_version: 4
|
274
284
|
summary: Type system for Ruby supporting coercions, constraints and complex types
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'dry/core/deprecations'
|
2
|
-
|
3
|
-
Dry::Core::Deprecations.warn('Form types were renamed to Params', tag: :'dry-types')
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Types
|
7
|
-
container.keys.grep(/^params\./).each do |key|
|
8
|
-
next if key.start_with?('params.int')
|
9
|
-
register(key.sub('params.', 'form.'), container[key])
|
10
|
-
end
|
11
|
-
|
12
|
-
register('form.int', self['params.integer'])
|
13
|
-
register('form.integer', self['params.integer'])
|
14
|
-
|
15
|
-
class Compiler
|
16
|
-
def visit_form_hash(node)
|
17
|
-
schema, meta = node
|
18
|
-
merge_with('params.hash', :symbolized, schema).meta(meta)
|
19
|
-
end
|
20
|
-
|
21
|
-
def visit_form_array(node)
|
22
|
-
member, meta = node
|
23
|
-
registry['params.array'].of(visit(member)).meta(meta)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/dry/types/compat/int.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'dry/core/deprecations'
|
2
|
-
|
3
|
-
Dry::Core::Deprecations.warn('Int type was renamed to Integer', tag: :'dry-types')
|
4
|
-
|
5
|
-
module Dry
|
6
|
-
module Types
|
7
|
-
register('int', self['integer'])
|
8
|
-
register('strict.int', self['strict.integer'])
|
9
|
-
register('coercible.int', self['coercible.integer'])
|
10
|
-
register('optional.strict.int', self['optional.strict.integer'])
|
11
|
-
register('optional.coercible.int', self['optional.coercible.integer'])
|
12
|
-
register('params.int', self['params.integer'])
|
13
|
-
end
|
14
|
-
end
|
@@ -1,199 +0,0 @@
|
|
1
|
-
require 'dry/types/fn_container'
|
2
|
-
|
3
|
-
module Dry
|
4
|
-
module Types
|
5
|
-
class Hash < Definition
|
6
|
-
# The built-in Hash type has constructors that you can use to define
|
7
|
-
# hashes with explicit schemas and coercible values using the built-in types.
|
8
|
-
#
|
9
|
-
# Basic {Schema} evaluates default values for keys missing in input hash
|
10
|
-
# (see {Schema#resolve_missing_value})
|
11
|
-
#
|
12
|
-
# @see Dry::Types::Default#evaluate
|
13
|
-
# @see Dry::Types::Default::Callable#evaluate
|
14
|
-
class Schema < Hash
|
15
|
-
NO_TRANSFORM = Dry::Types::FnContainer.register { |x| x }
|
16
|
-
SYMBOLIZE_KEY = Dry::Types::FnContainer.register(:to_sym.to_proc)
|
17
|
-
|
18
|
-
# @return [Hash{Symbol => Definition}]
|
19
|
-
attr_reader :member_types
|
20
|
-
|
21
|
-
# @return [#call]
|
22
|
-
attr_reader :transform_key
|
23
|
-
|
24
|
-
# @param [Class] _primitive
|
25
|
-
# @param [Hash] options
|
26
|
-
# @option options [Hash{Symbol => Definition}] :member_types
|
27
|
-
# @option options [String] :key_transform_fn
|
28
|
-
def initialize(_primitive, **options)
|
29
|
-
@member_types = options.fetch(:member_types)
|
30
|
-
|
31
|
-
meta = options[:meta] || EMPTY_HASH
|
32
|
-
key_fn = meta.fetch(:key_transform_fn, NO_TRANSFORM)
|
33
|
-
|
34
|
-
@transform_key = Dry::Types::FnContainer[key_fn]
|
35
|
-
|
36
|
-
super
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param [Hash] hash
|
40
|
-
# @return [Hash{Symbol => Object}]
|
41
|
-
def call(hash)
|
42
|
-
coerce(hash)
|
43
|
-
end
|
44
|
-
alias_method :[], :call
|
45
|
-
|
46
|
-
# @param [Hash] hash
|
47
|
-
# @yieldparam [Failure] failure
|
48
|
-
# @yieldreturn [Result]
|
49
|
-
# @return [Logic::Result]
|
50
|
-
# @return [Object] if coercion fails and a block is given
|
51
|
-
def try(hash)
|
52
|
-
if hash.is_a?(::Hash)
|
53
|
-
success = true
|
54
|
-
output = {}
|
55
|
-
|
56
|
-
begin
|
57
|
-
result = try_coerce(hash) do |key, member_result|
|
58
|
-
success &&= member_result.success?
|
59
|
-
output[key] = member_result.input
|
60
|
-
|
61
|
-
member_result
|
62
|
-
end
|
63
|
-
rescue ConstraintError, UnknownKeysError, SchemaError, MissingKeyError => e
|
64
|
-
success = false
|
65
|
-
result = e
|
66
|
-
end
|
67
|
-
else
|
68
|
-
success = false
|
69
|
-
output = hash
|
70
|
-
result = "#{hash} must be a hash"
|
71
|
-
end
|
72
|
-
|
73
|
-
if success
|
74
|
-
success(output)
|
75
|
-
else
|
76
|
-
failure = failure(output, result)
|
77
|
-
block_given? ? yield(failure) : failure
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# @param meta [Boolean] Whether to dump the meta to the AST
|
82
|
-
# @return [Array] An AST representation
|
83
|
-
def to_ast(meta: true)
|
84
|
-
[
|
85
|
-
:hash_schema,
|
86
|
-
[
|
87
|
-
member_types.map { |name, member| [:member, [name, member.to_ast(meta: meta)]] },
|
88
|
-
meta ? self.meta : EMPTY_HASH
|
89
|
-
]
|
90
|
-
]
|
91
|
-
end
|
92
|
-
|
93
|
-
# @param [Hash] hash
|
94
|
-
# @return [Boolean]
|
95
|
-
def valid?(hash)
|
96
|
-
result = try(hash)
|
97
|
-
result.success?
|
98
|
-
end
|
99
|
-
alias_method :===, :valid?
|
100
|
-
|
101
|
-
# Whether the schema rejects unknown keys
|
102
|
-
# @return [Boolean]
|
103
|
-
def strict?
|
104
|
-
meta.fetch(:strict, false)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Make the schema intolerant to unknown keys
|
108
|
-
# @return [Schema]
|
109
|
-
def strict
|
110
|
-
meta(strict: true)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Injects a key transformation function
|
114
|
-
# @param [#call,nil] proc
|
115
|
-
# @param [#call,nil] block
|
116
|
-
# @return [Schema]
|
117
|
-
def with_key_transform(proc = nil, &block)
|
118
|
-
fn = proc || block
|
119
|
-
|
120
|
-
if fn.nil?
|
121
|
-
raise ArgumentError, "a block or callable argument is required"
|
122
|
-
end
|
123
|
-
|
124
|
-
handle = Dry::Types::FnContainer.register(fn)
|
125
|
-
meta(key_transform_fn: handle)
|
126
|
-
end
|
127
|
-
|
128
|
-
# @param [{Symbol => Definition}] type_map
|
129
|
-
# @return [Schema]
|
130
|
-
def schema(type_map)
|
131
|
-
member_types = self.member_types.merge(transform_types(type_map))
|
132
|
-
Schema.new(primitive, **options, member_types: member_types, meta: meta)
|
133
|
-
end
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
def resolve(hash)
|
138
|
-
result = {}
|
139
|
-
|
140
|
-
hash.each do |key, value|
|
141
|
-
k = transform_key.(key)
|
142
|
-
|
143
|
-
if member_types.key?(k)
|
144
|
-
result[k] = yield(member_types[k], k, value)
|
145
|
-
elsif strict?
|
146
|
-
raise UnknownKeysError.new(*unexpected_keys(hash.keys))
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
if result.size < member_types.size
|
151
|
-
resolve_missing_keys(result, &Proc.new)
|
152
|
-
end
|
153
|
-
|
154
|
-
result
|
155
|
-
end
|
156
|
-
|
157
|
-
def resolve_missing_keys(result)
|
158
|
-
member_types.each do |k, type|
|
159
|
-
next if result.key?(k)
|
160
|
-
|
161
|
-
if type.default?
|
162
|
-
result[k] = yield(type, k, Undefined)
|
163
|
-
elsif !type.meta[:omittable]
|
164
|
-
raise MissingKeyError, k
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# @param keys [Array<Symbol>]
|
170
|
-
# @return [Array<Symbol>]
|
171
|
-
def unexpected_keys(keys)
|
172
|
-
keys.map(&transform_key) - member_types.keys
|
173
|
-
end
|
174
|
-
|
175
|
-
# @param [Hash] hash
|
176
|
-
# @return [Hash{Symbol => Object}]
|
177
|
-
def try_coerce(hash)
|
178
|
-
resolve(hash) do |type, key, value|
|
179
|
-
yield(key, type.try(value))
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# @param [Hash] hash
|
184
|
-
# @return [Hash{Symbol => Object}]
|
185
|
-
def coerce(hash)
|
186
|
-
resolve(hash) do |type, key, value|
|
187
|
-
begin
|
188
|
-
type.call(value)
|
189
|
-
rescue ConstraintError => e
|
190
|
-
raise SchemaError.new(key, value, e.result)
|
191
|
-
rescue TypeError, ArgumentError => e
|
192
|
-
raise SchemaError.new(key, value, e.message)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|