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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +15 -0
  3. data/.github/workflows/benchmark.yml +39 -0
  4. data/.github/workflows/lint.yml +2 -4
  5. data/.github/workflows/ruby.yml +1 -1
  6. data/.gitignore +3 -0
  7. data/.rubocop.yml +5 -1
  8. data/.yardopts +7 -0
  9. data/CHANGELOG.md +21 -0
  10. data/Gemfile +12 -0
  11. data/README.md +1 -1
  12. data/bench/benchmark.rb +34 -0
  13. data/bench/benchmarks/basic_field_serializer.rb +21 -0
  14. data/bench/benchmarks/view_selection.rb +47 -0
  15. data/docs/serializers.md +4 -1
  16. data/example/Gemfile +2 -2
  17. data/example/Gemfile.lock +46 -44
  18. data/example/config/environments/production.rb +1 -1
  19. data/lib/sober_swag/compiler/path.rb +42 -3
  20. data/lib/sober_swag/compiler/paths.rb +20 -0
  21. data/lib/sober_swag/compiler/primitive.rb +20 -1
  22. data/lib/sober_swag/compiler/type.rb +105 -22
  23. data/lib/sober_swag/compiler.rb +29 -3
  24. data/lib/sober_swag/controller/route.rb +103 -20
  25. data/lib/sober_swag/controller.rb +39 -12
  26. data/lib/sober_swag/input_object.rb +124 -7
  27. data/lib/sober_swag/nodes/array.rb +19 -0
  28. data/lib/sober_swag/nodes/attribute.rb +45 -4
  29. data/lib/sober_swag/nodes/base.rb +27 -7
  30. data/lib/sober_swag/nodes/binary.rb +30 -13
  31. data/lib/sober_swag/nodes/enum.rb +16 -1
  32. data/lib/sober_swag/nodes/list.rb +20 -0
  33. data/lib/sober_swag/nodes/nullable_primitive.rb +3 -0
  34. data/lib/sober_swag/nodes/object.rb +4 -1
  35. data/lib/sober_swag/nodes/one_of.rb +11 -3
  36. data/lib/sober_swag/nodes/primitive.rb +34 -2
  37. data/lib/sober_swag/nodes/sum.rb +8 -0
  38. data/lib/sober_swag/output_object/definition.rb +57 -1
  39. data/lib/sober_swag/output_object/field.rb +31 -11
  40. data/lib/sober_swag/output_object/field_syntax.rb +19 -3
  41. data/lib/sober_swag/output_object/view.rb +46 -1
  42. data/lib/sober_swag/output_object.rb +40 -19
  43. data/lib/sober_swag/parser.rb +7 -1
  44. data/lib/sober_swag/serializer/array.rb +27 -3
  45. data/lib/sober_swag/serializer/base.rb +75 -25
  46. data/lib/sober_swag/serializer/conditional.rb +33 -1
  47. data/lib/sober_swag/serializer/field_list.rb +23 -5
  48. data/lib/sober_swag/serializer/hash.rb +53 -0
  49. data/lib/sober_swag/serializer/mapped.rb +10 -1
  50. data/lib/sober_swag/serializer/optional.rb +18 -1
  51. data/lib/sober_swag/serializer/primitive.rb +3 -0
  52. data/lib/sober_swag/serializer.rb +1 -0
  53. data/lib/sober_swag/server.rb +27 -11
  54. data/lib/sober_swag/type/named.rb +14 -0
  55. data/lib/sober_swag/types/comma_array.rb +4 -0
  56. data/lib/sober_swag/version.rb +1 -1
  57. data/lib/sober_swag.rb +6 -1
  58. metadata +9 -2
@@ -3,6 +3,12 @@ module SoberSwag
3
3
  ##
4
4
  # Describe a single controller endpoint.
5
5
  class Route
6
+ ##
7
+ # @param method [Symbol] the HTTP method to get
8
+ # @param action_name [Symbol] the name of the rails action
9
+ # (the name of the controller method, usually)
10
+ # @param path [String] an OpenAPI V3 path template,
11
+ # which should [match this format](https://swagger.io/docs/specification/describing-parameters/#path-parameters)
6
12
  def initialize(method, action_name, path)
7
13
  @method = method
8
14
  @path = path
@@ -12,20 +18,56 @@ module SoberSwag
12
18
  @tags = []
