dry-types 0.15.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
  3. data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
  4. data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +18 -2
  7. data/.travis.yml +10 -5
  8. data/.yardopts +6 -2
  9. data/CHANGELOG.md +186 -3
  10. data/Gemfile +11 -5
  11. data/README.md +4 -3
  12. data/Rakefile +4 -2
  13. data/benchmarks/hash_schemas.rb +10 -6
  14. data/benchmarks/lax_schema.rb +15 -0
  15. data/benchmarks/profile_invalid_input.rb +15 -0
  16. data/benchmarks/profile_lax_schema_valid.rb +16 -0
  17. data/benchmarks/profile_valid_input.rb +15 -0
  18. data/benchmarks/schema_valid_vs_invalid.rb +21 -0
  19. data/benchmarks/setup.rb +17 -0
  20. data/docsite/source/array-with-member.html.md +13 -0
  21. data/docsite/source/built-in-types.html.md +116 -0
  22. data/docsite/source/constraints.html.md +31 -0
  23. data/docsite/source/custom-types.html.md +93 -0
  24. data/docsite/source/default-values.html.md +91 -0
  25. data/docsite/source/enum.html.md +69 -0
  26. data/docsite/source/getting-started.html.md +57 -0
  27. data/docsite/source/hash-schemas.html.md +169 -0
  28. data/docsite/source/index.html.md +155 -0
  29. data/docsite/source/map.html.md +17 -0
  30. data/docsite/source/optional-values.html.md +96 -0
  31. data/docsite/source/sum.html.md +21 -0
  32. data/dry-types.gemspec +21 -19
  33. data/lib/dry-types.rb +2 -0
  34. data/lib/dry/types.rb +60 -17
  35. data/lib/dry/types/any.rb +21 -10
  36. data/lib/dry/types/array.rb +17 -1
  37. data/lib/dry/types/array/constructor.rb +32 -0
  38. data/lib/dry/types/array/member.rb +72 -13
  39. data/lib/dry/types/builder.rb +49 -5
  40. data/lib/dry/types/builder_methods.rb +43 -16
  41. data/lib/dry/types/coercions.rb +84 -19
  42. data/lib/dry/types/coercions/json.rb +22 -3
  43. data/lib/dry/types/coercions/params.rb +98 -30
  44. data/lib/dry/types/compiler.rb +35 -12
  45. data/lib/dry/types/constrained.rb +78 -27
  46. data/lib/dry/types/constrained/coercible.rb +36 -6
  47. data/lib/dry/types/constraints.rb +15 -1
  48. data/lib/dry/types/constructor.rb +77 -62
  49. data/lib/dry/types/constructor/function.rb +200 -0
  50. data/lib/dry/types/container.rb +5 -0
  51. data/lib/dry/types/core.rb +35 -14
  52. data/lib/dry/types/decorator.rb +37 -10
  53. data/lib/dry/types/default.rb +48 -16
  54. data/lib/dry/types/enum.rb +31 -16
  55. data/lib/dry/types/errors.rb +73 -7
  56. data/lib/dry/types/extensions.rb +6 -0
  57. data/lib/dry/types/extensions/maybe.rb +52 -5
  58. data/lib/dry/types/extensions/monads.rb +29 -0
  59. data/lib/dry/types/fn_container.rb +5 -0
  60. data/lib/dry/types/hash.rb +32 -14
  61. data/lib/dry/types/hash/constructor.rb +16 -3
  62. data/lib/dry/types/inflector.rb +2 -0
  63. data/lib/dry/types/json.rb +7 -5
  64. data/lib/dry/types/{safe.rb → lax.rb} +33 -16
  65. data/lib/dry/types/map.rb +70 -32
  66. data/lib/dry/types/meta.rb +51 -0
  67. data/lib/dry/types/module.rb +10 -5
  68. data/lib/dry/types/nominal.rb +105 -14
  69. data/lib/dry/types/options.rb +12 -25
  70. data/lib/dry/types/params.rb +14 -3
  71. data/lib/dry/types/predicate_inferrer.rb +197 -0
  72. data/lib/dry/types/predicate_registry.rb +34 -0
  73. data/lib/dry/types/primitive_inferrer.rb +97 -0
  74. data/lib/dry/types/printable.rb +5 -1
  75. data/lib/dry/types/printer.rb +70 -64
  76. data/lib/dry/types/result.rb +26 -0
  77. data/lib/dry/types/schema.rb +177 -80
  78. data/lib/dry/types/schema/key.rb +48 -35
  79. data/lib/dry/types/spec/types.rb +43 -6
  80. data/lib/dry/types/sum.rb +70 -21
  81. data/lib/dry/types/type.rb +49 -0
  82. data/lib/dry/types/version.rb +3 -1
  83. metadata +91 -62
