sober_swag 0.17.0 → 0.21.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/benchmark.yml +39 -0
- data/.github/workflows/lint.yml +2 -4
- data/.github/workflows/ruby.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +5 -1
- data/.yardopts +7 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +12 -0
- data/README.md +1 -1
- data/bench/benchmark.rb +34 -0
- data/bench/benchmarks/basic_field_serializer.rb +21 -0
- data/bench/benchmarks/view_selection.rb +47 -0
- data/docs/serializers.md +4 -1
- data/example/Gemfile +2 -2
- data/example/Gemfile.lock +46 -44
- data/example/config/environments/production.rb +1 -1
- data/lib/sober_swag/compiler/path.rb +42 -3
- data/lib/sober_swag/compiler/paths.rb +20 -0
- data/lib/sober_swag/compiler/primitive.rb +20 -1
- data/lib/sober_swag/compiler/type.rb +105 -22
- data/lib/sober_swag/compiler.rb +29 -3
- data/lib/sober_swag/controller/route.rb +103 -20
- data/lib/sober_swag/controller.rb +39 -12
- data/lib/sober_swag/input_object.rb +124 -7
- data/lib/sober_swag/nodes/array.rb +19 -0
- data/lib/sober_swag/nodes/attribute.rb +45 -4
- data/lib/sober_swag/nodes/base.rb +27 -7
- data/lib/sober_swag/nodes/binary.rb +30 -13
- data/lib/sober_swag/nodes/enum.rb +16 -1
- data/lib/sober_swag/nodes/list.rb +20 -0
- data/lib/sober_swag/nodes/nullable_primitive.rb +3 -0
- data/lib/sober_swag/nodes/object.rb +4 -1
- data/lib/sober_swag/nodes/one_of.rb +11 -3
- data/lib/sober_swag/nodes/primitive.rb +34 -2
- data/lib/sober_swag/nodes/sum.rb +8 -0
- data/lib/sober_swag/output_object/definition.rb +57 -1
- data/lib/sober_swag/output_object/field.rb +31 -11
- data/lib/sober_swag/output_object/field_syntax.rb +19 -3
- data/lib/sober_swag/output_object/view.rb +46 -1
- data/lib/sober_swag/output_object.rb +40 -19
- data/lib/sober_swag/parser.rb +7 -1
- data/lib/sober_swag/serializer/array.rb +27 -3
- data/lib/sober_swag/serializer/base.rb +75 -25
- data/lib/sober_swag/serializer/conditional.rb +33 -1
- data/lib/sober_swag/serializer/field_list.rb +23 -5
- data/lib/sober_swag/serializer/hash.rb +53 -0
- data/lib/sober_swag/serializer/mapped.rb +10 -1
- data/lib/sober_swag/serializer/optional.rb +18 -1
- data/lib/sober_swag/serializer/primitive.rb +3 -0
- data/lib/sober_swag/serializer.rb +1 -0
- data/lib/sober_swag/server.rb +27 -11
- data/lib/sober_swag/type/named.rb +14 -0
- data/lib/sober_swag/types/comma_array.rb +4 -0
- data/lib/sober_swag/version.rb +1 -1
- data/lib/sober_swag.rb +6 -1
- metadata +9 -2
data/lib/sober_swag/nodes/sum.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
module SoberSwag
|
2
2
|
module Nodes
|
3
|
+
##
|
4
|
+
# A "Sum" type represents either one type or the other.
|
5
|
+
#
|
6
|
+
# It is called "Sum" because, if a type can be either type `A` or type `B`,
|
7
|
+
# the number of possible values for the type of `number_of_values(A) + number_of_values(B)`.
|
8
|
+
#
|
9
|
+
# Internally, this is primarily used when an object can be either one type or another.
|
10
|
+
# It will latter be flattened into {SoberSwag::Nodes::OneOf}
|
3
11
|
class Sum < Binary
|
4
12
|
end
|
5
13
|
end
|
@@ -9,14 +9,56 @@ module SoberSwag
|
|
9
9
|
@views = []
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
##
|
13
|
+
# @return [Array<SoberSwag::OutputObject::Field>]
|
14
|
+
attr_reader :fields
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [Array<SoberSwag::OutputObject::View>]
|
18
|
+
attr_reader :views
|
13
19
|
|
14
20
|
include FieldSyntax
|
15
21
|
|
22
|
+
##
|
23
|
+
# Adds a "type key", which is basically a field that will be
|
24
|
+
# serialized out to a constant value.
|
25
|
+
#
|
26
|
+
# This is useful if you have multiple types you may want to serialize out, and want consumers of your API to distinguish between them.
|
27
|
+
#
|
28
|
+
# By default this will have the key "type" but you can set it with the keyword arg.
|
29
|
+
# ```ruby
|
30
|
+
# type_key('MyObject')
|
31
|
+
# # is equivalent to
|
32
|
+
# field :type, SoberSwag::Serializer::Primitive.new(SoberSwag::Types::String.enum('MyObject')) do |_, _|
|
33
|
+
# 'MyObject'
|
34
|
+
# end
|
35
|
+
# ```
|
36
|
+
# @param str [String, Symbol] the value to serialize (will be converted to a string)
|
37
|
+
# @param
|
38
|
+
def type_key(str, field_name: :type)
|
39
|
+
str = str.to_s
|
40
|
+
field(
|
41
|
+
field_name,
|
42
|
+
SoberSwag::Serializer::Primitive.new(
|
43
|
+
SoberSwag::Types::String.enum(str)
|
44
|
+
)
|
45
|
+
) { |_, _| str }
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Adds a new field to the fields array
|
50
|
+
# @param field [SoberSwag::OutputObject::Field]
|
16
51
|
def add_field!(field)
|
17
52
|
@fields << field
|
18
53
|
end
|
19
54
|
|
55
|
+
##
|
56
|
+
# Define a new view, with the view DSL
|
57
|
+
# @param name [Symbol] the name of the view
|
58
|
+
# @param inherits [Symbol] the name of another view this
|
59
|
+
# view will "inherit" from
|
60
|
+
# @yieldself [SoberSwag::OutputObject::View]
|
61
|
+
# @return [nil] nothing interesting.
|
20
62
|
def view(name, inherits: nil, &block)
|
21
63
|
initial_fields =
|
22
64
|
if inherits.nil? || inherits == :base
|
@@ -31,6 +73,14 @@ module SoberSwag
|
|
31
73
|
@views << view
|
32
74
|
end
|
33
75
|
|
76
|
+
##
|
77
|
+
# @overload identifier()
|
78
|
+
# Get the identifier of this output object.
|
79
|
+
# @return [String] the identifier
|
80
|
+
# @overload identifier(arg)
|
81
|
+
# Set the identifier of this output object.
|
82
|
+
# @param arg [String] the external identifier to use for this OutputObject
|
83
|
+
# @return [String] `arg`
|
34
84
|
def identifier(arg = nil)
|
35
85
|
@identifier = arg if arg
|
36
86
|
@identifier
|
@@ -38,6 +88,12 @@ module SoberSwag
|
|
38
88
|
|
39
89
|
private
|
40
90
|
|
91
|
+
##
|
92
|
+
# Get the already-defined view with a specific name.
|
93
|
+
#
|
94
|
+
# @param name [Symbol] name of view to look up
|
95
|
+
# @return [SoberSwag::OutputObject::View] the view found
|
96
|
+
# @raise [ArgumentError] if no view with that name found
|
41
97
|
def find_view(name)
|
42
98
|
@views.find { |view| view.name == name } || (raise ArgumentError, "no view #{name.inspect} defined!")
|
43
99
|
end
|
@@ -4,6 +4,18 @@ module SoberSwag
|
|
4
4
|
# A single field in an output object.
|
5
5
|
# Later used to make an actual serializer from this.
|
6
6
|
class Field
|
7
|
+
##
|
8
|
+
# @param name [Symbol] the name of this field
|
9
|
+
# @param serializer [SoberSwag::Serializer::Base, Proc, Lambda] how to serialize
|
10
|
+
# the value in this field.
|
11
|
+
# If given a `Proc` or `Lambda`, the `Proc` or `Lambda` should return
|
12
|
+
# an instance of SoberSwag::Serializer::Base when called.
|
13
|
+
# @param from [Symbol] an optional parameter specifying
|
14
|
+
# that this field should be plucked "from" another
|
15
|
+
# attribute of a ruby object
|
16
|
+
# @param block [Proc] a proc to get this field from a serialized
|
17
|
+
# object. If not given, will try to grab an attribute
|
18
|
+
# with the same name, *or* with the name of `from:` if that was sent.
|
7
19
|
def initialize(name, serializer, from: nil, &block)
|
8
20
|
@name = name
|
9
21
|
@root_serializer = serializer
|
@@ -11,12 +23,18 @@ module SoberSwag
|
|
11
23
|
@block = block
|
12
24
|
end
|
13
25
|
|
26
|
+
##
|
27
|
+
# @return [Symbol] name of this field.
|
14
28
|
attr_reader :name
|
15
29
|
|
30
|
+
##
|
31
|
+
# @return [SoberSwag::Serializer::Base]
|
16
32
|
def serializer
|
17
33
|
@serializer ||= resolved_serializer.serializer.via_map(&transform_proc)
|
18
34
|
end
|
19
35
|
|
36
|
+
##
|
37
|
+
# @return [SoberSwag::Serializer::Base]
|
20
38
|
def resolved_serializer
|
21
39
|
if @root_serializer.is_a?(Proc)
|
22
40
|
@root_serializer.call
|
@@ -27,17 +45,19 @@ module SoberSwag
|
|
27
45
|
|
28
46
|
private
|
29
47
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
##
|
49
|
+
# @return [Proc]
|
50
|
+
def transform_proc
|
51
|
+
return @transform_proc if defined?(@transform_proc)
|
52
|
+
|
53
|
+
return @transform_proc = @block if @block
|
54
|
+
|
55
|
+
key = @from || @name
|
56
|
+
@transform_proc = proc do |object, _|
|
57
|
+
if object.respond_to?(key)
|
58
|
+
object.public_send(key)
|
59
|
+
else
|
60
|
+
object[key]
|
41
61
|
end
|
42
62
|
end
|
43
63
|
end
|
@@ -3,6 +3,13 @@ module SoberSwag
|
|
3
3
|
##
|
4
4
|
# Syntax for definitions that can add fields.
|
5
5
|
module FieldSyntax
|
6
|
+
##
|
7
|
+
# Defines a new field.
|
8
|
+
# @see SoberSwag::OutputObject::Field#initialize
|
9
|
+
# @param name [Symbol] name of this field
|
10
|
+
# @param serializer [SoberSwag::Serializer::Base] serializer to use for this field.
|
11
|
+
# @param from [Symbol] method name to extract this field from, for convenience.
|
12
|
+
# @param block [Proc] optional way to extract this field.
|
6
13
|
def field(name, serializer, from: nil, &block)
|
7
14
|
add_field!(Field.new(name, serializer, from: from, &block))
|
8
15
|
end
|
@@ -10,22 +17,31 @@ module SoberSwag
|
|
10
17
|
##
|
11
18
|
# Similar to #field, but adds multiple at once.
|
12
19
|
# Named #multi because #fields was already taken.
|
20
|
+
#
|
21
|
+
# @param names [Array<Symbol>] the field names to add.
|
22
|
+
# @param serializer [SoberSwag::Serializer::Base] the serializer to use for all fields.
|
13
23
|
def multi(names, serializer)
|
14
24
|
names.each { |name| field(name, serializer) }
|
15
25
|
end
|
16
26
|
|
17
27
|
##
|
18
28
|
# Given a symbol to this, we will use a primitive name
|
29
|
+
# @param name [Symbol] symbol to look up.
|
30
|
+
# @return [SoberSwag::Serializer::Base] serializer to use.
|
19
31
|
def primitive(name)
|
20
32
|
SoberSwag::Serializer.primitive(SoberSwag::Types.const_get(name))
|
21
33
|
end
|
22
34
|
|
23
35
|
##
|
24
36
|
# Merge in anything that has a list of fields, and use it.
|
25
|
-
# Note that merging in a full
|
26
|
-
|
37
|
+
# Note that merging in a full output object *will not* also merge in views, just fields defined on the base.
|
38
|
+
#
|
39
|
+
# @param other [#fields] a field container, like a {SoberSwag::OutputObject} or something
|
40
|
+
# @param except [Array<Symbol>] optionally exclude a field from the output object being merged
|
41
|
+
# @return [void]
|
42
|
+
def merge(other, except: [])
|
27
43
|
other.fields.each do |field|
|
28
|
-
add_field!(field)
|
44
|
+
add_field!(field) unless except.include?(field.name)
|
29
45
|
end
|
30
46
|
end
|
31
47
|
end
|
@@ -3,44 +3,87 @@ module SoberSwag
|
|
3
3
|
##
|
4
4
|
# DSL for defining a view.
|
5
5
|
# Used in `view` blocks within {SoberSwag::OutputObject.define}.
|
6
|
+
#
|
7
|
+
# Views are "variants" of {SoberSwag::OutputObject}s that contain
|
8
|
+
# different fields.
|
6
9
|
class View < SoberSwag::Serializer::Base
|
10
|
+
##
|
11
|
+
# Define a new view with the given base fields.
|
12
|
+
# @param name [Symbol] name for this view
|
13
|
+
# @param base_fields [Array<SoberSwag::OutputObject::Field>] fields already defined
|
14
|
+
# @yieldself [SoberSwag::OutputObject::View]
|
15
|
+
#
|
16
|
+
# @return [SoberSwag::OutputObject::View]
|
7
17
|
def self.define(name, base_fields, &block)
|
8
18
|
new(name, base_fields).tap do |view|
|
9
19
|
view.instance_eval(&block)
|
10
20
|
end
|
11
21
|
end
|
12
22
|
|
23
|
+
##
|
24
|
+
# An error thrown when you try to nest views inside views.
|
13
25
|
class NestingError < Error; end
|
14
26
|
|
15
27
|
include FieldSyntax
|
16
28
|
|
29
|
+
##
|
30
|
+
# @param name [Sybmol] name for this view.
|
31
|
+
# @param base_fields [Array<SoberSwag::OutputObject::Field>] already-defined fields.
|
17
32
|
def initialize(name, base_fields = [])
|
18
33
|
@name = name
|
19
34
|
@fields = base_fields.dup
|
20
35
|
end
|
21
36
|
|
22
|
-
|
37
|
+
##
|
38
|
+
# @return [Symbol] the name of this view
|
39
|
+
attr_reader :name
|
40
|
+
|
41
|
+
##
|
42
|
+
# @return [Array<SoberSwag::OutputObject::Fields>] the fields defined in this view.
|
43
|
+
attr_reader :fields
|
23
44
|
|
45
|
+
##
|
46
|
+
# Serialize an object according to this view.
|
47
|
+
# @param object what to serialize
|
48
|
+
# @param opts [Hash] arbitrary options
|
49
|
+
# @return [Hash] the serialized result
|
24
50
|
def serialize(obj, opts = {})
|
25
51
|
serializer.serialize(obj, opts)
|
26
52
|
end
|
27
53
|
|
54
|
+
##
|
55
|
+
# Get the type of this view.
|
56
|
+
# @return [Class] the type, a subclass of {Dry::Struct}
|
28
57
|
def type
|
29
58
|
serializer.type
|
30
59
|
end
|
31
60
|
|
61
|
+
##
|
62
|
+
# Excludes a field with the given name from this view.
|
63
|
+
# @param name [Symbol] field to exclude.
|
64
|
+
# @return [nil] nothing interesting
|
32
65
|
def except!(name)
|
33
66
|
@fields.reject! { |f| f.name == name }
|
34
67
|
end
|
35
68
|
|
69
|
+
##
|
70
|
+
# Always raises {NestingError}
|
71
|
+
# @raise {NestingError} always
|
36
72
|
def view(*)
|
37
73
|
raise NestingError, 'no views in views'
|
38
74
|
end
|
39
75
|
|
76
|
+
##
|
77
|
+
# Adds a field do this view.
|
78
|
+
# @param field [SoberSwag::OutputObject::Field]
|
79
|
+
# @return [nil] nothing interesting
|
40
80
|
def add_field!(field)
|
41
81
|
@fields << field
|
42
82
|
end
|
43
83
|
|
84
|
+
##
|
85
|
+
# Pretty show for humans
|
86
|
+
# @return [String]
|
44
87
|
def to_s
|
45
88
|
"<SoberSwag::OutputObject::View(#{identifier})>"
|
46
89
|
end
|
@@ -48,6 +91,8 @@ module SoberSwag
|
|
48
91
|
##
|
49
92
|
# Get the serializer defined by this view.
|
50
93
|
# WARNING: Don't add more fields after you call this.
|
94
|
+
#
|
95
|
+
# @return [SoberSwag::Serializer::FieldList]
|
51
96
|
def serializer
|
52
97
|
@serializer ||=
|
53
98
|
SoberSwag::Serializer::FieldList.new(fields).tap { |s| s.identifier(identifier) }
|
@@ -5,7 +5,7 @@ module SoberSwag
|
|
5
5
|
# Create a serializer that is heavily inspired by the "Blueprinter" library.
|
6
6
|
# This allows you to make "views" and such inside.
|
7
7
|
#
|
8
|
-
# Under the hood, this is actually all based on {SoberSwag::
|
8
|
+
# Under the hood, this is actually all based on {SoberSwag::Serializer::Base}.
|
9
9
|
class OutputObject < SoberSwag::Serializer::Base
|
10
10
|
autoload(:Field, 'sober_swag/output_object/field')
|
11
11
|
autoload(:Definition, 'sober_swag/output_object/definition')
|
@@ -20,7 +20,7 @@ module SoberSwag
|
|
20
20
|
#
|
21
21
|
# PersonSerializer = SoberSwag::OutputObject.define do
|
22
22
|
# field :id, primitive(:Integer)
|
23
|
-
# field :name,
|
23
|
+
# field :name, primitive(:String).optional
|
24
24
|
#
|
25
25
|
# view :complex do
|
26
26
|
# field :age, primitive(:Integer)
|
@@ -32,9 +32,12 @@ module SoberSwag
|
|
32
32
|
# However, this is only a hack to get rid of the weird naming issue when
|
33
33
|
# generating swagger from dry structs: their section of the schema area
|
34
34
|
# is defined by their *Ruby Class Name*. In the future, if we get rid of this,
|
35
|
-
# we might be able to keep this on the value-level, in which case {
|
35
|
+
# we might be able to keep this on the value-level, in which case {.define}
|
36
36
|
# can simply return an *instance* of SoberSwag::Serializer that does
|
37
37
|
# the correct thing, with the name you give it. This works for now, though.
|
38
|
+
#
|
39
|
+
# @return [Class] the serializer generated.
|
40
|
+
# @yieldself [SoberSwag::OutputObject::Definition]
|
38
41
|
def self.define(&block)
|
39
42
|
d = Definition.new.tap do |o|
|
40
43
|
o.instance_eval(&block)
|
@@ -42,28 +45,51 @@ module SoberSwag
|
|
42
45
|
new(d.fields, d.views, d.identifier)
|
43
46
|
end
|
44
47
|
|
48
|
+
##
|
49
|
+
# @param fields [Array<SoberSwag::OutputObject::Field>] the fields for this OutputObject
|
50
|
+
# @param views [Array<SoberSwag::OutputObject::View>] the views for this OutputObject
|
51
|
+
# @param identifier [String] the external identifier for this OutputObject
|
45
52
|
def initialize(fields, views, identifier)
|
46
53
|
@fields = fields
|
47
54
|
@views = views
|
48
55
|
@identifier = identifier
|
49
56
|
end
|
50
57
|
|
51
|
-
|
58
|
+
##
|
59
|
+
# @return [Array<SoberSwag::OutputObject::Field>]
|
60
|
+
attr_reader :fields
|
61
|
+
##
|
62
|
+
# @return [Array<SoberSwag::OutputObject::View>]
|
63
|
+
attr_reader :views
|
64
|
+
##
|
65
|
+
# @return [String] the external ID to use for this object
|
66
|
+
attr_reader :identifier
|
52
67
|
|
68
|
+
##
|
69
|
+
# Perform serialization.
|
53
70
|
def serialize(obj, opts = {})
|
54
71
|
serializer.serialize(obj, opts)
|
55
72
|
end
|
56
73
|
|
74
|
+
##
|
75
|
+
# Get a Dry::Struct of the type this OutputObject will serialize to.
|
57
76
|
def type
|
58
77
|
serializer.type
|
59
78
|
end
|
60
79
|
|
80
|
+
##
|
81
|
+
# Get a serializer for a single view contained in this output object.
|
82
|
+
# Note: given `:base`, it will return a serializer for the base OutputObject
|
83
|
+
# @param name [Symbol] the name of the view
|
84
|
+
# @return [SoberSwag::Serializer::Base] the serializer
|
61
85
|
def view(name)
|
62
86
|
return base_serializer if name == :base
|
63
87
|
|
64
88
|
@views.find { |v| v.name == name }
|
65
89
|
end
|
66
90
|
|
91
|
+
##
|
92
|
+
# A serializer for the "base type" of this OutputObject, with no views.
|
67
93
|
def base
|
68
94
|
base_serializer
|
69
95
|
end
|
@@ -72,30 +98,25 @@ module SoberSwag
|
|
72
98
|
# Compile down this to an appropriate serializer.
|
73
99
|
# It uses {SoberSwag::Serializer::Conditional} to do view-parsing,
|
74
100
|
# and {SoberSwag::Serializer::FieldList} to do the actual serialization.
|
75
|
-
|
101
|
+
#
|
102
|
+
# @todo: optimize view selection to use binary instead of linear search
|
103
|
+
def serializer
|
76
104
|
@serializer ||=
|
77
105
|
begin
|
78
|
-
views.
|
79
|
-
|
80
|
-
|
81
|
-
proc do |object, options|
|
82
|
-
if options[:view].to_s == view.name.to_s
|
83
|
-
[:left, object]
|
84
|
-
else
|
85
|
-
[:right, object]
|
86
|
-
end
|
87
|
-
end,
|
88
|
-
view_serializer,
|
89
|
-
base
|
90
|
-
)
|
91
|
-
end
|
106
|
+
view_choices = views.map { |view| [view.name.to_s, view.serializer] }.to_h
|
107
|
+
view_choices['base'] = base_serializer
|
108
|
+
SoberSwag::Serializer::Hash.new(view_choices, base, proc { |_, options| options[:view]&.to_s })
|
92
109
|
end
|
93
110
|
end
|
94
111
|
|
112
|
+
##
|
113
|
+
# @return [String]
|
95
114
|
def to_s
|
96
115
|
"<SoberSwag::OutputObject(#{identifier})>"
|
97
116
|
end
|
98
117
|
|
118
|
+
##
|
119
|
+
# @return [SoberSwag::Serializer::FieldList] serializer for this output object.
|
99
120
|
def base_serializer
|
100
121
|
@base_serializer ||= SoberSwag::Serializer::FieldList.new(fields).tap do |s|
|
101
122
|
s.identifier(identifier)
|
data/lib/sober_swag/parser.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
module SoberSwag
|
2
2
|
##
|
3
3
|
# Parses a *Dry-Types Schema* into a set of nodes we can use to compile.
|
4
|
-
# This is mostly because the
|
4
|
+
# This is mostly because the visitor pattern sucks and catamorphisms are nice.
|
5
|
+
#
|
6
|
+
# Do not use this class directly, as it is not part of the public api.
|
7
|
+
# Instead, use classes from the {SoberSwag::Compiler} namespace.
|
5
8
|
class Parser
|
6
9
|
def initialize(node)
|
7
10
|
@node = node
|
8
11
|
@found = Set.new
|
9
12
|
end
|
10
13
|
|
14
|
+
##
|
15
|
+
# Compile to one of our internal nodes.
|
16
|
+
# @return [SoberSwag::Nodes::Base] the node that describes this type.
|
11
17
|
def to_syntax # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
12
18
|
case @node
|
13
19
|
when Dry::Types::Default
|