13
19
  end
14
20
 
15
- attr_reader :response_serializers, :response_descriptions, :controller, :method, :path, :action_name
21
+ ##
22
+ # A hash of response code -> response serializer
23
+ # @return [Hash{Symbol => SoberSwag::Serializer::Base}]
24
+ # response code to response serializer
25
+ attr_reader :response_serializers
26
+
27
+ ##
28
+ # A hash of response code -> response description
29
+ # @return [Hash{Symbol => String}]
30
+ # response code to response description
31
+ attr_reader :response_descriptions
32
+
33
+ ##
34
+ # The HTTP method of this route.
35
+ # @return [Symbol]
36
+ attr_reader :method
37
+
38
+ ##
39
+ # The swagger path specifier of this route.
40
+ # @return [String]
41
+ attr_reader :path
42
+
43
+ ##
44
+ # The name of the rails action (usually the controller method) of this route.
45
+ # @return [Symbol]
46
+ attr_reader :action_name
16
47
 
17
48
  ##
18
49
  # What to parse the request body into.
50
+ # @return [Class] a swagger-able class type for a request body.
19
51
  attr_reader :request_body_class
20
52
  ##
21
53
  # What to parse the request query_params into.
54
+ # @return [Class] a swagger-able class type for query parameters.
22
55
  attr_reader :query_params_class
23
56
  ##
24
57
  # What to parse the path params into.
58
+ # @return [Class] a swagger-able class type for path parameters.
25
59
  attr_reader :path_params_class
26
60
 
27
61
  ##
28
62
  # Standard swagger tags.
63
+ #
64
+ # @overload tags()
65
+ # Get the tags for this route.
66
+ # @return [Array<String,Symbol>] the tags.
67
+ # @overload tags(*args)
68
+ # Set the tags for this route.
69
+ # @param tags [Array<String,Symbol>] the tags to set
70
+ # @return [Array<String,Symbol>] the tags used
29
71
  def tags(*args)
30
72
  return @tags if args.empty?
31
73
 
@@ -34,8 +76,13 @@ module SoberSwag
34
76
 
35
77
  ##
36
78
  # Define the request body, using SoberSwag's type-definition scheme.
37
- # The block passed will be used to define the body of a new sublcass of `base` (defaulted to {SoberSwag::InputObject}.)
38
- # If you want, you can also define utility methods in here
79
+ # The block passed will be used to define the body of a new subclass of `base` (defaulted to {SoberSwag::InputObject}.)
80
+ # @overload request_body(base)
81
+ # Give a Swagger-able type that will be used to parse the request body, and used in generated docs.
82
+ # @param base [Class] a swagger-able class
83
+ # @overload request_body(base = SoberSwag::InputObject, &block)
84
+ # Define a Swagger-able type inline to use to parse the request body.
85
+ # @see SoberSwag.input_object
39
86
  def request_body(base = SoberSwag::InputObject, &block)
40
87
  @request_body_class = make_input_object!(base, &block)
41
88
  action_module.const_set('RequestBody', @request_body_class)
@@ -48,9 +95,12 @@ module SoberSwag
48
95
  end
49
96
 
50
97
  ##
51
- # Define the shape of the query_params parameters, using SoberSwag's type-definition scheme.
52
- # The block passed is the body of the newly-defined type.
53
- # You can also include a base type.
98
+ # @overload query_params(base)
99
+ # Give a Swagger-able type that will be used to parse the query params, and used in generated docs.
100
+ # @param base [Class] a swagger-able class
101
+ # @overload query_params(base = SoberSwag::InputObject, &block)
102
+ # Define a Swagger-able type inline to use to parse the query params.
103
+ # @see SoberSwag.input_object
54
104
  def query_params(base = SoberSwag::InputObject, &block)
55
105
  @query_params_class = make_input_object!(base, &block)
56
106
  action_module.const_set('QueryParams', @query_params_class)
@@ -63,9 +113,12 @@ module SoberSwag
63
113
  end
64
114
 
65
115
  ##