@@ -1,42 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Types
5
+ # Common API for types with options
6
+ #
7
+ # @api private
3
8
  module Options
4
9
  # @return [Hash]
5
10
  attr_reader :options
6
11
 
7
12
  # @see Nominal#initialize
8
- def initialize(*args, meta: EMPTY_HASH, **options)
13
+ #
14
+ # @api private
15
+ def initialize(*args, **options)
9
16
  @__args__ = args.freeze
10
17
  @options = options.freeze
11
- @meta = meta.freeze
12
18
  end
13
19
 
14
20
  # @param [Hash] new_options
21
+ #
15
22
  # @return [Type]
16
- def with(**new_options)
17
- self.class.new(*@__args__, **options, meta: @meta, **new_options)
18
- end
19
-
20
- # @overload meta
21
- # @return [Hash] metadata associated with type
22
23
  #
23
- # @overload meta(data)
24
- # @param [Hash] new metadata to merge into existing metadata
25
- # @return [Type] new type with added metadata
26
- def meta(data = nil)
27
- if !data
28
- @meta
29
- elsif data.empty?
30
- self
31
- else
32
- with(meta: @meta.merge(data))
33
- end
34
- end
35
-
36
- # Resets meta
37
- # @return [Dry::Types::Type]
38
- def pristine
39
- with(meta: EMPTY_HASH)
24
+ # @api private
25
+ def with(**new_options)
26
+ self.class.new(*@__args__, **options, **new_options)
40
27
  end
41
28
  end
42
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/types/coercions/params'
2
4
 
3
5
  module Dry
@@ -27,7 +29,7 @@ module Dry
27
29
  end
28
30
 
29
31
  register('params.bool') do
30
- (self['params.true'] | self['params.false']).safe
32
+ self['params.true'] | self['params.false']
31
33
  end
32
34
 
33
35
  register('params.integer') do
@@ -43,11 +45,20 @@ module Dry
43
45
  end
44
46
 
45
47
  register('params.array') do
46
- self['nominal.array'].constructor(Coercions::Params.method(:to_ary)).safe
48
+ self['nominal.array'].constructor(Coercions::Params.method(:to_ary))
47
49
  end
48
50
 
49
51
  register('params.hash') do
50
- self['nominal.hash'].constructor(Coercions::Params.method(:to_hash)).safe
52
+ self['nominal.hash'].constructor(Coercions::Params.method(:to_hash))
53
+ end
54
+
55
+ register('params.symbol') do
56
+ self['nominal.symbol'].constructor(Coercions::Params.method(:to_symbol))
57
+ end
58
+
59
+ COERCIBLE.each_key do |name|
60
+ next if name.equal?(:string)
61
+ register("optional.params.#{name}", self['params.nil'] | self["params.#{name}"])
51
62
  end
52
63
  end
53
64
  end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/core/cache'
