dry-types 0.14.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 +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
|