66
- # Define the shape of the *path* parameters, using SoberSwag's type-definition scheme.
67
- # The block passed will be the body of a new subclass of `base` (defaulted to {SoberSwag::InputObject}).
68
- # Names of this should match the names in the path template originally passed to {SoberSwag::Controller::Route.new}
116
+ # @overload path_params(base)
117
+ # Give a Swagger-able type that will be used to parse the path params, and used in generated docs.
118
+ # @param base [Class] a swagger-able class
119
+ # @overload path_params(base = SoberSwag::InputObject, &block)
120
+ # Define a Swagger-able type inline to use to parse the path params.
121
+ # @see SoberSwag.input_object
69
122
  def path_params(base = SoberSwag::InputObject, &block)
70
123
  @path_params_class = make_input_object!(base, &block)
71
124
  action_module.const_set('PathParams', @path_params_class)
@@ -78,19 +131,27 @@ module SoberSwag
78
131
  end
79
132
 
80
133
  ##
81
- # Define the body of the action method in the controller.
82
- def action(&body)
83
- return @action if body.nil?
84
-
85
- @action ||= body
86
- end
87
-
134
+ # @overload description()
135
+ # Get a description of this route object.
136
+ # @return [String] markdown-formatted description
137
+ # @overload description(desc)
138
+ # Set the description of this route object.
139
+ # @param desc [String] markdown-formatted description
140
+ # @return [String] `desc`.
88
141
  def description(desc = nil)
89
142
  return @description if desc.nil?
90
143
 
91
144
  @description = desc
92
145
  end
93
146
 
147
+ ##
148
+ # @overload summary()
149
+ # Get the summary of this route object, a short string that identifies
150
+ # what it does.
151
+ # @return [String] markdown-formatted summary
152
+ # @overload summary(sum)
153
+ # Set a short, markdown-formatted summary of what this route does.
154
+ # @param sum [String] markdown-formatted summary
94
155
  def summary(sum = nil)
95
156
  return @summary if sum.nil?
96
157
 
@@ -100,14 +161,35 @@ module SoberSwag
100
161
  ##
101
162
  # The container module for all the constants this will eventually define.
102
163
  # Each class generated by this Route will be defined within this module.
164
+ # @return [Module] the module under which constants will be defined.
103
165
  def action_module
104
166
  @action_module ||= Module.new
105
167
  end
106
168
 
107
169
  ##
108
- # Define a serializer for a response with the given status code.
109
- # You may either give a serializer you defined elsewhere, or define one inline as if passed to
110
- # {SoberSwag::OutputObject.define}
170
+ # @overload response(status_code, description, &block)
171
+ # Define a new response from this route, by defining a serializer inline.
172
+ # This serializer will be defined as if with {SoberSwag::OutputObject.define}
173
+ #
174
+ # Generally, you want to define your serializers elsewhere for independent testing and such.
175
+ # However, if you have a really quick thing to serialize, this works.
176
+ # @param status_code [Symbol]
177
+ # the name of the HTTP status of this response.
178
+ # @param description [String]
179
+ # a description of what this response is, markdown-formatted
180
+ # @param block [Proc]
181
+ # passed to {SoberSwag::OutputObject.define}
182
+ #
183
+ # @overload response(status_code, description, serializer)
184
+ # Define a new response from this route, with an existing serializer.
185
+ # The generated swagger will document this response's format using the serializer.
186
+ #
187
+ # @param status_code [Symbol]
188
+ # the name of the HTTP status of this response
189
+ # @param description [String]
190
+ # a description of what this response is, markdown-formatted
191
+ # @param serializer [SoberSwag::Serializer::Base] a serializer to use for the
192
+ # body of this response
111
193
  def response(status_code, description, serializer = nil, &block)
112
194
  status_key = Rack::Utils.status_code(status_code)
113
195
 
@@ -120,7 +202,8 @@ module SoberSwag
120
202
  end
121
203
 
122
204
  ##
123
- # What you should call the module of this action in your controller
205
+ # What you should call the module of this action in your controller.
206
+ # @return [String]
124
207
  def action_module_name
125
208
  action_name.to_s.classify
126
209
  end
@@ -2,7 +2,8 @@ require 'active_support/concern'
2
2
 
3
3
  module SoberSwag
4
4
  ##
5
- # Controller concern
5
+ # This module can be included in any subclass of `ActionController` or `ActionController::API` to make it `SoberSwag`-able.
6
+ # This means that you can use the mechanics of SoberSwag to define a type-safe API, with generated Swagger documentation!
6
7
  module Controller
7
8
  extend ActiveSupport::Concern
8
9
 
