jsapi 0.8.0 → 0.9.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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jsapi/dsl/base.rb +55 -0
  3. data/lib/jsapi/dsl/callback.rb +21 -0
  4. data/lib/jsapi/dsl/class_methods.rb +116 -10
  5. data/lib/jsapi/dsl/definitions.rb +173 -33
  6. data/lib/jsapi/dsl/examples.rb +30 -0
  7. data/lib/jsapi/dsl/operation.rb +60 -32
  8. data/lib/jsapi/dsl/parameter.rb +2 -2
  9. data/lib/jsapi/dsl/request_body.rb +2 -2
  10. data/lib/jsapi/dsl/response.rb +35 -11
  11. data/lib/jsapi/dsl/schema.rb +16 -16
  12. data/lib/jsapi/dsl.rb +4 -4
  13. data/lib/jsapi/meta/base/attributes.rb +21 -19
  14. data/lib/jsapi/meta/base/model.rb +15 -7
  15. data/lib/jsapi/meta/callback/model.rb +34 -0
  16. data/lib/jsapi/meta/callback/reference.rb +14 -0
  17. data/lib/jsapi/meta/callback.rb +19 -0
  18. data/lib/jsapi/meta/contact.rb +30 -0
  19. data/lib/jsapi/meta/defaults.rb +2 -2
  20. data/lib/jsapi/meta/definitions.rb +230 -124
  21. data/lib/jsapi/meta/example/model.rb +43 -0
  22. data/lib/jsapi/meta/example/reference.rb +14 -0
  23. data/lib/jsapi/meta/example.rb +19 -0
  24. data/lib/jsapi/meta/external_documentation.rb +25 -0
  25. data/lib/jsapi/meta/header/model.rb +81 -0
  26. data/lib/jsapi/meta/header/reference.rb +14 -0
  27. data/lib/jsapi/meta/header.rb +19 -0
  28. data/lib/jsapi/meta/info.rb +52 -0
  29. data/lib/jsapi/meta/license.rb +25 -0
  30. data/lib/jsapi/meta/link/model.rb +48 -0
  31. data/lib/jsapi/meta/link/reference.rb +14 -0
  32. data/lib/jsapi/meta/link.rb +19 -0
  33. data/lib/jsapi/meta/oauth_flow.rb +52 -0
  34. data/lib/jsapi/meta/openapi.rb +0 -28
  35. data/lib/jsapi/meta/operation.rb +19 -18
  36. data/lib/jsapi/meta/parameter/model.rb +6 -4
  37. data/lib/jsapi/meta/parameter/reference.rb +6 -4
  38. data/lib/jsapi/meta/parameter/to_openapi.rb +13 -0
  39. data/lib/jsapi/meta/parameter.rb +2 -1
  40. data/lib/jsapi/meta/property.rb +1 -1
  41. data/lib/jsapi/meta/request_body/model.rb +6 -5
  42. data/lib/jsapi/meta/request_body.rb +1 -1
  43. data/lib/jsapi/meta/response/model.rb +16 -13
  44. data/lib/jsapi/meta/response.rb +1 -1
  45. data/lib/jsapi/meta/schema/array.rb +1 -1
  46. data/lib/jsapi/meta/schema/base.rb +5 -5
  47. data/lib/jsapi/meta/schema/discriminator.rb +2 -2
  48. data/lib/jsapi/meta/schema/object.rb +3 -3
  49. data/lib/jsapi/meta/schema/reference.rb +1 -1
  50. data/lib/jsapi/meta/schema.rb +2 -2
  51. data/lib/jsapi/meta/security_requirement.rb +25 -0
  52. data/lib/jsapi/meta/security_scheme/api_key.rb +38 -0
  53. data/lib/jsapi/meta/security_scheme/base.rb +14 -0
  54. data/lib/jsapi/meta/security_scheme/http/basic.rb +34 -0
  55. data/lib/jsapi/meta/security_scheme/http/bearer.rb +36 -0
  56. data/lib/jsapi/meta/security_scheme/http/other.rb +36 -0
  57. data/lib/jsapi/meta/security_scheme/http.rb +29 -0
  58. data/lib/jsapi/meta/security_scheme/oauth2.rb +44 -0
  59. data/lib/jsapi/meta/security_scheme/open_id_connect.rb +32 -0
  60. data/lib/jsapi/meta/security_scheme.rb +49 -0
  61. data/lib/jsapi/meta/server.rb +34 -0
  62. data/lib/jsapi/meta/server_variable.rb +34 -0
  63. data/lib/jsapi/meta/tag.rb +34 -0
  64. data/lib/jsapi/meta.rb +15 -1
  65. data/lib/jsapi/model/base.rb +5 -5
  66. data/lib/jsapi/version.rb +1 -1
  67. metadata +38 -42
  68. data/lib/jsapi/dsl/node.rb +0 -62
  69. data/lib/jsapi/dsl/openapi/callback.rb +0 -23
  70. data/lib/jsapi/dsl/openapi/callbacks.rb +0 -34
  71. data/lib/jsapi/dsl/openapi/examples.rb +0 -32
  72. data/lib/jsapi/dsl/openapi/root.rb +0 -126
  73. data/lib/jsapi/dsl/openapi.rb +0 -6
  74. data/lib/jsapi/meta/openapi/callback/model.rb +0 -34
  75. data/lib/jsapi/meta/openapi/callback/reference.rb +0 -16
  76. data/lib/jsapi/meta/openapi/callback.rb +0 -21
  77. data/lib/jsapi/meta/openapi/contact.rb +0 -32
  78. data/lib/jsapi/meta/openapi/example/model.rb +0 -44
  79. data/lib/jsapi/meta/openapi/example/reference.rb +0 -16
  80. data/lib/jsapi/meta/openapi/example.rb +0 -21
  81. data/lib/jsapi/meta/openapi/external_documentation.rb +0 -27
  82. data/lib/jsapi/meta/openapi/header/model.rb +0 -82
  83. data/lib/jsapi/meta/openapi/header/reference.rb +0 -16
  84. data/lib/jsapi/meta/openapi/header.rb +0 -21
  85. data/lib/jsapi/meta/openapi/info.rb +0 -54
  86. data/lib/jsapi/meta/openapi/license.rb +0 -27
  87. data/lib/jsapi/meta/openapi/link/model.rb +0 -50
  88. data/lib/jsapi/meta/openapi/link/reference.rb +0 -16
  89. data/lib/jsapi/meta/openapi/link.rb +0 -21
  90. data/lib/jsapi/meta/openapi/oauth_flow.rb +0 -52
  91. data/lib/jsapi/meta/openapi/root.rb +0 -132
  92. data/lib/jsapi/meta/openapi/security_requirement.rb +0 -27
  93. data/lib/jsapi/meta/openapi/security_scheme/api_key.rb +0 -40
  94. data/lib/jsapi/meta/openapi/security_scheme/base.rb +0 -16
  95. data/lib/jsapi/meta/openapi/security_scheme/http/basic.rb +0 -36
  96. data/lib/jsapi/meta/openapi/security_scheme/http/bearer.rb +0 -39
  97. data/lib/jsapi/meta/openapi/security_scheme/http/other.rb +0 -39
  98. data/lib/jsapi/meta/openapi/security_scheme/http.rb +0 -31
  99. data/lib/jsapi/meta/openapi/security_scheme/oauth2.rb +0 -46
  100. data/lib/jsapi/meta/openapi/security_scheme/open_id_connect.rb +0 -34
  101. data/lib/jsapi/meta/openapi/security_scheme.rb +0 -51
  102. data/lib/jsapi/meta/openapi/server.rb +0 -36
  103. data/lib/jsapi/meta/openapi/server_variable.rb +0 -36
  104. data/lib/jsapi/meta/openapi/tag.rb +0 -36