4
+ require 'dry/types/predicate_registry'
5
+
6
+ module Dry
7
+ module Types
8
+ # PredicateInferrer returns the list of predicates used by a type.
9
+ #
10
+ # @api public
11
+ class PredicateInferrer
12
+ extend Core::Cache
13
+
14
+ TYPE_TO_PREDICATE = {
15
+ DateTime => :date_time?,
16
+ FalseClass => :false?,
17
+ Integer => :int?,
18
+ NilClass => :nil?,
19
+ String => :str?,
20
+ TrueClass => :true?,
21
+ BigDecimal => :decimal?
22
+ }.freeze
23
+
24
+ REDUCED_TYPES = {
25
+ [[[:true?], [:false?]]] => %i[bool?]
26
+ }.freeze
27
+
28
+ HASH = %i[hash?].freeze
29
+
30
+ ARRAY = %i[array?].freeze
31
+
32
+ NIL = %i[nil?].freeze
33
+
34
+ # Compiler reduces type AST into a list of predicates
35
+ #
36
+ # @api private
37
+ class Compiler
38
+ # @return [PredicateRegistry]
39
+ # @api private
40
+ attr_reader :registry
41
+
42
+ # @api private
43
+ def initialize(registry)
44
+ @registry = registry
45
+ end
46
+
47
+ # @api private
48
+ def infer_predicate(type)
49
+ [TYPE_TO_PREDICATE.fetch(type) { :"#{type.name.split('::').last.downcase}?" }]
50
+ end
51
+
52
+ # @api private
53
+ def visit(node)
54
+ meth, rest = node
55
+ public_send(:"visit_#{meth}", rest)
56
+ end
57
+
58
+ # @api private
59
+ def visit_nominal(node)
60
+ type = node[0]
61
+ predicate = infer_predicate(type)
62
+
63
+ if registry.key?(predicate[0])
64
+ predicate
65
+ else
66
+ [type?: type]
67
+ end
68
+ end
69
+
70
+ # @api private
71
+ def visit_hash(_)
72
+ HASH
73
+ end
74
+
75
+ # @api private
76
+ def visit_array(_)
77
+ ARRAY
78
+ end
79
+
80
+ # @api private
81
+ def visit_lax(node)
82
+ visit(node)
83
+ end
84
+
85
+ # @api private
86
+ def visit_constructor(node)
87
+ other, * = node
88
+ visit(other)
89
+ end
90
+
91
+ # @api private
92
+ def visit_enum(node)
93
+ other, * = node
94
+ visit(other)
95
+ end
96
+
97
+ # @api private
98
+ def visit_sum(node)
99
+ left_node, right_node, = node
100
+ left = visit(left_node)
101
+ right = visit(right_node)
102
+
103
+ if left.eql?(NIL)
104
+ right
105
+ else
106
+ [[left, right]]
107
+ end
108
+ end
109
+
110
+ # @api private
111
+ def visit_constrained(node)
112
+ other, rules = node
113
+ predicates = visit(rules)
114
+
115
+ if predicates.empty?
116
+ visit(other)
117
+ else
118
+ [*visit(other), *merge_predicates(predicates)]
119
+ end
120
+ end
121
+
122
+ # @api private
123
+ def visit_any(_)
124
+ EMPTY_ARRAY
125
+ end
126
+
127
+ # @api private
128
+ def visit_and(node)
129
+ left, right = node
130
+ visit(left) + visit(right)
131
+ end
132
+
133
+ # @api private
134
+ def visit_predicate(node)
135
+ pred, args = node
136
+
137
+ if pred.equal?(:type?)
138
+ EMPTY_ARRAY
139
+ elsif registry.key?(pred)
140
+ *curried, _ = args
141
+ values = curried.map { |_, v| v }
142
+
143
+ if values.empty?
144
+ [pred]
145
+ else
146
+ [pred => values[0]]
147
+ end
148
+ else
149
+ EMPTY_ARRAY
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ # @api private
156
+ def merge_predicates(nodes)
157
+ preds, merged = nodes.each_with_object([[], {}]) do |predicate, (ps, h)|
158
+ if predicate.is_a?(::Hash)
159
+ h.update(predicate)
160
+ else
161
+ ps << predicate
162
+ end
163
+ end
164
+
165
+ merged.empty? ? preds : [*preds, merged]
166
+ end
167
+ end
168
+
169
+ # @return [Compiler]
170
+ # @api private
171
+ attr_reader :compiler
172
+
173
+ # @api private
174
+ def initialize(registry = PredicateRegistry.new)
175
+ @compiler = Compiler.new(registry)
176
+ end
177
+
178
+ # Infer predicate identifier from the provided type
179
+ #
180
+ # @param [Type] type
181
+ # @return [Symbol]
182
+ #
183
+ # @api private
184
+ def [](type)
185
+ self.class.fetch_or_store(type) do
186
+ predicates = compiler.visit(type.to_ast)
187
+
188
+ if predicates.is_a?(::Hash)
189
+ predicates
190
+ else
191
+ REDUCED_TYPES[predicates] || predicates
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/logic/predicates'
4
+
5
+ module Dry
6
+ module Types
7
+ # A registry with predicate objects from `Dry::Logic::Predicates`
8
+ #
9
+ # @api private
10
+ class PredicateRegistry
11
+ # @api private
12
+ attr_reader :predicates
13
+
14
+ # @api private
15
+ attr_reader :has_predicate
16
+
17
+ # @api private
18
+ def initialize(predicates = Logic::Predicates)
19
+ @predicates = predicates
20
+ @has_predicate = ::Kernel.instance_method(:respond_to?).bind(@predicates)
21
+ end
22
+
23
+ # @api private
24
+ def [](name)
25
+ predicates[name]
26
+ end
27
+
28
+ # @api private
29
+ def key?(name)
30
+ has_predicate.(name)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/core/cache'
4
+
5
+ module Dry
6
+ module Types
7
+ # PrimitiveInferrer returns the list of classes matching a type.
8
+ #
9
+ # @api public
10
+ class PrimitiveInferrer
11
+ extend Core::Cache
12
+
13
+ # Compiler reduces type AST into a list of primitives
14
+ #
15
+ # @api private
16
+ class Compiler
17
+ # @api private
18
+ def visit(node)
19
+ meth, rest = node
20
+ public_send(:"visit_#{meth}", rest)
21
+ end
22
+
23
+ # @api private
24
+ def visit_nominal(node)
25
+ type, _ = node
26
+ type
27
+ end
28
+
29
+ # @api private
30
+ def visit_hash(_)
31
+ ::Hash
32
+ end
33
+ alias_method :visit_schema, :visit_hash
34
+
35
+ # @api private
36
+ def visit_array(_)
37
+ ::Array
38
+ end
39
+
40
+ # @api private
41
+ def visit_lax(node)
42
+ visit(node)
43
+ end
44
+
45
+ # @api private
46
+ def visit_constructor(node)
47
+ other, * = node
48
+ visit(other)
49
+ end
50
+
51
+ # @api private
52
+ def visit_enum(node)
53
+ other, * = node
54
+ visit(other)
55
+ end
56
+
57
+ # @api private
58
+ def visit_sum(node)
59
+ left, right = node
60
+
61
+ [visit(left), visit(right)].flatten(1)
62
+ end
63
+
64
+ # @api private
65
+ def visit_constrained(node)
66
+ other, * = node
67
+ visit(other)
68
+ end
69
+
70
+ # @api private
71
+ def visit_any(_)
72
+ ::Object
73
+ end
74
+ end
75
+
76
+ # @return [Compiler]
77
+ # @api private
78
+ attr_reader :compiler
79
+
80
+ # @api private
81
+ def initialize
82
+ @compiler = Compiler.new
83
+ end
84
+
85
+ # Infer primitives from the provided type
86
+ #
87
+ # @return [Array[Class]]
88
+ #
89
+ # @api private
90
+ def [](type)
91
+ self.class.fetch_or_store(type) do
92
+ Array(compiler.visit(type.to_ast)).freeze
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Types
5
+ # @api private
3
6
  module Printable