@@ -17,14 +18,18 @@ module SoberSwag
17
18
  include ::Dry::Types()
18
19
  end
19
20
 
20
- class_methods do
21
+ ##
22
+ # Module containing class methods.
23
+ # Any class that `include`s {SoberSwag::Controller} will also `extend` {SoberSwag::Controller::ClassMethods}.
24
+ module ClassMethods
21
25
  ##
22
26
  # Define a new action with the given HTTP method, action name, and path.
23
- # This will eventaully delegate to making an actual method on your controller,
27
+ # This will eventually delegate to making an actual method on your controller,
24
28
  # so you can use controllers as you wish with no harm.
25
29
  #
26
30
  # This method takes a block, evaluated in the context of a {SoberSwag::Controller::Route}.
27
31
  # Used like:
32
+ #
28
33
  # define(:get, :show, '/posts/{id}') do
29
34
  # path_params do
30
35
  # attribute :id, Types::Integer
@@ -36,32 +41,46 @@ module SoberSwag
36
41
  # end
37
42
  #
38
43
  # This will define an "action module" on this class to contain the generated types.
39
- # In the above example, the following constants will be deifned on the controller:
40
- # PostsController::Show # the container module for everything in this action
41
- # PostsController::Show::PathParams # the dry-struct type for the path attribute.
44
+ # In the above example, the following constants will be defined on the controller:
45
+ #
46
+ # - `PostsController::Show` - the container module for everything in this action
47
+ # - `PostsController::Show::PathParams` - the dry-struct type for the path attribute.
48
+ #
42
49
  # So, in the same controller, you can refer to Show::PathParams to get the type created by the 'path_params' block above.
50
+ #
51
+ # The block given evaluates in the context of `SoberSwag::Controller::Route`.
52
+ #
53
+ # @todo Explore parsing the `path` parameter from rails routes so we can avoid forcing the duplicate boilerplate.
54
+ #
55
+ # @param method [Symbol] the HTTP method of this route
56
+ # @param action [Symbol] the name of the controller method this maps onto
57
+ # @param path [String] an OpenAPI v3 Path Specifier
43
58
  def define(method, action, path, &block)
44
59
  r = Route.new(method, action, path)
45
60
  r.instance_eval(&block)
46
61
  const_set(r.action_module_name, r.action_module)
47
62
  defined_routes << r
48
- define_method(action, r.action) if r.action
49
63
  end
50
64
 
51
65
  ##
52
66
  # All the routes that this controller knows about.
67
+ # @return [Array<SoberSwag::Controller::Route>
53
68
  def defined_routes
54
69
  @defined_routes ||= []
55
70
  end
56
71
 
57
72
  ##
58
73
  # Find a route with the given name.
74
+ # @param name [Symbol] the name
75
+ # @return [SoberSwag::Controller::Route]
59
76
  def find_route(name)
60
77
  defined_routes.find { |r| r.action_name.to_s == name.to_s }
61
78
  end
62
79
 
63
80
  ##
64
- # A swagger definition for *this controller only*.
81
+ # Get the OpenAPI v3 definition for this controller.
82
+ #
83
+ # @return [Hash]
65
84
  def swagger_info
66
85
  @swagger_info ||=
67
86
  begin
@@ -74,14 +93,19 @@ module SoberSwag
74
93
  end
75
94
  end
76
95
 
96
+ included do |base|
97
+ base.extend ClassMethods
98
+ end
99
+
77
100
  ##
78
- # Action to get the singular swagger for this entire API.
101
+ # ActiveController action to get the swagger definition for this API.
102
+ # It renders a JSON of the OpenAPI v3 schema for this API.
79
103
  def swagger
80
104
  render json: self.class.swagger_info
81
105
  end
82
106
 
83
107
  ##
84
- # Get the path parameters, parsed into the type you defined with {SoberSwag::Controller.define}
108
+ # Get the path parameters, parsed into the type you defined with {SoberSwag::Controller::ClassMethods#define}
85
109
  # @raise [UndefinedPathError] if there's no path params defined for this route
86
110
  # @raise [Dry::Struct::Error] if we cannot convert the path params to the defined type.
87
111
  def parsed_path
@@ -95,7 +119,7 @@ module SoberSwag
95
119
  end
96
120
 
97
121
  ##