@@ -3,89 +3,146 @@
3
3
  module Jsapi
4
4
  module Meta
5
5
  class Definitions < Base::Model
6
+ include OpenAPI::Extensions
7
+
6
8
  ##
7
- # :attr_reader: children
8
- # The +Definitions+ instances that directly inherit from this instance.
9
- attribute :children, read_only: true, default: []
9
+ # :attr: base_path
10
+ # The base path of the API. Applies to \OpenAPI 2.0.
11
+ attribute :base_path, String
12
+
13
+ ##
14
+ # :attr: callbacks
15
+ # The reusable Callback objects. Applies to \OpenAPI 3.0 and higher.
16
+ attribute :callbacks, { String => Callback }
10
17
 
11
18
  ##
12
19
  # :attr: defaults
13
20
  # The Defaults.
14
- attribute :defaults, { String => Defaults }, keys: Schema::TYPES, default: {}
21
+ attribute :defaults, { String => Defaults }, keys: Schema::TYPES
15
22
 
16
23
  ##
17
- # :attr: dependent_definitions
18
- # The +Definitions+ instances that directly include this instance.
19
- attribute :dependent_definitions, read_only: true, default: []
24
+ # :attr: examples
25
+ # The reusable Example objects. Applies to \OpenAPI 3.0 and higher.
26
+ attribute :examples, { String => Example }
20
27
 
