dry-types 1.5.1 → 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/LICENSE +1 -1
  4. data/README.md +7 -13
  5. data/dry-types.gemspec +19 -18
  6. data/lib/dry/types/array/constructor.rb +0 -2
  7. data/lib/dry/types/array/member.rb +1 -3
  8. data/lib/dry/types/array.rb +0 -3
  9. data/lib/dry/types/builder.rb +43 -14
  10. data/lib/dry/types/builder_methods.rb +1 -1
  11. data/lib/dry/types/coercions/params.rb +4 -3
  12. data/lib/dry/types/compat.rb +1 -0
  13. data/lib/dry/types/compiler.rb +1 -3
  14. data/lib/dry/types/composition.rb +152 -0
  15. data/lib/dry/types/constrained.rb +0 -5
  16. data/lib/dry/types/constraints.rb +3 -7
  17. data/lib/dry/types/constructor/function.rb +8 -9
  18. data/lib/dry/types/constructor.rb +4 -10
  19. data/lib/dry/types/container.rb +1 -3
  20. data/lib/dry/types/core.rb +2 -3
  21. data/lib/dry/types/decorator.rb +0 -2
  22. data/lib/dry/types/default.rb +3 -6
  23. data/lib/dry/types/enum.rb +0 -3
  24. data/lib/dry/types/errors.rb +13 -1
  25. data/lib/dry/types/extensions/maybe.rb +8 -5
  26. data/lib/dry/types/extensions/monads.rb +7 -2
  27. data/lib/dry/types/fn_container.rb +0 -2
  28. data/lib/dry/types/hash/constructor.rb +2 -4
  29. data/lib/dry/types/hash.rb +1 -6
  30. data/lib/dry/types/implication.rb +66 -0
  31. data/lib/dry/types/intersection.rb +108 -0
  32. data/lib/dry/types/lax.rb +1 -4
  33. data/lib/dry/types/map.rb +9 -3
  34. data/lib/dry/types/module.rb +18 -9
  35. data/lib/dry/types/nominal.rb +2 -13
  36. data/lib/dry/types/predicate_inferrer.rb +8 -9
  37. data/lib/dry/types/predicate_registry.rb +7 -7
  38. data/lib/dry/types/primitive_inferrer.rb +0 -2
  39. data/lib/dry/types/printer/composition.rb +44 -0
  40. data/lib/dry/types/printer.rb +116 -131
  41. data/lib/dry/types/result.rb +0 -2
  42. data/lib/dry/types/schema/key.rb +1 -4
  43. data/lib/dry/types/schema.rb +6 -4
  44. data/lib/dry/types/sum.rb +3 -95
  45. data/lib/dry/types/type.rb +1 -3
  46. data/lib/dry/types/version.rb +1 -1
  47. data/lib/dry/types.rb +49 -22
  48. metadata +32 -46
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/options"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # Common API for types
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
- require "dry/types/decorator"
5
-
6
3
  module Dry
7
4
  module Types
8
5
  # Default types are useful when a missing value should be replaced by a default one
@@ -53,7 +50,7 @@ module Dry
53
50
  # @param [Object] value
54
51
  #
55
52
  # @api private
56
- def initialize(type, value, **options)
53
+ def initialize(type, value, **)
57
54
  super
58
55
  @value = value
59
56
  end
@@ -65,8 +62,8 @@ module Dry
65
62
  # @return [Default]
66
63
  #
67
64
  # @api public
68
- def constrained(*args)
69
- type.constrained(*args).default(value)
65
+ def constrained(...)
66
+ type.constrained(...).default(value)
70
67
  end
71
68
 
72
69
  # @return [true]
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
- require "dry/types/decorator"
5
-
6
3
  module Dry
7
4
  module Types
8
5
  # Enum types can be used to define an enum on top of an existing type
@@ -51,6 +51,7 @@ module Dry
51
51
 
52
52
  # @param [Array<CoercionError>] errors
53
53
  def initialize(errors)
54
+ super("")
54
55
  @errors = errors
55
56
  end
56
57
 
@@ -66,11 +67,22 @@ module Dry
66
67
  end
67
68
 
68
69
  class SchemaError < CoercionError
70
+ # @return [String, Symbol]
71
+ attr_reader :key
72
+
73
+ # @return [Object]
74
+ attr_reader :value
75
+
69
76
  # @param [String,Symbol] key
70
77
  # @param [Object] value
71
78
  # @param [String, #to_s] result
72
79
  def initialize(key, value, result)