98
- # Get the request body, parsed into the type you defined with {SoberSwag::Controller.define}.
122
+ # Get the request body, parsed into the type you defined with {SoberSwag::Controller::ClassMethods#define}.
99
123
  # @raise [UndefinedBodyError] if there's no request body defined for this route
100
124
  # @raise [Dry::Struct::Error] if we cannot convert the path params to the defined type.
101
125
  def parsed_body
@@ -109,7 +133,7 @@ module SoberSwag
109
133
  end
110
134
 
111
135
  ##
112
- # Get the query params, parsed into the type you defined with {SoberSwag::Controller.define}
136
+ # Get the query params, parsed into the type you defined with {SoberSwag::Controller::ClassMethods#define}
113
137
  # @raise [UndefinedQueryError] if there's no query params defined for this route
114
138
  # @raise [Dry::Struct::Error] if we cannot convert the path params to the defined type.
115
139
  def parsed_query
@@ -140,6 +164,8 @@ module SoberSwag
140
164
  # This kinda violates the "be liberal in what you accept" principle,
141
165
  # but it keeps the docs honest: parameters sent in the body *must* be
142
166
  # in the body.
167
+ #
168
+ # @return [Hash]
143
169
  def body_params
144
170
  request.request_parameters
145
171
  end
@@ -147,6 +173,7 @@ module SoberSwag
147
173
  ##
148
174
  # Get the action-definition for the current action.
149
175
  # Under the hood, delegates to the `:action` key of rails params.
176
+ # @return [SoberSwag::Controller::Route]
150
177
  def current_action_def
151
178
  self.class.find_route(params[:action])
152
179
  end
@@ -1,6 +1,6 @@
1
1
  module SoberSwag
2
2
  ##
3
- # A variant of Dry::Struct that allows you to set a "model name" that is publically visible.
3
+ # A variant of Dry::Struct that allows you to set a "model name" that is publicly visible.
4
4
  # If you do not set one, it will be the Ruby class name, with any '::' replaced with a '.'.
5
5
  #
6
6
  # This otherwise behaves exactly like a Dry::Struct.
@@ -12,24 +12,101 @@ module SoberSwag
12
12
  class << self
13
13
  ##
14
14
  # The name to use for this type in external documentation.
15
- def identifier(arg = nil)
16
- @identifier = arg if arg
15
+ #
16
+ # @param new_ident [String] what to call this InputObject in external documentation.
17
+ def identifier(new_ident = nil)
18
+ @identifier = new_ident if new_ident
17
19
 
18
20
  @identifier || name.to_s.gsub('::', '.')
19
21
  end
20
22
 
23
+ ##
24
+ # @overload attribute(key, parent = SoberSwag::InputObject, &block)
25
+ # Defines an attribute as a direct sub-object.
26
+ # This block will be called as in {SoberSwag.input_object}.
27
+ # This might be useful in a case like the following:
28
+ #
29
+ # ```ruby
30
+ # class Classroom < SoberSwag::InputObject
31
+ # attribute :biographical_detail do
32
+ # attribute :student_name, primitive(:String)
33
+ # end
34
+ # end
35
+ # ```
36
+ #
37
+ # @param key [Symbol] the attribute name
38
+ # @param parent [Class] the parent class to use for the sub-object
39
+ # @overload attribute(key, type)
40
+ # Defines a new attribute with the given type.
41
+ # @param key [Symbol] the attribute name
42
+ # @param type the attribute type
21
43
  def attribute(key, parent = SoberSwag::InputObject, &block)
22
44
  raise ArgumentError, "parent class #{parent} is not an input object type!" unless valid_field_def?(parent, block)
23
45
 
24
46
  super(key, parent, &block)
25
47
  end
26
48
 
