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.
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,14 +1,16 @@
1
+ require 'dry/core/deprecations'
1
2
  require 'dry/types/builder'
2
3
  require 'dry/types/result'
3
4
  require 'dry/types/options'
4
5
 
5
6
  module Dry
6
7
  module Types
7
- class Definition
8
+ class Nominal
8
9
  include Type
9
10
  include Options
10
11
  include Builder
11
- include Dry::Equalizer(:primitive, :options, :meta)
12
+ include Printable
13
+ include Dry::Equalizer(:primitive, :options, :meta, inspect: false)
12
14
 
13
15
  # @return [Class]
14
16
  attr_reader :primitive
@@ -96,15 +98,19 @@ module Dry
96
98
  alias_method :valid?, :primitive?
97
99
  alias_method :===, :primitive?
98
100
 
99
- # Return AST representation of a type definition
101
+ # Return AST representation of a type nominal
100
102
  #
101
103
  # @api public
102
104
  #
103
105
  # @return [Array]
104
106
  def to_ast(meta: true)
105
- [:definition, [primitive, meta ? self.meta : EMPTY_HASH]]
107
+ [:nominal, [primitive, meta ? self.meta : EMPTY_HASH]]
106
108
  end
107
109
  end
110
+
111
+ extend Dry::Core::Deprecations[:'dry-types']
112
+ Definition = Nominal
113
+ deprecate_constant(:Definition, message: "Nominal")
108
114
  end
109
115
  end
110
116
 
@@ -4,7 +4,7 @@ module Dry
4
4
  # @return [Hash]
5
5
  attr_reader :options
6
6
 
7
- # @see Definition#initialize
7
+ # @see Nominal#initialize
8
8
  def initialize(*args, meta: EMPTY_HASH, **options)
9
9
  @__args__ = args.freeze
10
10
  @options = options.freeze
@@ -13,7 +13,7 @@ module Dry
13
13
 
14
14
  # @param [Hash] new_options
15
15
  # @return [Type]
16
- def with(new_options)
16
+ def with(**new_options)
17
17
  self.class.new(*@__args__, **options, meta: @meta, **new_options)
18
18
  end
19
19
 
@@ -3,27 +3,27 @@ require 'dry/types/coercions/params'
3
3
  module Dry
4
4
  module Types
5
5
  register('params.nil') do
6
- self['nil'].constructor(Coercions::Params.method(:to_nil))
6
+ self['nominal.nil'].constructor(Coercions::Params.method(:to_nil))
7
7
  end
8
8
 
9
9
  register('params.date') do
10
- self['date'].constructor(Coercions::Params.method(:to_date))
10
+ self['nominal.date'].constructor(Coercions::Params.method(:to_date))
11
11
  end
12
12
 
13
13
  register('params.date_time') do
14
- self['date_time'].constructor(Coercions::Params.method(:to_date_time))
14
+ self['nominal.date_time'].constructor(Coercions::Params.method(:to_date_time))
15
15
  end
16
16
 
17
17
  register('params.time') do
18
- self['time'].constructor(Coercions::Params.method(:to_time))
18
+ self['nominal.time'].constructor(Coercions::Params.method(:to_time))
19
19
  end
20
20
 
21
21
  register('params.true') do
22
- self['true'].constructor(Coercions::Params.method(:to_true))
22
+ self['nominal.true'].constructor(Coercions::Params.method(:to_true))
23
23
  end
24
24
 
25
25
  register('params.false') do
26
- self['false'].constructor(Coercions::Params.method(:to_false))
26
+ self['nominal.false'].constructor(Coercions::Params.method(:to_false))
27
27
  end
28
28
 
29
29
  register('params.bool') do
@@ -31,23 +31,23 @@ module Dry
31
31
  end
32
32
 
33
33
  register('params.integer') do
34
- self['integer'].constructor(Coercions::Params.method(:to_int))
34
+ self['nominal.integer'].constructor(Coercions::Params.method(:to_int))
35
35
  end
36
36
 
37
37
  register('params.float') do
38
- self['float'].constructor(Coercions::Params.method(:to_float))
38
+ self['nominal.float'].constructor(Coercions::Params.method(:to_float))
39
39
  end
40
40
 
41
41
  register('params.decimal') do
42
- self['decimal'].constructor(Coercions::Params.method(:to_decimal))
42
+ self['nominal.decimal'].constructor(Coercions::Params.method(:to_decimal))
43
43
  end
44
44
 
45
45
  register('params.array') do
46
- self['array'].constructor(Coercions::Params.method(:to_ary)).safe
46
+ self['nominal.array'].constructor(Coercions::Params.method(:to_ary)).safe
47
47
  end
48
48
 
49
49
  register('params.hash') do
50
- self['hash'].constructor(Coercions::Params.method(:to_hash)).safe
50
+ self['nominal.hash'].constructor(Coercions::Params.method(:to_hash)).safe
51
51
  end
52
52
  end
53
53
  end