21
28
  ##
22
- # :attr: included_definitions
23
- # The +Definitions+ instances included.
24
- attribute :included_definitions, [Definitions], add_method: :include, default: []
29
+ # :attr: external_docs
30
+ # The ExternalDocumentation object.
31
+ attribute :external_docs, ExternalDocumentation
25
32
 
26
33
  ##
27
- # :attr: on_rescues
28
- # The methods or procs to be called whenever an exception is rescued.
29
- attribute :on_rescues, [], default: []
34
+ # :attr: headers
35
+ # The reusable Header objects. Applies to \OpenAPI 3.0 and higher.
36
+ attribute :headers, { String => Header }
30
37
 
31
38
  ##
32
- # :attr: openapi
33
- # The OpenAPI root object.
34
- attribute :openapi, OpenAPI
39
+ # :attr: host
40
+ # The host serving the API. Applies to \OpenAPI 2.0.
41
+ attribute :host, String
35
42
 
36
43
  ##
37
- # :attr: operations
38
- # The Operation objects.
39
- attribute :operations, { String => Operation }, default: {}
44
+ # :attr: info
45
+ # The Info object.
46
+ attribute :info, Info
40
47
 
41
48
  ##
42
- # :attr_reader: owner
43
- # The class to which this instance is assigned.
44
- attribute :owner, read_only: true
49
+ # :attr: links
50
+ # The reusable Link objects. Applies to \OpenAPI 3.0 and higher.
51
+ attribute :links, { String => Link }
45
52
 
46
53
  ##
47
- # :attr: parameters
48
- # The reusable Parameter objects.
49
- attribute :parameters, { String => Parameter }, default: {}
54
+ # :attr: on_rescues
55
+ # The methods or procs to be called whenever an exception is rescued.
56
+ attribute :on_rescues, []
50
57
 
51
58
  ##
52
- # :attr_reader: parent
53
- # The +Definitions+ instance from which this instance inherits.
54
- attribute :parent, read_only: true
59
+ # :attr: operations
60
+ # The Operation objects.
61
+ attribute :operations, { String => Operation }
62
+
63
+ ##
64
+ # :attr: parameters
65
+ # The reusable Parameter objects.
66
+ attribute :parameters, { String => Parameter }
55
67
 
56
68
  ##
57
69
  # :attr: rescue_handlers
58
70
  # The RescueHandler objects.
59
- attribute :rescue_handlers, [RescueHandler], default: []
71
+ attribute :rescue_handlers, [RescueHandler]
60
72
 
61
73
  ##
62
74
  # :attr: request_bodies
63
75
  # The reusable RequestBody objects.
64
- attribute :request_bodies, { String => RequestBody }, default: {}
76
+ attribute :request_bodies, { String => RequestBody }
65
77
 
66
78
  ##
67
79
  # :attr: responses
68
80
  # The reusable Response objects.
69
- attribute :responses, { String => Response }, default: {}
81
+ attribute :responses, { String => Response }
70
82
 
71
83
  ##
72
84
  # :attr: schemas
73
85
  # The reusable Schema objects.
74
- attribute :schemas, { String => Schema }, default: {}
86
+ attribute :schemas, { String => Schema }
87
+
88
+ ##
89
+ # :attr: schemes
90
+ # The array of transfer protocols supported by the API. Possible values are:
91
+ #
92
+ # - <code>"http"</code>
93
+ # - <code>"https"</code>
94
+ # - <code>"ws"</code>
95
+ # - <code>"wss"</code>
96
+ #
97
+ # Applies to \OpenAPI 2.0.
98
+ attribute :schemes, [String], values: %w[http https ws wss]
99
+
100
+ ##
101
+ # :attr: security_requirements
102
+ # The array of SecurityRequirement objects.
103
+ attribute :security_requirements, [SecurityRequirement]
75
104
 