73
- super("#{value.inspect} (#{value.class}) has invalid type for :#{key} violates constraints (#{result} failed)")
80
+ @key = key
81
+ @value = value
82
+ super(
83
+ "#{value.inspect} (#{value.class}) has invalid type "\
84
+ "for :#{key} violates constraints (#{result} failed)"
85
+ )
74
86
  end
75
87
  end
76
88
 
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
- require "dry/monads/maybe"
5
- require "dry/types/decorator"
3
+ require "dry/monads"
4
+ require "dry/monads/version"
5
+
6
+ if Gem::Version.new(Dry::Monads::VERSION) < Gem::Version.new("1.5.0")
7
+ raise "dry-types requires dry-monads >= 1.5.0"
8
+ end
6
9
 
7
10
  module Dry
8
11
  module Types
@@ -15,7 +18,7 @@ module Dry
15
18
  include Decorator
16
19
  include Builder
17
20
  include Printable
18
- include ::Dry::Monads::Maybe::Mixin
21
+ include ::Dry::Monads[:maybe]
19
22
 
20
23
  # @param [Dry::Monads::Maybe, Object] input
21
24
  #
@@ -99,7 +102,7 @@ module Dry
99
102
  end
100
103
 
101
104
  # @api private
102
- class Schema::Key
105
+ class Schema::Key # rubocop:disable Style/ClassAndModuleChildren
103
106
  # @api private
104
107
  def maybe
105
108
  __new__(type.maybe)
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/monads/result"
3
+ require "dry/monads"
4
+ require "dry/monads/version"
5
+
6
+ if Gem::Version.new(Dry::Monads::VERSION) < Gem::Version.new("1.5.0")
7
+ raise "dry-types requires dry-monads >= 1.5.0"
8
+ end
4
9
 
5
10
  module Dry
6
11
  module Types
@@ -8,7 +13,7 @@ module Dry
8
13
  #
9
14
  # @api public
10
15
  class Result
11
- include Dry::Monads::Result::Mixin
16
+ include ::Dry::Monads[:result]
12
17
 
13
18
  # Turn result into a monad
14
19
  #
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/container"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # Internal container for constructor functions used by the built-in types
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/constructor"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # Hash type exposes additional APIs for working with schema hashes
@@ -24,8 +22,8 @@ module Dry
24
22
  # @see Dry::Types::Array#of
25
23
  #
26
24
  # @api public
27
- def schema(*args)
28
- type.schema(*args).constructor(fn, meta: meta)
25
+ def schema(...)
26
+ type.schema(...).constructor(fn, meta: meta)
29
27
  end
30
28
  end
31
29
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/hash/constructor"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # Hash types can be used to define maps and schemas
@@ -51,7 +49,7 @@ module Dry
51
49
  # @api private
52
50
  def weak(*)
53
51
  raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
54
- "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
52
+ "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/main/CHANGELOG.md"
55
53
  end
56
54
  alias_method :permissive, :weak
57
55
  alias_method :strict, :weak
@@ -132,6 +130,3 @@ module Dry
132
130
  end
133
131
  end
134
132
  end