4
7
  # @return [String]
5
- # @api public
8
+ #
9
+ # @api private
6
10
  def to_s
7
11
  PRINTER.(self) { super }
8
12
  end
@@ -8,6 +8,7 @@ module Dry
8
8
  Nominal => :visit_nominal,
9
9
  Constructor => :visit_constructor,
10
10
  Hash::Constructor => :visit_constructor,
11
+ Array::Constructor => :visit_constructor,
11
12
  Constrained => :visit_constrained,
12
13
  Constrained::Coercible => :visit_constrained,
13
14
  Hash => :visit_hash,
@@ -16,7 +17,7 @@ module Dry
16
17
  Map => :visit_map,
17
18
  Array => :visit_array,
18
19
  Array::Member => :visit_array_member,
19
- Safe => :visit_safe,
20
+ Lax => :visit_lax,
20
21
  Enum => :visit_enum,
21
22
  Default => :visit_default,
22
23
  Default::Callable => :visit_default,
@@ -26,9 +27,9 @@ module Dry
26
27
  }
27
28
 
28
29
  def call(type)
29
- output = "".dup
30
+ output = ''.dup
30
31
  visit(type) { |str| output << str }
31
- "#<Dry::Types[#{ output }]>"
32
+ "#<Dry::Types[#{output}]>"
32
33
  end