76
- undef add_operation, add_parameter, include
105
+ alias add_security add_security_requirement
106
+
107
+ ##
108
+ # :attr: security_schemes
109
+ # The SecurityScheme objects.
110
+ attribute :security_schemes, { String => SecurityScheme }
111
+
112
+ ##
113
+ # :attr: servers
114
+ # The array of Server objects. Applies to \OpenAPI 3.0 and higher.
115
+ attribute :servers, [Server]
116
+
117
+ ##
118
+ # :attr: tags
119
+ # The array of Tag objects.
120
+ attribute :tags, [Tag]
121
+
122
+ # The class to which this instance is assigned.
123
+ attr_reader :owner
124
+
125
+ # The +Definitions+ instance from which this instance inherits.
126
+ attr_reader :parent
77
127
 
78
128
  def initialize(keywords = {})
129
+ keywords = keywords.dup
79
130
  @owner = keywords.delete(:owner)
80
131
  @parent = keywords.delete(:parent)
132
+ included = keywords.delete(:include)
81
133
  super(keywords)
82
134
 
135
+ Array(included).each do |definitions|
136
+ include(definitions)
137
+ end
83
138
  @parent&.inherited(self)
84
139
  end
85
140
 
141
+ undef add_operation, add_parameter
142
+
86
143
  def add_operation(name = nil, keywords = {}) # :nodoc:
87
144
  name = name.nil? ? default_operation_name : name.to_s
88
- keywords = keywords.reverse_merge(path: default_path)
145
+ keywords = keywords.reverse_merge(path: default_operation_path)
89
146
  (@operations ||= {})[name] = Operation.new(name, keywords)
90
147
  end
91
148
 
@@ -98,20 +155,20 @@ module Jsapi
98
155
  # inherited/included.
99
156
  def ancestors
100
157
  @ancestors ||= [self].tap do |ancestors|
101
- (included_definitions + Array(parent)).each do |definitions|
102
- ancestors.push(*definitions.ancestors)
158
+ [@included_definitions, @parent].flatten.each do |definitions|
159
+ ancestors.push(*definitions.ancestors) if definitions
103
160
  end
104
161
  end.uniq
105
162
  end
106
163
 
107
164
  # Returns the default value for +type+ within +context+.
108
165
  def default_value(type, context: nil)
109
- components.dig(:defaults, type.to_s)&.value(context: context)
166
+ objects.dig(:defaults, type.to_s)&.value(context: context)
110
167
  end
111
168
 
112
169
  # Returns the operation with the specified name.
113
170
  def find_operation(name = nil)
114
- return components.dig(:operations, name.to_s) if name.present?
171
+ return objects.dig(:operations, name.to_s) if name.present?
115
172
 
116
173
  # Return the one and only operation
117
174
  operations.values.first if operations.one?
@@ -119,46 +176,43 @@ module Jsapi
119
176
 
120
177
  # Returns the reusable parameter with the specified name.
121
178
  def find_parameter(name)
122
- components.dig(:parameters, name&.to_s)
179
+ objects.dig(:parameters, name&.to_s)
123
180
  end
124
181
 
125
182
  # Returns the reusable request body with the specified name.
126
183
  def find_request_body(name)
127
- components.dig(:request_bodies, name&.to_s)
184
+ objects.dig(:request_bodies, name&.to_s)
128
185
  end
129
186
 
130
187
  # Returns the reusable response with the specified name.
131
188
  def find_response(name)
132
- components.dig(:responses, name&.to_s)
189
+ objects.dig(:responses, name&.to_s)
133
190
  end
134
191
 
135
192
  # Returns the reusable schema with the specified name.
136
193
  def find_schema(name)
137
- components.dig(:schemas, name&.to_s)
194
+ objects.dig(:schemas, name&.to_s)
138
195
  end
139
196
 
140
197
  # Includes +definitions+.
141
198
  def include(definitions)
142
199
  if circular_dependency?(definitions)
143
- raise ArgumentError, 'detected circular dependency between ' \
144
- "#{owner.inspect} and " \
145
- "#{definitions.owner.inspect}"
200
+ raise ArgumentError,
201
+ 'detected circular dependency between ' \
202
+ "#{owner.inspect} and " \
203
+ "#{definitions.owner.inspect}"
146
204
  end
