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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +15 -0
  3. data/.rubocop.yml +43 -0
  4. data/.travis.yml +2 -3
  5. data/CHANGELOG.md +119 -1
  6. data/Gemfile +2 -2
  7. data/benchmarks/hash_schemas.rb +5 -5
  8. data/dry-types.gemspec +2 -2
  9. data/lib/dry/types.rb +67 -45
  10. data/lib/dry/types/any.rb +11 -2
  11. data/lib/dry/types/array.rb +1 -4
  12. data/lib/dry/types/array/member.rb +2 -2
  13. data/lib/dry/types/builder.rb +23 -3
  14. data/lib/dry/types/builder_methods.rb +10 -11
  15. data/lib/dry/types/coercions/params.rb +2 -0
  16. data/lib/dry/types/compat.rb +0 -2
  17. data/lib/dry/types/compiler.rb +25 -33
  18. data/lib/dry/types/constrained.rb +5 -4
  19. data/lib/dry/types/constructor.rb +33 -12
  20. data/lib/dry/types/container.rb +2 -0
  21. data/lib/dry/types/core.rb +22 -12
  22. data/lib/dry/types/default.rb +4 -4
  23. data/lib/dry/types/enum.rb +10 -3
  24. data/lib/dry/types/errors.rb +1 -1
  25. data/lib/dry/types/extensions/maybe.rb +11 -1
  26. data/lib/dry/types/hash.rb +70 -63
  27. data/lib/dry/types/hash/constructor.rb +20 -0
  28. data/lib/dry/types/json.rb +7 -7
  29. data/lib/dry/types/map.rb +6 -1
  30. data/lib/dry/types/module.rb +115 -0
  31. data/lib/dry/types/{definition.rb → nominal.rb} +10 -4
  32. data/lib/dry/types/options.rb +2 -2
  33. data/lib/dry/types/params.rb +11 -11
  34. data/lib/dry/types/printable.rb +12 -0
  35. data/lib/dry/types/printer.rb +309 -0
  36. data/lib/dry/types/result.rb +2 -2
  37. data/lib/dry/types/safe.rb +4 -2
  38. data/lib/dry/types/schema.rb +298 -0
  39. data/lib/dry/types/schema/key.rb +130 -0
  40. data/lib/dry/types/spec/types.rb +12 -4
  41. data/lib/dry/types/sum.rb +14 -15
  42. data/lib/dry/types/version.rb +1 -1
  43. metadata +22 -12
  44. data/lib/dry/types/compat/form_types.rb +0 -27
  45. data/lib/dry/types/compat/int.rb +0 -14
  46. data/lib/dry/types/hash/schema.rb +0 -199
  47. data/lib/dry/types/hash/schema_builder.rb +0 -75
@@ -1,4 +1,4 @@
1
- RSpec.shared_examples_for 'Dry::Types::Definition without primitive' do
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::Definition#meta' do
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::Definition do
79
- it_behaves_like 'Dry::Types::Definition without primitive'
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) do |result|
33
+ try(input) { |result|
33
34
  raise ConstraintError.new(result, input)
34
- end.input
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
- left == Types['strict.nil']
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
- result = left.try(input) do
77
- right.try(input)
78
- end
79
-
80
- return result if result.success?
81
-
82
- if block
83
- yield(result)
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 Definition#to_ast
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
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Types
3
- VERSION = '0.14.0'.freeze
3
+ VERSION = '0.15.0'.freeze
4
4
  end
5
5
  end
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.14.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-01-29 00:00:00.000000000 Z
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: '1.6'
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: '1.6'
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/schema.rb
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.2
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
@@ -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