33
34
 
34
35
  def visit(type, &block)
@@ -36,34 +37,38 @@ module Dry
36
37
  if type.is_a?(Type)
37
38
  return yield type.inspect
38
39
  else
39
- raise ArgumentError, "Do not know how to print #{ type.class }"
40
+ raise ArgumentError, "Do not know how to print #{type.class}"
40
41
  end
41
42
  end
42
43
  send(print_with, type, &block)
43
44
  end
44
45
 
45
46
  def visit_any(_)
46
- yield "Any"
47
+ yield 'Any'
47
48
  end
48
49
 
49
- def visit_array(_)
50
- yield "Array"
50
+ def visit_array(type)
51
+ visit_options(EMPTY_HASH, type.meta) do |opts|
52
+ yield "Array#{opts}"
53
+ end
51
54
  end
52
55
 
53
56
  def visit_array_member(array)
54
57
  visit(array.member) do |type|
55
- yield "Array<#{ type }>"
58
+ visit_options(EMPTY_HASH, array.meta) do |opts|
59
+ yield "Array<#{type}#{opts}>"
60
+ end
56
61
  end
57
62
  end
58
63
 
59
64
  def visit_constructor(constructor)
60
65
  visit(constructor.type) do |type|
61
- visit_callable(constructor.fn) do |fn|
66
+ visit_callable(constructor.fn.fn) do |fn|
62
67
  options = constructor.options.dup
63
68
  options.delete(:fn)
64
69
 
65
- visit_options(options, constructor.meta) do |opts|
66
- yield "Constructor<#{ type } fn=#{ fn }#{ opts }>"
70
+ visit_options(options) do |opts|
71
+ yield "Constructor<#{type} fn=#{fn}#{opts}>"
67
72
  end
68
73
  end
69
74
  end
@@ -74,8 +79,8 @@ module Dry
74
79
  options = constrained.options.dup
75
80
  rule = options.delete(:rule)
76
81
 
77
- visit_options(options, constrained.meta) do |opts|
78
- yield "Constrained<#{ type } rule=[#{ rule.to_s }]>"
82
+ visit_options(options) do |_opts|
83
+ yield "Constrained<#{type} rule=[#{rule}]>"
79
84
  end
80
85
  end
81
86
  end
@@ -83,37 +88,38 @@ module Dry
83
88
  def visit_schema(schema)
84
89
  options = schema.options.dup
85
90
  size = schema.count
86
- key_fn_str = ""
87
- type_fn_str = ""
88
- strict_str = ""
91
+ key_fn_str = ''
92
+ type_fn_str = ''
93
+ strict_str = ''
89
94
 
90
- strict_str = "strict " if options.delete(:strict)
95
+ strict_str = 'strict ' if options.delete(:strict)
91
96
 
92
97
  if key_fn = options.delete(:key_transform_fn)
93
98
  visit_callable(key_fn) do |fn|
94
- key_fn_str = "key_fn=#{ fn } "
99
+ key_fn_str = "key_fn=#{fn} "
95
100
  end
96
101
  end
97
102
 
98
103
  if type_fn = options.delete(:type_transform_fn)
99
104
  visit_callable(type_fn) do |fn|
100
- type_fn_str = "type_fn=#{ fn } "
105
+ type_fn_str = "type_fn=#{fn} "
101
106
  end
102
107
  end
103
108
 
104
109
  keys = options.delete(:keys)
105
110
 
106
111
  visit_options(options, schema.meta) do |opts|
107
- schema_parameters = "#{ key_fn_str }#{ type_fn_str }#{ strict_str }#{ opts }"
112
+ opts = "#{opts[1..-1]} " unless opts.empty?
113
+ schema_parameters = "#{key_fn_str}#{type_fn_str}#{strict_str}#{opts}"
108
114
 
109
- header = "Schema<#{ schema_parameters }keys={"
115
+ header = "Schema<#{schema_parameters}keys={"
110
116
 
111
117
  if size.zero?
112
- yield "#{ header}}>"
118
+ yield "#{header}}>"
113
119
  else