147
205
 
148
206
  (@included_definitions ||= []) << definitions
149
207
  definitions.included(self)
150
- attribute_changed(:included_definitions)
208
+ invalidate_ancestors
151
209
  self
152
210
  end
153
211
 
154
- def inspect(*attributes) # :nodoc:
155
- super(*(attributes.presence || %i[owner]))
156
- end
157
-
158
212
  # Returns a hash representing the \JSON \Schema document for +name+.
159
213
  def json_schema_document(name)
160
214
  find_schema(name)&.to_json_schema&.tap do |json_schema_document|
161
- if (schemas = components[:schemas].except(name.to_s)).any?
215
+ if (schemas = objects[:schemas].except(name.to_s)).any?
162
216
  json_schema_document[:definitions] = schemas.transform_values(&:to_json_schema)
163
217
  end
164
218
  end
@@ -166,7 +220,7 @@ module Jsapi
166
220
 
167
221
  # Returns the methods or procs to be called when rescuing an exception.
168
222
  def on_rescue_callbacks
169
- components[:on_rescues]
223
+ objects[:on_rescues]
170
224
  end
171
225
 
172
226
  # Returns a hash representing the \OpenAPI document for +version+.
@@ -174,72 +228,94 @@ module Jsapi
174
228
  # Raises an +ArgumentError+ if +version+ is not supported.
175
229
  def openapi_document(version = nil)
176
230
  version = OpenAPI::Version.from(version)
177
- operations = components[:operations].values
178
-
179
- component_keys = %i[parameters responses parameters schemas]
180
- component_keys.insert(3, :request_bodies) if version.major > 2
181
-
182
- openapi_components = component_keys.filter_map do |key|
183
- value = components[key].transform_values do |component|
184
- case key
185
- when :parameters
186
- component.to_openapi(version, self).first
187
- when :responses
188
- component.to_openapi(version, self)
189
- else
190
- component.to_openapi(version)
231
+ operations = objects[:operations].values
232
+
233
+ openapi_paths =
234
+ operations.group_by { |operation| operation.path || default_operation_path }
235
+ .transform_values do |operations_by_path|
236
+ operations_by_path.index_by(&:method).transform_values do |operation|
237
+ operation.to_openapi(version, self)
191
238
  end
192
239
  end.presence
193
240
 
194
- [key, value] if value.present?
195
- end.to_h
196
-
197
- (openapi&.to_openapi(version, self) || {}).tap do |openapi_document|
198
- openapi_document[:paths] =
199
- operations
200
- .group_by { |operation| operation.path || default_path }
201
- .transform_values do |operations_by_path|
202
- operations_by_path.index_by(&:method).transform_values do |operation|
203
- operation.to_openapi(version, self)
204
- end
205
- end.presence
206
-
241
+ openapi_objects =
207
242
  if version.major == 2
