sober_swag 0.17.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
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