135
-
136
- require "dry/types/schema/key"
137
- require "dry/types/schema"
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # Implication type
6
+ #
7
+ # @api public
8
+ class Implication
9
+ include Composition
10
+
11
+ def self.operator
12
+ :>
13
+ end
14
+
15
+ # @param [Object] input
16
+ #
17
+ # @return [Object]
18
+ #
19
+ # @api private
20
+ def call_unsafe(input)
21
+ if left.try(input).success?
22
+ right.call_unsafe(input)
23
+ else
24
+ input
25
+ end
26
+ end
27
+
28
+ # @param [Object] input
29
+ #
30
+ # @return [Object]
31
+ #
32
+ # @api private
33
+ def call_safe(input, &block)
34
+ if left.try(input).success?
35
+ right.call_safe(input, &block)
36
+ else
37
+ input
38
+ end
39
+ end
40
+
41
+ # @param [Object] input
42
+ #
43
+ # @api public
44
+ def try(input)
45
+ if left.try(input).success?
46
+ right.try(input)
47
+ else
48
+ Result::Success.new(input)
49
+ end
50
+ end
51
+
52
+ # @param [Object] value
53
+ #
54
+ # @return [Boolean]
55
+ #
56
+ # @api private
57
+ def primitive?(value)
58
+ if left.primitive?(value)
59
+ right.primitive?(value)
60
+ else
61
+ true
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+ require "dry/types/options"
5
+ require "dry/types/meta"
6
+
7
+ module Dry
8
+ module Types
9
+ # Intersection type
10
+ #
11
+ # @api public
12
+ class Intersection
13
+ include Composition
14
+
15
+ def self.operator
16
+ :&
17
+ end
18
+
19
+ # @param [Object] input
20
+ #
21
+ # @return [Object]
22
+ #
23
+ # @api private
24
+ def call_unsafe(input)
25
+ merge_results(left.call_unsafe(input), right.call_unsafe(input))
26
+ end
27
+
28
+ # @param [Object] input
29
+ #
30
+ # @return [Object]
31
+ #
32
+ # @api private
33
+ def call_safe(input, &block)
34
+ try_sides(input, &block).input
35
+ end
36
+
37
+ # @param [Object] input
38
+ #
39
+ # @api public
40
+ def try(input)
41
+ try_sides(input) do |failure|
42
+ if block_given?
43
+ yield(failure)
44
+ else
45
+ failure
46
+ end
47
+ end
48
+ end
49
+
50
+ # @param [Object] value
51
+ #
52
+ # @return [Boolean]
53
+ #
54
+ # @api private
55
+ def primitive?(value)
56
+ left.primitive?(value) && right.primitive?(value)
57
+ end
58
+
59
+ private
60
+
61
+ # @api private
62
+ def try_sides(input, &block)
63
+ results = []
64
+
65
+ [left, right].each do |side|
66
+ result = try_side(side, input, &block)
67
+ return result if result.failure?
68
+
69
+ results << result
70
+ end
71
+
72
+ Result::Success.new(merge_results(*results.map(&:input)))
73
+ end
74
+
75
+ # @api private
76
+ def try_side(side, input)
77
+ failure = nil
78
+
79
+ result = side.try(input) do |f|
80
+ failure = f
81
+ yield(f)
82
+ end
83
+
84
+ if result.is_a?(Result)
85
+ result
86
+ elsif failure
87
+ Result::Failure.new(result, failure)
88
+ else
89
+ Result::Success.new(result)
90
+ end
91
+ end
92
+
93
+ # @api private
94
+ def merge_results(left_result, right_result)
95
+ case left_result
96
+ when ::Array
97
+ left_result
98
+ .zip(right_result)
99
+ .map { |lhs, rhs| merge_results(lhs, rhs) }
100
+ when ::Hash
101
+ left_result.merge(right_result)
102
+ else
103
+ left_result
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
data/lib/dry/types/lax.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/deprecations"
4
- require "dry/types/decorator"
5
-
6
3
  module Dry
7
4
  module Types
8
5
  # Lax types rescue from type-related errors when constructors fail
@@ -68,7 +65,7 @@ module Dry
68
65
  end
69
66
  end
70
67
 
71
- extend ::Dry::Core::Deprecations[:'dry-types']
68
+ extend ::Dry::Core::Deprecations[:"dry-types"]
72
69
  Safe = Lax
73
70
  deprecate_constant(:Safe)
74
71
  end
data/lib/dry/types/map.rb CHANGED
@@ -14,15 +14,17 @@ module Dry
14
14
  # # => {1 => 'right'}
15
15
  #
16
16
  # type.('1' => 'wrong')
17
- # # Dry::Types::MapError: "1" violates constraints (type?(Integer, "1") AND gteq?(1, "1") AND lteq?(10, "1") failed)
17
+ # # Dry::Types::MapError: "1" violates constraints (type?(Integer, "1")
18
+ # # AND gteq?(1, "1")
19
+ # # AND lteq?(10, "1") failed)
18
20
  #
19
21
  # type.(11 => 'wrong')
20
22
  # # Dry::Types::MapError: 11 violates constraints (lteq?(10, 11) failed)
21
23
  #
22
24
  # @api public
23
25
  class Map < Nominal
24
- def initialize(_primitive, key_type: Types["any"], value_type: Types["any"], meta: EMPTY_HASH)
25
- super(_primitive, key_type: key_type, value_type: value_type, meta: meta)
26
+ def initialize(primitive, key_type: Types["any"], value_type: Types["any"], meta: EMPTY_HASH)
27
+ super(primitive, key_type: key_type, value_type: value_type, meta: meta)
26
28
  end
27
29
 
28
30
  # @return [Type]
@@ -100,6 +102,8 @@ module Dry
100
102
  private
101
103
 
102
104
  # @api private
105
+ # rubocop:disable Metrics/PerceivedComplexity
106
+ # rubocop:disable Metrics/AbcSize
103
107
  def coerce(input)
104
108
  unless primitive?(input)