208
- consumes = operations.filter_map { |operation| operation.consumes(self) }
209
- produces = operations.flat_map { |operation| operation.produces(self) }
210
-
211
- openapi_document.merge!(
212
- consumes: (consumes.uniq.sort if consumes.present?),
213
- produces: (produces.uniq.sort if produces.present?),
214
- definitions: openapi_components[:schemas],
215
- parameters: openapi_components[:parameters],
216
- responses: openapi_components[:responses]
217
- )
218
- elsif openapi_components.any?
219
- (openapi_document[:components] ||= {}).reverse_merge!(
220
- openapi_components.transform_keys do |key|
221
- key.to_s.camelize(:lower).to_sym
222
- end
223
- )
224
- end
225
- end.compact
243
+ %i[base_path external_docs info host parameters responses parameters schemas
244
+ schemes security_requirements security_schemes tags]
245
+ else
246
+ %i[callbacks examples external_docs headers info links parameters request_bodies
247
+ responses schemas security_requirements security_schemes servers tags]
248
+ end.to_h { |key| [key, object_to_openapi(objects[key], version).presence] }
249
+
250
+ with_openapi_extensions(
251
+ if version.major == 2
252
+ openapi_server = objects[:servers].first || default_server
253
+ uri = URI(openapi_server.url) if openapi_server
254
+ {
255
+ # Order according to the OpenAPI specification 2.x
256
+ swagger: '2.0',
257
+ info: openapi_objects[:info],
258
+ host: openapi_objects[:host] || uri&.hostname,
259
+ basePath: openapi_objects[:base_path] || uri&.path,
260
+ schemes: openapi_objects[:schemes] || Array(uri&.scheme).presence,
261
+ consumes: operations.filter_map do |operation|
262
+ operation.consumes(self)
263
+ end.uniq.sort.presence,
264
+ produces: operations.flat_map do |operation|
265
+ operation.produces(self)
266
+ end.uniq.sort.presence,
267
+ paths: openapi_paths,
268
+ definitions: openapi_objects[:schemas],
269
+ parameters: openapi_objects[:parameters],
270
+ responses: openapi_objects[:responses],
271
+ securityDefinitions: openapi_objects[:security_schemes]
272
+ }
273
+ else
274
+ {
275
+ # Order according to the OpenAPI specification 3.x
276
+ openapi: version.minor.zero? ? '3.0.3' : '3.1.0',
277
+ info: openapi_objects[:info],
278
+ servers: openapi_objects[:servers] ||
279
+ [default_server&.to_openapi].compact.presence,
280
+ paths: openapi_paths,
281
+ components: {
282
+ schemas: openapi_objects[:schemas],
283
+ responses: openapi_objects[:responses],
284
+ parameters: openapi_objects[:parameters],
285
+ examples: openapi_objects[:examples],
286
+ requestBodies: openapi_objects[:request_bodies],
287
+ headers: openapi_objects[:headers],
288
+ securitySchemes: openapi_objects[:security_schemes],
289
+ links: openapi_objects[:links],
290
+ callbacks: openapi_objects[:callbacks]
291
+ }.compact.presence
292
+ }
293
+ end.merge(
294
+ security: openapi_objects[:security_requirements],
295
+ tags: openapi_objects[:tags],
296
+ externalDocs: openapi_objects[:external_docs]
297
+ ).compact
298
+ )
226
299
  end
227
300
 
228
301
  # Returns the first RescueHandler to handle +exception+, or nil if no one could be found.
229
302
  def rescue_handler_for(exception)
230
- components[:rescue_handlers].find { |r| r.match?(exception) }
303
+ objects[:rescue_handlers].find { |r| r.match?(exception) }
231
304
  end
232
305
 
233
306
  protected
234
307
 
235
- def attribute_changed(name) # :nodoc:
236
- return if name == :openapi
308
+ # The +Definitions+ instances that directly inherit from this instance.
309
+ attr_reader :children
237
310
 
238
- if name == :included_definitions
239
- invalidate_ancestors
240
- else
241
- invalidate_components
242
- end
311
+ # The +Definitions+ instances that directly include this instance.
312
+ attr_reader :dependent_definitions
313
+
314
+ # The +Definitions+ instances included.
315
+ attr_reader :included_definitions
316
+
317
+ def attribute_changed(*) # :nodoc:
318
+ invalidate_objects
243
319
  end
244
320
 
245
321
  # Invoked whenever it is included in another +Definitions+ instance.
@@ -259,43 +335,73 @@ module Jsapi
259
335
  # Invalidates cached ancestors.
260
336
  def invalidate_ancestors
261
337
  @ancestors = nil
262
- @components = nil
263
- (children + dependent_definitions).each(&:invalidate_ancestors)
338
+ @objects = nil
339
+ @children&.each(&:invalidate_ancestors)
340
+ @dependent_definitions&.each(&:invalidate_ancestors)
264
341
  end
265
342
 
266
- # Invalidates cached components.
267
- def invalidate_components
268
- @components = nil
269
- (children + dependent_definitions).each(&:invalidate_components)
343
+ # Invalidates cached objects.
344
+ def invalidate_objects
345
+ @objects = nil
346
+ @children&.each(&:invalidate_objects)
347
+ @dependent_definitions&.each(&:invalidate_objects)
270
348
  end
271
349
 
272
350
  private
273
351
 
274
352
  def circular_dependency?(other)
275
353
  return true if other == self
276
- return false if other.included_definitions.none?
354
+ return false unless other.included_definitions&.any?
277
355
 
278
356
  other.included_definitions.any? { |included| circular_dependency?(included) }
279
357
  end
280
358
 