114
120
  yield header.dup << keys.map { |key|
115
121
  visit(key) { |type| type }
116
- }.join(" ") << "}>"
122
+ }.join(' ') << '}>'
117
123
  end
118
124
  end
119
125
  end
@@ -125,8 +131,8 @@ module Dry
125
131
  options.delete(:key_type)
126
132
  options.delete(:value_type)
127
133
 
128
- visit_options(options, map.meta) do |opts|
129
- yield "Map<#{ key } => #{ value }>"
134
+ visit_options(options) do |_opts|
135
+ yield "Map<#{key} => #{value}>"
130
136
  end
131
137
  end
132
138
  end
@@ -135,9 +141,9 @@ module Dry
135
141
  def visit_key(key)
136
142
  visit(key.type) do |type|
137
143
  if key.required?
138
- yield "#{ key.name }: #{ type }"
144
+ yield "#{key.name}: #{type}"
139
145
  else
140
- yield "#{ key.name }?: #{ type }"
146
+ yield "#{key.name}?: #{type}"
141
147
  end
142
148
  end
143
149
  end
@@ -145,7 +151,7 @@ module Dry
145
151
  def visit_sum(sum)
146
152
  visit_sum_constructors(sum) do |constructors|
147
153
  visit_options(sum.options, sum.meta) do |opts|
148
- yield "Sum<#{ constructors }#{ opts }>"
154
+ yield "Sum<#{constructors}#{opts}>"
149
155
  end
150
156
  end
151
157
  end
@@ -157,11 +163,11 @@ module Dry
157
163
  case sum.right
158
164
  when Sum
159
165
  visit_sum_constructors(sum.right) do |right|
160
- yield "#{ left } | #{ right }"
166
+ yield "#{left} | #{right}"
161
167
  end
162
168
  else
163
169
  visit(sum.right) do |right|
164
- yield "#{ left } | #{ right }"
170
+ yield "#{left} | #{right}"
165
171
  end
166
172
  end
167
173
  end
@@ -170,11 +176,11 @@ module Dry
170
176
  case sum.right
171
177
  when Sum
172
178
  visit_sum_constructors(sum.right) do |right|
173
- yield "#{ left } | #{ right }"
179
+ yield "#{left} | #{right}"
174
180
  end
175
181
  else
176
182
  visit(sum.right) do |right|
177
- yield "#{ left } | #{ right }"
183
+ yield "#{left} | #{right}"
178
184
  end
179
185
  end
180
186
  end
@@ -186,15 +192,15 @@ module Dry
186
192
  options = enum.options.dup
187
193
  mapping = options.delete(:mapping)
188
194
 
189
- visit_options(options, enum.meta) do |opts|
195
+ visit_options(options) do |opts|
190
196
  if mapping == enum.inverted_mapping
191
- values = mapping.values.map(&:inspect).join(", ")
192
- yield "Enum<#{ type } values={#{ values }}#{ opts }>"
197
+ values = mapping.values.map(&:inspect).join(', ')
198
+ yield "Enum<#{type} values={#{values}}#{opts}>"
193
199
  else
194
200
  mapping_str = mapping.map { |key, value|
195
- "#{ key.inspect }=>#{ value.inspect }"
196
- }.join(", ")
197
- yield "Enum<#{ type } mapping={#{ mapping_str }}#{ opts }>"
201
+ "#{key.inspect}=>#{value.inspect}"
202
+ }.join(', ')
203
+ yield "Enum<#{type} mapping={#{mapping_str}}#{opts}>"
198
204
  end
199
205
  end
200
206
  end
@@ -202,13 +208,13 @@ module Dry
202
208
 
203
209
  def visit_default(default)
204
210
  visit(default.type) do |type|
205
- visit_options(default.options, default.meta) do |opts|
211
+ visit_options(default.options) do |opts|
206
212
  if default.is_a?(Default::Callable)
207
213
  visit_callable(default.value) do |fn|
208
- yield "Default<#{ type } value_fn=#{ fn }#{ opts }>"
214
+ yield "Default<#{type} value_fn=#{fn}#{opts}>"
209
215
  end
210
216
  else
211
- yield "Default<#{ type } value=#{ default.value.inspect }#{ opts }>"
217
+ yield "Default<#{type} value=#{default.value.inspect}#{opts}>"
212
218
  end