49
+ ##
50
+ # Add on an attribute which only ever parses from a constant value.
51
+ # By default, this attribute will be called `type`, but you can override it with the kwarg.
52
+ # This is useful in situations where you want to emulate a sum type.
53
+ # For example, if you want to make an API endpoint that can either accept or reject proposals
54
+ #
55
+ # ```ruby
56
+ #
57
+ # ApiInputType = SoberSwag.input_object {
58
+ # identifier 'AcceptProposal'
59
+ # type_attribute 'accept'
60
+ # attribute(:message, primitive(:String))
61
+ # } | SoberSwag.input_object {
62
+ # identifier 'RejectProposal'
63
+ # type_attribute 'reject'
64
+ # attribute(:message, primitive(:String))
65
+ # }
66
+ # ```
67
+ #
68
+ # Under the hood, this basically looks like:
69
+ #
70
+ # ```ruby
71
+ # type_attribute 'archive'
72
+ # # is equivalent to
73
+ #
74
+ # attribute(:type, SoberSwag::Types::String.enum('archive'))
75
+ # ```
76
+ #
77
+ # @param value [String,Symbol] the value to parse
78
+ # @param attribute_key [Symbol] what key to use
79
+ def type_attribute(value, attribute_key: :type)
80
+ attribute(attribute_key, SoberSwag::Types::String.enum(value.to_s))
81
+ end
82
+
83
+ ##
84
+ # @overload attribute(key, parent = SoberSwag::InputObject, &block)
85
+ # Defines an optional attribute by defining a sub-object inline.
86
+ # This differs from a nil-able attribute as it can be *not provided*, while nilable attributes must be set to `null`.
87
+ #
88
+ # Yields to the block like in {SoberSwag.input_object}
89
+ #
90
+ # @param key [Symbol] the attribute name
91
+ # @param parent [Class] the parent class to use for the sub-object
92
+ # @overload attribute(key, type)
93
+ # Defines an optional attribute with a given type.
94
+ # This differs from a nil-able attribute as it can be *not provided*, while nilable attributes must be set to `null`.
95
+ #
96
+ # @param key [Symbol] the attribute name
97
+ # @param type the attribute type, another parsable object.
27
98
  def attribute?(key, parent = SoberSwag::InputObject, &block)
28
99
  raise ArgumentError, "parent class #{parent} is not an input object type!" unless valid_field_def?(parent, block)
29
100
 
30
101
  super(key, parent, &block)
31
102
  end
32
103
 
104
+ ##
105
+ # Add metadata keys, like `:description`, to the defined type.
106
+ # Note: does NOT mutate the type, returns a new type with the metadata added.
107
+ #
108
+ # @param args [Hash] the argument values
109
+ # @return [SoberSwag::InputObject] the new input object class
33
110
  def meta(*args)
34
111
  original = self
35
112
 
@@ -42,8 +119,25 @@ module SoberSwag
42
119
  end
43
120
 
44
121
  ##
45
- # .primitive is already defined on Dry::Struct, so forward to the superclass if
46
- # not called as a way to get a primitive type
122
+ # Convenience method: you can use `.primitive` get a primitive parser for a given type.
123
+ # This lets you write:
124
+ #
125
+ # ```ruby
126
+ # class Foo < SoberSwag::InputObject
127
+ # attribute :bar, primitive(:String)
128
+ # end
129
+ # ```
130
+ #
131
+ # instead of
132
+ #
133
+ # ```ruby
134
+ # class Foo < SoberSwag::InputObject
135
+ # attribute :bar, SoberSwag::Types::String
136
+ # end
137
+ # ```
138
+ #
139
+ # @param args [Symbol] a symbol
140
+ # @return a primitive parser
47
141
  def primitive(*args)
48
142
  if args.length == 1
49
143
  SoberSwag::Types.const_get(args.first)
@@ -52,8 +146,31 @@ module SoberSwag
52
146
  end
53
147
  end
54
148
 
55
- def param(sym)
56
- SoberSwag::Types::Params.const_get(sym)
149
+ ##
150
+ # Convenience method: you can use `.param` to get a parameter parser of a given type.
151
+ # Said parsers are more loose: for example, `param(:Integer)` will parse the string `"10"` into `10`, while
152
+ # `primitive(:Integer)` will throw an error.
153
+ #
154
+ # This method lets you write:
155
+ #
156
+ # ```ruby
157
+ # class Foo < SoberSwag::InputObject
158
+ # attribute :bar, param(:Integer)
159
+ # end
160
+ # ```
161
+ #
162
+ # instead of
163
+ #
164
+ # ```ruby
165
+ # class Foo < SoberSwag::InputObject
166
+ # attribute :bar, SoberSwag::Types::Param::Integer
167
+ # end
168
+ # ```
169
+ #
170
+ # @param name [Symbol] the name of the parameter type to get
171
+ # @return a parameter parser
172
+ def param(name)
173
+ SoberSwag::Types::Params.const_get(name)
57
174
  end
58
175
 
59
176
  private