281
- def components
282
- @components ||= ancestors.each_with_object({}) do |ancestor, components|
283
- %i[defaults operations parameters request_bodies responses schemas].each do |type|
284
- (components[type] ||= {}).reverse_merge!(ancestor.send(type))
359
+ def default_operation_name
360
+ @default_operation_name ||=
361
+ if (name = @owner.try(:name))
362
+ name.demodulize.delete_suffix('Controller').underscore
285
363
  end
286
- %i[on_rescues rescue_handlers].each do |type|
287
- (components[type] ||= []).push(*ancestor.send(type))
364
+ end
365
+
366
+ def default_operation_path
367
+ @default_operation_path ||= "/#{default_operation_name}"
368
+ end
369
+
370
+ def default_server
371
+ @default_server ||=
372
+ if (name = @owner.try(:name))
373
+ Server.new(
374
+ url: name.deconstantize.split('::')
375
+ .map(&:underscore)
376
+ .join('/').prepend('/')
377
+ )
288
378
  end
289
- end
290
379
  end
291
380
 
292
- def default_operation_name
293
- @default_operation_name ||=
294
- @owner.to_s.demodulize.delete_suffix('Controller').underscore
381
+ def objects
382
+ @objects ||= ancestors.each_with_object({}) do |ancestor, objects|
383
+ self.class.attribute_names.each do |key|
384
+ case value = ancestor.send(key)
385
+ when Hash
386
+ (objects[key] ||= {}).reverse_merge!(value)
387
+ when Array
388
+ (objects[key] ||= []).push(*value)
389
+ else
390
+ objects[key] ||= value
391
+ end
392
+ end
393
+ end
295
394
  end
296
395
 
297
- def default_path
298
- @default_path ||= "/#{default_operation_name}"
396
+ def object_to_openapi(object, version)
397
+ case object
398
+ when Array
399
+ object.map { |item| object_to_openapi(item, version) }
400
+ when Hash
401
+ object.transform_values { |value| object_to_openapi(value, version) }
402
+ else
403
+ object.respond_to?(:to_openapi) ? object.to_openapi(version, self) : object
404
+ end
299
405
  end
300
406
  end
301
407
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Example
6
+ # Specifies an example object.
7
+ class Model < Base::Model
8
+ include OpenAPI::Extensions
9
+
10
+ ##
11
+ # :attr: description
12
+ # The description of the example.
13
+ attribute :description, String
14
+
15
+ ##
16
+ # :attr: external
17
+ # If true, +value+ is interpreted as a URI pointing to an external sample value.
18
+ attribute :external, values: [true, false]
19
+
20
+ ##
21
+ # :attr: summary
22
+ # The summary of the example.
23
+ attribute :summary, String
24
+
25
+ ##
26
+ # :attr: value
27
+ # The sample value.
28
+ attribute :value
29
+
30
+ # Returns a hash representing the \OpenAPI example object.
31
+ def to_openapi(*)
32
+ with_openapi_extensions(summary: summary, description: description).tap do |hash|
33
+ if external?
34
+ hash[:external_value] = value
35
+ else
36
+ hash[:value] = value
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ module Example
6
+ class Reference < Base::Reference
7
+ # Returns a hash representing the \OpenAPI reference object.
8
+ def to_openapi(*)
9
+ { '$ref': "#/components/examples/#{ref}" }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'example/model'
4
+ require_relative 'example/reference'
5
+
6
+ module Jsapi
7
+ module Meta
8
+ module Example
9
+ class << self
10
+ # Creates a Model or Reference.
11
+ def new(keywords = {})
12
+ return Reference.new(keywords) if keywords.key?(:ref)
13
+
14
+ Model.new(keywords)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Meta
5
+ # Specifies an external documentation object.
6
+ class ExternalDocumentation < Base::Model
7
+ include OpenAPI::Extensions
8
+
9
+ ##
10
+ # :attr: description
11
+ # The description of the external documentation.
12
+ attribute :description, String
13
+
14
+ ##
15
+ # :attr: url
16
+ # The URL of the external documentation.
17
+ attribute :url, String
18
+
19
+ # Returns a hash representing the \OpenAPI external documentation object.
20
+ def to_openapi(*)
21
+ with_openapi_extensions(url: url, description: description)
22
+ end
23
+ end
24
+ end
25
+ end