213
219
  end
214
220
  end
@@ -216,31 +222,31 @@ module Dry
216
222
 
217
223
  def visit_nominal(type)
218
224
  visit_options(type.options, type.meta) do |opts|
219
- yield "Nominal<#{ type.primitive }#{ opts }>"
225
+ yield "Nominal<#{type.primitive}#{opts}>"
220
226
  end
221
227
  end
222
228
 
223
- def visit_safe(safe)
224
- visit(safe.type) do |type|
225
- yield "Safe<#{ type }>"
229
+ def visit_lax(lax)
230
+ visit(lax.type) do |type|
231
+ yield "Lax<#{type}>"
226
232
  end
227
233
  end
228
234
 
229
235
  def visit_hash(hash)
230
236
  options = hash.options.dup
231
- type_fn_str = ""
237
+ type_fn_str = ''
232
238
 
233
239
  if type_fn = options.delete(:type_transform_fn)
234
240
  visit_callable(type_fn) do |fn|
235
- type_fn_str = "type_fn=#{ fn }"
241
+ type_fn_str = "type_fn=#{fn}"
236
242
  end
237
243
  end
238
244
 
239
245
  visit_options(options, hash.meta) do |opts|
240
246
  if opts.empty? && type_fn_str.empty?
241
- yield "Hash"
247
+ yield 'Hash'
242
248
  else
243
- yield "Hash<#{ type_fn_str }#{ opts }>"
249
+ yield "Hash<#{type_fn_str}#{opts}>"
244
250
  end
245
251
  end
246
252
  end
@@ -250,41 +256,41 @@ module Dry
250
256
 
251
257
  case fn
252
258
  when Method
253
- yield "#{ fn.receiver }.#{ fn.name }"
259
+ yield "#{fn.receiver}.#{fn.name}"
254
260
  when Proc
255
261
  path, line = fn.source_location
256
262
 
257
- if line && line.zero?
258
- yield ".#{ path }"
263
+ if line&.zero?
264
+ yield ".#{path}"
259
265
  elsif path
260
- yield "#{ path.sub(Dir.pwd + "/", EMPTY_STRING) }:#{ line }"
266
+ yield "#{path.sub(Dir.pwd + '/', EMPTY_STRING)}:#{line}"
261
267
  elsif fn.lambda?
262
- yield "(lambda)"
268
+ yield '(lambda)'
263
269
  else
264
270
  match = fn.to_s.match(/\A#<Proc:0x\h+\(&:(\w+)\)>\z/)
265
271
 
266
272
  if match
267
- yield ".#{ match[1] }"
273
+ yield ".#{match[1]}"
268
274
  else
269
- yield "(proc)"
275
+ yield '(proc)'
270
276
  end
271
277
  end
272
278
  else
273
279
  call = fn.method(:call)
274
280
 
275
281
  if call.owner == fn.class
276
- yield "#{ fn.class.to_s }#call"
282
+ yield "#{fn.class}#call"
277
283
  else
278
- yield "#{ fn.to_s }.call"
284
+ yield "#{fn}.call"
279
285
  end
280
286
  end
281
287
  end
282
288
 
283
- def visit_options(options, meta)
289
+ def visit_options(options, meta = EMPTY_HASH)
284
290
  if options.empty? && meta.empty?
285
- yield ""
291
+ yield ''
286
292
  else
287
- opts = options.empty? ? "" : " options=#{ options.inspect }"
293
+ opts = options.empty? ? '' : " options=#{options.inspect}"
288
294
 
289
295
  if meta.empty?
290
296
  yield opts
@@ -292,13 +298,13 @@ module Dry
292
298
  values = meta.map do |key, value|
293
299
  case key
294
300
  when Symbol
295
- "#{ key }: #{ value.inspect }"
301
+ "#{key}: #{value.inspect}"
296
302
  else
297
- "#{ key.inspect }=>#{ value.inspect }"
303
+ "#{key.inspect}=>#{value.inspect}"
298
304
  end
299
305
  end
300
306
 
301
- yield "#{ opts } meta={#{ values.join(", ") }}"
307
+ yield "#{opts} meta={#{values.join(', ')}}"
302
308
  end
303
309
  end
304
310
  end