@@ -0,0 +1,12 @@
1
+ module Dry
2
+ module Types
3
+ module Printable
4
+ # @return [String]
5
+ # @api public
6
+ def to_s
7
+ PRINTER.(self) { super }
8
+ end
9
+ alias_method :inspect, :to_s
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # @api private
6
+ class Printer
7
+ MAPPING = {
8
+ Nominal => :visit_nominal,
9
+ Constructor => :visit_constructor,
10
+ Hash::Constructor => :visit_constructor,
11
+ Constrained => :visit_constrained,
12
+ Constrained::Coercible => :visit_constrained,
13
+ Hash => :visit_hash,
14
+ Schema => :visit_schema,
15
+ Schema::Key => :visit_key,
16
+ Map => :visit_map,
17
+ Array => :visit_array,
18
+ Array::Member => :visit_array_member,
19
+ Safe => :visit_safe,
20
+ Enum => :visit_enum,
21
+ Default => :visit_default,
22
+ Default::Callable => :visit_default,
23
+ Sum => :visit_sum,
24
+ Sum::Constrained => :visit_sum,
25
+ Any.class => :visit_any
26
+ }
27
+
28
+ def call(type)
29
+ output = "".dup
30
+ visit(type) { |str| output << str }
31
+ "#<Dry::Types[#{ output }]>"
32
+ end
33
+
34
+ def visit(type, &block)
35
+ print_with = MAPPING.fetch(type.class) do
36
+ if type.is_a?(Type)
37
+ return yield type.inspect
38
+ else
39
+ raise ArgumentError, "Do not know how to print #{ type.class }"
40
+ end
41
+ end
42
+ send(print_with, type, &block)
43
+ end
44
+
45
+ def visit_any(_)
46
+ yield "Any"
47
+ end
48
+
49
+ def visit_array(_)
50
+ yield "Array"
51
+ end
52
+
53
+ def visit_array_member(array)
54
+ visit(array.member) do |type|
55
+ yield "Array<#{ type }>"
56
+ end
57
+ end
58
+
59
+ def visit_constructor(constructor)
60
+ visit(constructor.type) do |type|
61
+ visit_callable(constructor.fn) do |fn|
62
+ options = constructor.options.dup
63
+ options.delete(:fn)
64
+
65
+ visit_options(options, constructor.meta) do |opts|
66
+ yield "Constructor<#{ type } fn=#{ fn }#{ opts }>"
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def visit_constrained(constrained)
73
+ visit(constrained.type) do |type|
74
+ options = constrained.options.dup
75
+ rule = options.delete(:rule)
76
+
77
+ visit_options(options, constrained.meta) do |opts|
78
+ yield "Constrained<#{ type } rule=[#{ rule.to_s }]>"
79
+ end
80
+ end
81
+ end
82
+
83
+ def visit_schema(schema)
84
+ options = schema.options.dup
85
+ size = schema.count
86
+ key_fn_str = ""
87
+ type_fn_str = ""
88
+ strict_str = ""
89
+
90
+ strict_str = "strict " if options.delete(:strict)
91
+
92
+ if key_fn = options.delete(:key_transform_fn)
93
+ visit_callable(key_fn) do |fn|
94
+ key_fn_str = "key_fn=#{ fn } "
95
+ end
96
+ end
97
+
98
+ if type_fn = options.delete(:type_transform_fn)
99
+ visit_callable(type_fn) do |fn|
100
+ type_fn_str = "type_fn=#{ fn } "
101
+ end
102
+ end
103
+
104
+ keys = options.delete(:keys)
105
+
106
+ visit_options(options, schema.meta) do |opts|
107
+ schema_parameters = "#{ key_fn_str }#{ type_fn_str }#{ strict_str }#{ opts }"
108
+
109
+ header = "Schema<#{ schema_parameters }keys={"
110
+
111
+ if size.zero?
112
+ yield "#{ header}}>"
113
+ else
114
+ yield header.dup << keys.map { |key|
115
+ visit(key) { |type| type }
116
+ }.join(" ") << "}>"
117
+ end
118
+ end
119
+ end
120
+
121
+ def visit_map(map)
122
+ visit(map.key_type) do |key|
123
+ visit(map.value_type) do |value|
124
+ options = map.options.dup
125
+ options.delete(:key_type)
126
+ options.delete(:value_type)
127
+
128
+ visit_options(options, map.meta) do |opts|
129
+ yield "Map<#{ key } => #{ value }>"
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def visit_key(key)
136
+ visit(key.type) do |type|
137
+ if key.required?
138
+ yield "#{ key.name }: #{ type }"
139
+ else
140
+ yield "#{ key.name }?: #{ type }"
141
+ end
142
+ end
143
+ end
144
+
145
+ def visit_sum(sum)
146
+ visit_sum_constructors(sum) do |constructors|
147
+ visit_options(sum.options, sum.meta) do |opts|
148
+ yield "Sum<#{ constructors }#{ opts }>"
149
+ end
150
+ end
151
+ end
152
+
153
+ def visit_sum_constructors(sum)
154
+ case sum.left
155
+ when Sum
156
+ visit_sum_constructors(sum.left) do |left|
157
+ case sum.right
158
+ when Sum
159
+ visit_sum_constructors(sum.right) do |right|
160
+ yield "#{ left } | #{ right }"
161
+ end
162
+ else
163
+ visit(sum.right) do |right|
164
+ yield "#{ left } | #{ right }"
165
+ end
166
+ end
167
+ end
168
+ else
169
+ visit(sum.left) do |left|
170
+ case sum.right
171
+ when Sum
172
+ visit_sum_constructors(sum.right) do |right|
173
+ yield "#{ left } | #{ right }"
174
+ end
175
+ else
176
+ visit(sum.right) do |right|
177
+ yield "#{ left } | #{ right }"
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ def visit_enum(enum)
185
+ visit(enum.type) do |type|
186
+ options = enum.options.dup
187
+ mapping = options.delete(:mapping)
188
+
189
+ visit_options(options, enum.meta) do |opts|
190
+ if mapping == enum.inverted_mapping
191
+ values = mapping.values.map(&:inspect).join(", ")
192
+ yield "Enum<#{ type } values={#{ values }}#{ opts }>"
193
+ else
194
+ mapping_str = mapping.map { |key, value|
195
+ "#{ key.inspect }=>#{ value.inspect }"
196
+ }.join(", ")
197
+ yield "Enum<#{ type } mapping={#{ mapping_str }}#{ opts }>"
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ def visit_default(default)
204
+ visit(default.type) do |type|
205
+ visit_options(default.options, default.meta) do |opts|
206
+ if default.is_a?(Default::Callable)
207
+ visit_callable(default.value) do |fn|
208
+ yield "Default<#{ type } value_fn=#{ fn }#{ opts }>"
209
+ end
210
+ else
211
+ yield "Default<#{ type } value=#{ default.value.inspect }#{ opts }>"
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ def visit_nominal(type)
218
+ visit_options(type.options, type.meta) do |opts|
219
+ yield "Nominal<#{ type.primitive }#{ opts }>"
220
+ end
221
+ end
222
+
223
+ def visit_safe(safe)
224
+ visit(safe.type) do |type|
225
+ yield "Safe<#{ type }>"
226
+ end
227
+ end
228
+
229
+ def visit_hash(hash)
230
+ options = hash.options.dup
231
+ type_fn_str = ""
232
+
233
+ if type_fn = options.delete(:type_transform_fn)
234
+ visit_callable(type_fn) do |fn|
235
+ type_fn_str = "type_fn=#{ fn }"
236
+ end
237
+ end
238
+
239
+ visit_options(options, hash.meta) do |opts|
240
+ if opts.empty? && type_fn_str.empty?
241
+ yield "Hash"
242
+ else
243
+ yield "Hash<#{ type_fn_str }#{ opts }>"
244
+ end
245
+ end
246
+ end
247
+
248
+ def visit_callable(callable)
249
+ fn = callable.is_a?(String) ? FnContainer[callable] : callable
250
+
251
+ case fn
252
+ when Method
253
+ yield "#{ fn.receiver }.#{ fn.name }"
254
+ when Proc
255
+ path, line = fn.source_location
256
+
257
+ if line && line.zero?
258
+ yield ".#{ path }"
259
+ elsif path
260
+ yield "#{ path.sub(Dir.pwd + "/", EMPTY_STRING) }:#{ line }"
261
+ elsif fn.lambda?
262
+ yield "(lambda)"
263
+ else
264
+ match = fn.to_s.match(/\A#<Proc:0x\h+\(&:(\w+)\)>\z/)
265
+
266
+ if match
267
+ yield ".#{ match[1] }"
268
+ else
269
+ yield "(proc)"
270
+ end
271
+ end
272
+ else
273
+ call = fn.method(:call)
274
+
275
+ if call.owner == fn.class
276
+ yield "#{ fn.class.to_s }#call"
277
+ else
278
+ yield "#{ fn.to_s }.call"
279
+ end
280
+ end
281
+ end
282
+
283
+ def visit_options(options, meta)
284
+ if options.empty? && meta.empty?
285
+ yield ""
286
+ else
287
+ opts = options.empty? ? "" : " options=#{ options.inspect }"
288
+
289
+ if meta.empty?
290
+ yield opts
291
+ else
292
+ values = meta.map do |key, value|
293
+ case key
294
+ when Symbol
295
+ "#{ key }: #{ value.inspect }"
296
+ else
297
+ "#{ key.inspect }=>#{ value.inspect }"
298
+ end
299
+ end
300
+
301
+ yield "#{ opts } meta={#{ values.join(", ") }}"
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ PRINTER = Printer.new.freeze
308
+ end
309
+ end
@@ -3,7 +3,7 @@ require 'dry/equalizer'
3
3
  module Dry
4
4
  module Types
5
5
  class Result
6
- include Dry::Equalizer(:input)
6
+ include Dry::Equalizer(:input, inspect: false)
7
7
 
8
8
  # @return [Object]
9
9
  attr_reader :input
@@ -26,7 +26,7 @@ module Dry
26
26
  end
27
27
 
28
28
  class Failure < Result
29
- include Dry::Equalizer(:input, :error)
29
+ include Dry::Equalizer(:input, :error, inspect: false)
30
30
 
31
31
  # @return [#to_s]
32
32
  attr_reader :error