dry-types 0.14.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
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 +12 -11
  5. data/CHANGELOG.md +115 -4
  6. data/Gemfile +2 -3
  7. data/benchmarks/hash_schemas.rb +5 -5
  8. data/dry-types.gemspec +1 -1
  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 +32 -11
  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 +18 -8
  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.1'.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.1
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-03-25 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
@@ -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.1
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
@@ -1,75 +0,0 @@
1
- require 'dry/types/hash/schema'
2
- require 'dry/types/fn_container'
3
-
4
- module Dry
5
- module Types
6
- class Hash < Definition
7
- # A bulder for legacy schemas
8
- # @api private
9
- class SchemaBuilder
10
- NIL_TO_UNDEFINED = -> v { v.nil? ? Undefined : v }
11
- OMITTABLE_KEYS = %i(schema weak symbolized).freeze
12
- STRICT = %i(strict strict_with_defaults).freeze
13
-
14
- # @param primitive [Type]
15
- # @option options [Hash{Symbol => Definition}] :member_types
16
- # @option options [Symbol] :hash_type
17
- def call(primitive, **options)
18
- hash_type = options.fetch(:hash_type)
19
- member_types = {}
20
-
21
- options.fetch(:member_types).each do |k, t|
22
- member_types[k] = build_type(hash_type, t)
23
- end
24
-
25
- instantiate(primitive, **options, member_types: member_types)
26
- end
27
-
28
- def instantiate(primitive, hash_type: :base, meta: EMPTY_HASH, **options)
29
- meta = meta.dup
30
-
31
- meta[:strict] = true if strict?(hash_type)
32
- meta[:key_transform_fn] = Schema::SYMBOLIZE_KEY if hash_type == :symbolized
33
-
34
- Schema.new(primitive, **options, meta: meta)
35
- end
36
-
37
- private
38
-
39
- def omittable?(constructor)
40
- OMITTABLE_KEYS.include?(constructor)
41
- end
42
-
43
- def strict?(constructor)
44
- STRICT.include?(constructor)
45
- end
46
-
47
- def build_type(constructor, type)
48
- type = safe(constructor, type)
49
- type = default(constructor, type) if type.default?
50
- type = type.meta(omittable: true) if omittable?(constructor)
51
- type
52
- end
53
-
54
- def safe(constructor, type)
55
- if constructor == :weak || constructor == :symbolized
56
- type.safe
57
- else
58
- type
59
- end
60
- end
61
-
62
- def default(constructor, type)
63
- case constructor
64
- when :strict_with_defaults
65
- type
66
- when :strict
67
- type.type
68
- else
69
- type.constructor(NIL_TO_UNDEFINED)
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end