105
109
  return failure(
@@ -131,6 +135,8 @@ module Dry
131
135
  failure(input, MultipleError.new(failures))
132
136
  end
133
137
  end
138
+ # rubocop:enable Metrics/PerceivedComplexity
139
+ # rubocop:enable Metrics/AbcSize
134
140
  end
135
141
  end
136
142
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/deprecations"
4
3
  require "dry/types/builder_methods"
5
4
 
6
5
  module Dry
@@ -8,7 +7,7 @@ module Dry
8
7
  # Export types registered in a container as module constants.
9
8
  # @example
10
9
  # module Types
11
- # include Dry::Types(:strict, :coercible, :nominal, default: :strict)
10
+ # include Dry.Types(:strict, :coercible, :nominal, default: :strict)
12
11
  # end
13
12
  #
14
13
  # Types.constants
@@ -26,10 +25,10 @@ module Dry
26
25
  extend(BuilderMethods)
27
26
 
28
27
  if constants.key?(:Nominal)
29
- singleton_class.send(:define_method, :included) do |base|
28
+ singleton_class.define_method(:included) do |base|
30
29
  super(base)
31
30
  base.instance_exec(const_get(:Nominal, false)) do |nominal|
32
- extend Dry::Core::Deprecations[:'dry-types']
31
+ extend Dry::Core::Deprecations[:"dry-types"]
33
32
  const_set(:Definition, nominal)
34
33
  deprecate_constant(:Definition, message: "Nominal")
35
34
  end
@@ -38,13 +37,16 @@ module Dry
38
37
  end
39
38
 
40
39
  # @api private
40
+ # rubocop:disable Metrics/AbcSize
41
+ # rubocop:disable Metrics/CyclomaticComplexity
42
+ # rubocop:disable Metrics/PerceivedComplexity
41
43
  def type_constants(*namespaces, default: Undefined, **aliases)
42
44
  if namespaces.empty? && aliases.empty? && Undefined.equal?(default)
43
45
  default_ns = :Strict
44
46
  elsif Undefined.equal?(default)
45
47
  default_ns = Undefined
46
48
  else
47
- default_ns = Inflector.camelize(default).to_sym
49
+ default_ns = Types::Inflector.camelize(default).to_sym
48
50
  end
49
51
 
50
52
  tree = registry_tree
@@ -52,7 +54,9 @@ module Dry
52
54
  if namespaces.empty? && aliases.empty?
53
55
  modules = tree.select { |_, v| v.is_a?(::Hash) }.map(&:first)
54
56
  else
55
- modules = (namespaces + aliases.keys).map { |n| Inflector.camelize(n).to_sym }
57
+ modules = (namespaces + aliases.keys).map { |n|
58
+ Types::Inflector.camelize(n).to_sym
59
+ }
56
60
  end
57
61
 
58
62
  tree.each_with_object({}) do |(key, value), constants|
@@ -64,13 +68,16 @@ module Dry
64
68
  constants.update(value) if key == default_ns
65
69
  end
66
70
  end
71
+ # rubocop:enable Metrics/AbcSize
72
+ # rubocop:enable Metrics/CyclomaticComplexity
73
+ # rubocop:enable Metrics/PerceivedComplexity
67
74
 
68
75
  # @api private
69
76
  def registry_tree
70
77
  @registry_tree ||= @registry.keys.each_with_object({}) { |key, tree|
71
78
  type = @registry[key]
72
79
  *modules, const_name = key.split(".").map { |part|
73
- Inflector.camelize(part).to_sym
80
+ Types::Inflector.camelize(part).to_sym
74
81
  }
75
82
  next if modules.empty?
76
83
 
@@ -91,9 +98,11 @@ module Dry
91
98
  ns.to_sym unless path.empty?
92
99
  }.compact.uniq
93
100
 
94
- (referenced.uniq - known).each do |name|
101
+ unknown = (referenced.uniq - known).first
102
+
103
+ if unknown
95
104
  raise ArgumentError,
96
- "#{name.inspect} is not a known type namespace. "\
105
+ "#{unknown.inspect} is not a known type namespace. "\
97
106
  "Supported options are #{known.map(&:inspect).join(", ")}"
98
107
  end
99
108
  end
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/deprecations"
4
- require "dry/core/equalizer"
5
- require "dry/types/builder"
6
- require "dry/types/result"
7
- require "dry/types/options"
8
- require "dry/types/meta"
9
-
10
3
  module Dry
11
4
  module Types
12
5
  # Nominal types define a primitive class and do not apply any constructors or constraints
@@ -20,7 +13,7 @@ module Dry
20
13
  include Meta
21
14
  include Builder
22
15
  include Printable
23
- include Dry::Equalizer(:primitive, :options, :meta, inspect: false, immutable: true)
16
+ include ::Dry::Equalizer(:primitive, :options, :meta, inspect: false, immutable: true)
24
17
 
25
18
  # @return [Class]
26
19
  attr_reader :primitive
@@ -199,12 +192,8 @@ module Dry
199
192
  end
200
193
  end
201
194
 
202
- extend Dry::Core::Deprecations[:'dry-types']
195
+ extend ::Dry::Core::Deprecations[:"dry-types"]
203
196
  Definition = Nominal
204
197
  deprecate_constant(:Definition, message: "Nominal")
205
198
  end
206
199
  end
207
-
208
- require "dry/types/array"
209
- require "dry/types/hash"
210
- require "dry/types/map"
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/cache"
4
- require "dry/core/class_attributes"
5
- require "dry/types/predicate_registry"
6
-
7
3
  module Dry
8
4
  module Types
9
5
  # PredicateInferrer returns the list of predicates used by a type.
@@ -55,7 +51,7 @@ module Dry
55
51
  end
56
52
 
57
53
  # @api private
58
- def infer_predicate(type)
54
+ def infer_predicate(type) # rubocop:disable Metrics/PerceivedComplexity
59
55
  pred = TYPE_TO_PREDICATE.fetch(type) do
60
56
  if type.name.nil? || self.class.infer_predicate_by_class_name.equal?(false)
61
57
  nil
@@ -175,9 +171,9 @@ module Dry
175
171
  def visit_predicate(node)
176
172
  pred, args = node
177
173
 
178
- if pred.equal?(:type?)
174
+ if pred.equal?(:type?) || !registry.key?(pred)
179
175
  EMPTY_ARRAY
180
- elsif registry.key?(pred)
176
+ else
181
177
  *curried, _ = args
182
178
  values = curried.map { |_, v| v }
183
179
 
@@ -186,11 +182,14 @@ module Dry
186
182
  else
187
183
  [pred => values[0]]
188
184
  end
189
- else
190
- EMPTY_ARRAY
191
185
  end
192
186
  end
193
187
 
188
+ # @api private
189
+ def visit_map(_node)
190
+ raise NotImplementedError, "map types are not supported yet"
191
+ end
192
+
194
193
  private
195
194
 
196
195
  # @api private
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/logic/predicates"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # A registry with predicate objects from `Dry::Logic::Predicates`
@@ -14,20 +12,22 @@ module Dry
14
12
  # @api private
15
13
  attr_reader :has_predicate
16
14
 
15
+ KERNEL_RESPOND_TO = ::Kernel.instance_method(:respond_to?)
16
+ private_constant(:KERNEL_RESPOND_TO)
17
+
17
18
  # @api private
18
19
  def initialize(predicates = Logic::Predicates)
19
20
  @predicates = predicates
20
- @has_predicate = ::Kernel.instance_method(:respond_to?).bind(@predicates)
21
21
  end
22
22
 
23
23
  # @api private
24
- def [](name)
25
- predicates[name]
24
+ def key?(name)
25
+ KERNEL_RESPOND_TO.bind_call(@predicates, name)
26
26
  end
27
27
 
28
28
  # @api private
29
- def key?(name)
30
- has_predicate.(name)
29
+ def [](name)
30
+ predicates[name]
31
31
  end
32
32
  end
33
33
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/cache"
4
-
5
3
  module Dry
6
4
  module Types
7
5
  # PrimitiveInferrer returns the list of classes matching a type.
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ # @api private
6
+ class Printer
7
+ # @api private
8
+ class Composition
9
+ def initialize(printer, composition_class)
10
+ @printer = printer
11
+ @composition_class = composition_class
12
+ freeze
13
+ end
14
+
15
+ def visit(composition)
16
+ visit_constructors(composition) do |constructors|
17
+ @printer.visit_options(composition.options, composition.meta) do |opts|
18
+ yield "#{@composition_class.composition_name}<#{constructors}#{opts}>"
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def visit_constructors(composition)
26
+ visit_constructor(composition.left) do |left|
27
+ visit_constructor(composition.right) do |right|
28
+ yield "#{left} #{@composition_class.operator} #{right}"
29
+ end
30
+ end
31
+ end
32
+
33
+ def visit_constructor(type, &block)
34
+ case type
35
+ when @composition_class
36
+ visit_constructors(type, &block)
37
+ else
38
+ @printer.visit(type, &block)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end