scorpio 0.0.4 → 0.1.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.
@@ -1,13 +1,12 @@
1
- # retrieved from
1
+ ---
2
2
  kind: discovery#restDescription
3
- etag: '"C5oy1hgQsABtYOYIOXWcR3BgYqU/Pyg0A4J33Dq212hoe9BYpSm0dl4"'
3
+ etag: '"YWOzh2SDasdU84ArJnpYek-OMdg/K3nEDF6hixE8Pks2-9Ysn9j9prQ"'
4
4
  discoveryVersion: v1
5
5
  id: discovery:v1
6
6
  name: discovery
7
7
  version: v1
8
8
  title: APIs Discovery Service
9
- description: Provides information about other Google APIs, such as what APIs are available,
10
- the resource, and method details for each API.
9
+ description: Provides information about other Google APIs, such as what APIs are available, the resource, and method details for each API.
11
10
  ownerDomain: google.com
12
11
  ownerName: Google
13
12
  icons:
@@ -19,7 +18,7 @@ baseUrl: https://www.googleapis.com/discovery/v1/
19
18
  basePath: "/discovery/v1/"
20
19
  rootUrl: https://www.googleapis.com/
21
20
  servicePath: discovery/v1/
22
- batchPath: batch
21
+ batchPath: batch/discovery/v1
23
22
  parameters:
24
23
  alt:
25
24
  type: string
@@ -36,8 +35,7 @@ parameters:
36
35
  location: query
37
36
  key:
38
37
  type: string
39
- description: API key. Your API key identifies your project and provides you with
40
- API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
38
+ description: API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.
41
39
  location: query
42
40
  oauth_token:
43
41
  type: string
@@ -50,14 +48,11 @@ parameters:
50
48
  location: query
51
49
  quotaUser:
52
50
  type: string
53
- description: Available to use for quota purposes for server-side applications.
54
- Can be any arbitrary string assigned to a user, but should not exceed 40 characters.
55
- Overrides userIp if both are provided.
51
+ description: Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters. Overrides userIp if both are provided.
56
52
  location: query
57
53
  userIp:
58
54
  type: string
59
- description: IP address of the site where the request originates. Use this if
60
- you want to enforce per-user limits.
55
+ description: IP address of the site where the request originates. Use this if you want to enforce per-user limits.
61
56
  location: query
62
57
  schemas:
63
58
  DirectoryList:
@@ -66,8 +61,7 @@ schemas:
66
61
  properties:
67
62
  discoveryVersion:
68
63
  type: string
69
- description: Indicate the version of the Discovery API used to generate this
70
- doc.
64
+ description: Indicate the version of the Discovery API used to generate this doc.
71
65
  default: v1
72
66
  items:
73
67
  type: array
@@ -131,20 +125,17 @@ schemas:
131
125
  properties:
132
126
  "$ref":
133
127
  type: string
134
- description: A reference to another schema. The value of this property is
135
- the "id" of another schema.
128
+ description: A reference to another schema. The value of this property is the "id" of another schema.
136
129
  additionalProperties:
137
130
  "$ref": JsonSchema
138
- description: If this is a schema for an object, this property is the schema
139
- for any additional properties with dynamic keys on this object.
131
+ description: If this is a schema for an object, this property is the schema for any additional properties with dynamic keys on this object.
140
132
  annotations:
141
133
  type: object
142
134
  description: Additional information about this property.
143
135
  properties:
144
136
  required:
145
137
  type: array
146
- description: A list of methods for which this property is required on
147
- requests.
138
+ description: A list of methods for which this property is required on requests.
148
139
  items:
149
140
  type: string
150
141
  default:
@@ -160,25 +151,21 @@ schemas:
160
151
  type: string
161
152
  enumDescriptions:
162
153
  type: array
163
- description: The descriptions for the enums. Each position maps to the corresponding
164
- value in the "enum" array.
154
+ description: The descriptions for the enums. Each position maps to the corresponding value in the "enum" array.
165
155
  items:
166
156
  type: string
167
157
  format:
168
158
  type: string
169
- description: 'An additional regular expression or key that helps constrain
170
- the value. For more details see: http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.23'
159
+ description: 'An additional regular expression or key that helps constrain the value. For more details see: http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.23'
171
160
  id:
172
161
  type: string
173
162
  description: Unique identifier for this schema.
174
163
  items:
175
164
  "$ref": JsonSchema
176
- description: If this is a schema for an array, this property is the schema
177
- for each element in the array.
165
+ description: If this is a schema for an array, this property is the schema for each element in the array.
178
166
  location:
179
167
  type: string
180
- description: Whether this parameter goes in the query or the path for REST
181
- requests.
168
+ description: Whether this parameter goes in the query or the path for REST requests.
182
169
  maximum:
183
170
  type: string
184
171
  description: The maximum value of this parameter.
@@ -187,21 +174,16 @@ schemas:
187
174
  description: The minimum value of this parameter.
188
175
  pattern:
189
176
  type: string
190
- description: 'The regular expression this parameter must conform to. Uses
191
- Java 6 regex format: http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html'
177
+ description: 'The regular expression this parameter must conform to. Uses Java 6 regex format: http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html'
192
178
  properties:
193
179
  type: object
194
- description: If this is a schema for an object, list the schema for each property
195
- of this object.
180
+ description: If this is a schema for an object, list the schema for each property of this object.
196
181
  additionalProperties:
197
182
  "$ref": JsonSchema
198
- description: A single property of this object. The value is itself a JSON
199
- Schema object describing this property.
183
+ description: A single property of this object. The value is itself a JSON Schema object describing this property.
200
184
  readOnly:
201
185
  type: boolean
202
- description: The value is read-only, generated by the service. The value cannot
203
- be modified by the client. If the value is included in a POST, PUT, or PATCH
204
- request, it is ignored by the service.
186
+ description: The value is read-only, generated by the service. The value cannot be modified by the client. If the value is included in a POST, PUT, or PATCH request, it is ignored by the service.
205
187
  repeated:
206
188
  type: boolean
207
189
  description: Whether this parameter may appear multiple times.
@@ -210,13 +192,10 @@ schemas:
210
192
  description: Whether the parameter is required.
211
193
  type:
212
194
  type: string
213
- description: 'The value type for this schema. A list of values can be found
214
- here: http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1'
195
+ description: 'The value type for this schema. A list of values can be found here: http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1'
215
196
  variant:
216
197
  type: object
217
- description: In a variant data type, the value of one property is used to
218
- determine how to interpret the entire entity. Its value must exist in a
219
- map of descriminant values to schema names.
198
+ description: In a variant data type, the value of one property is used to determine how to interpret the entire entity. Its value must exist in a map of descriminant values to schema names.
220
199
  properties:
221
200
  discriminant:
222
201
  type: string
@@ -262,18 +241,15 @@ schemas:
262
241
  batchPath:
263
242
  type: string
264
243
  description: The path for REST batch requests.
265
- default: batch
266
244
  canonicalName:
267
245
  type: string
268
- description: Indicates how the API name should be capitalized and split into
269
- various parts. Useful for generating pretty class names.
246
+ description: Indicates how the API name should be capitalized and split into various parts. Useful for generating pretty class names.
270
247
  description:
271
248
  type: string
272
249
  description: The description of this API.
273
250
  discoveryVersion:
274
251
  type: string
275
- description: Indicate the version of the Discovery API used to generate this
276
- doc.
252
+ description: Indicate the version of the Discovery API used to generate this doc.
277
253
  default: v1
278
254
  documentationLink:
279
255
  type: string
@@ -284,8 +260,7 @@ schemas:
284
260
  readOnly: true
285
261
  exponentialBackoffDefault:
286
262
  type: boolean
287
- description: Enable exponential backoff for suitable methods in the generated
288
- clients.
263
+ description: Enable exponential backoff for suitable methods in the generated clients.
289
264
  features:
290
265
  type: array
291
266
  description: A list of supported features for this API.
@@ -324,9 +299,7 @@ schemas:
324
299
  description: The name of this API.
325
300
  ownerDomain:
326
301
  type: string
327
- description: The domain of the owner of this API. Together with the ownerName
328
- and a packagePath values, this can be used to generate a library for this
329
- API which would have a unique fully qualified name.
302
+ description: The domain of the owner of this API. Together with the ownerName and a packagePath values, this can be used to generate a library for this API which would have a unique fully qualified name.
330
303
  ownerName:
331
304
  type: string
332
305
  description: The name of the owner of this API. See ownerDomain.
@@ -348,8 +321,7 @@ schemas:
348
321
  description: The resources in this API.
349
322
  additionalProperties:
350
323
  "$ref": RestResource
351
- description: An individual resource description. Contains methods and sub-resources
352
- related to this resource.
324
+ description: An individual resource description. Contains methods and sub-resources related to this resource.
353
325
  revision:
354
326
  type: string
355
327
  description: The version of this API.
@@ -382,15 +354,13 @@ schemas:
382
354
  description: Description of this method.
383
355
  etagRequired:
384
356
  type: boolean
385
- description: Whether this method requires an ETag to be specified. The ETag
386
- is sent as an HTTP If-Match or If-None-Match header.
357
+ description: Whether this method requires an ETag to be specified. The ETag is sent as an HTTP If-Match or If-None-Match header.
387
358
  httpMethod:
388
359
  type: string
389
360
  description: HTTP method used by this method.
390
361
  id:
391
362
  type: string
392
- description: A unique ID for this method. This property can be used to match
393
- methods between different versions of Discovery.
363
+ description: A unique ID for this method. This property can be used to match methods between different versions of Discovery.
394
364
  mediaUpload:
395
365
  type: object
396
366
  description: Media upload parameters.
@@ -413,13 +383,11 @@ schemas:
413
383
  properties:
414
384
  multipart:
415
385
  type: boolean
416
- description: True if this endpoint supports uploading multipart
417
- media.
386
+ description: True if this endpoint supports uploading multipart media.
418
387
  default: 'true'
419
388
  path:
420
389
  type: string
421
- description: The URI path to be used for upload. Should be used
422
- in conjunction with the basePath property at the api-level.
390
+ description: The URI path to be used for upload. Should be used in conjunction with the basePath property at the api-level.
423
391
  simple:
424
392
  type: object
425
393
  description: Supports uploading as a single HTTP request.
@@ -430,13 +398,10 @@ schemas:
430
398
  default: 'true'
431
399
  path:
432
400
  type: string
433
- description: The URI path to be used for upload. Should be used
434
- in conjunction with the basePath property at the api-level.
401
+ description: The URI path to be used for upload. Should be used in conjunction with the basePath property at the api-level.
435
402
  parameterOrder:
436
403
  type: array
437
- description: Ordered list of required parameters, serves as a hint to clients
438
- on how to structure their method signatures. The array is ordered such that
439
- the "most-significant" parameter appears first.
404
+ description: Ordered list of required parameters, serves as a hint to clients on how to structure their method signatures. The array is ordered such that the "most-significant" parameter appears first.
440
405
  items:
441
406
  type: string
442
407
  parameters:
@@ -447,8 +412,7 @@ schemas:
447
412
  description: Details for a single parameter in this method.
448
413
  path:
449
414
  type: string
450
- description: The URI path of this REST method. Should be used in conjunction
451
- with the basePath property at the api-level.
415
+ description: The URI path of this REST method. Should be used in conjunction with the basePath property at the api-level.
452
416
  request:
453
417
  type: object
454
418
  description: The schema for the request.
@@ -482,9 +446,7 @@ schemas:
482
446
  description: Whether this method supports subscriptions.
483
447
  useMediaDownloadService:
484
448
  type: boolean
485
- description: Indicates that downloads from this method should use the download
486
- service URL (i.e. "/download"). Only applies if the method supports media
487
- download.
449
+ description: Indicates that downloads from this method should use the download service URL (i.e. "/download"). Only applies if the method supports media download.
488
450
  RestResource:
489
451
  id: RestResource
490
452
  type: object
@@ -1,6 +1,19 @@
1
1
  require "scorpio/version"
2
+ require "pathname"
3
+ require "pp"
4
+ require "api_hammer/ycomb"
5
+ require "scorpio/json-schema-fragments"
2
6
 
3
7
  module Scorpio
8
+ def self.root
9
+ @root ||= Pathname.new(__FILE__).dirname.parent.expand_path
10
+ end
11
+
12
+ # generally put in code paths that are not expected to be valid control flow paths.
13
+ # rather a NotImplementedCorrectlyError. but that's too long.
14
+ class Bug < NotImplementedError
15
+ end
16
+
4
17
  proc { |v| define_singleton_method(:error_classes_by_status) { v } }.call({})
5
18
  class Error < StandardError; end
6
19
  class HTTPError < Error
@@ -53,13 +66,29 @@ module Scorpio
53
66
  error_classes_by_status.freeze
54
67
 
55
68
  autoload :Model, 'scorpio/model'
69
+ autoload :OpenAPI, 'scorpio/openapi'
70
+ autoload :Google, 'scorpio/google_api_document'
71
+ autoload :JSON, 'scorpio/json'
72
+ autoload :Schema, 'scorpio/schema'
56
73
 
57
74
  class << self
58
75
  def stringify_symbol_keys(hash)
59
76
  unless hash.is_a?(Hash)
60
- raise ArgumentError, "expected argument to be a Hash; got #{hash.class}: #{hash.inspect}"
77
+ raise ArgumentError, "expected argument to be a Hash; got #{hash.class}: #{hash.pretty_inspect}"
61
78
  end
62
79
  hash.map { |k,v| {k.is_a?(Symbol) ? k.to_s : k => v} }.inject({}, &:update)
63
80
  end
64
81
  end
82
+
83
+ module FingerprintHash
84
+ def ==(other)
85
+ other.respond_to?(:fingerprint) && other.fingerprint == self.fingerprint
86
+ end
87
+
88
+ alias eql? ==
89
+
90
+ def hash
91
+ fingerprint.hash
92
+ end
93
+ end
65
94
  end
@@ -0,0 +1,232 @@
1
+ require 'api_hammer/ycomb'
2
+ require 'scorpio/schema_object_base'
3
+
4
+ module Scorpio
5
+ module Google
6
+ apidoc_schema_doc = ::JSON.parse(Scorpio.root.join('documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest').read)
7
+ api_document_class = proc do |*key|
8
+ Scorpio.class_for_schema(Scorpio::JSON::Node.new_by_type(apidoc_schema_doc, ['schemas', *key]))
9
+ end
10
+
11
+ # naming these is not strictly necessary, but is nice to have.
12
+ # generated: puts Scorpio::Google::ApiDocument.document['schemas'].select { |k,v| v['type'] == 'object' }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = api_document_class.call('#{k}')" }
13
+ DirectoryList = api_document_class.call('DirectoryList')
14
+ JsonSchema = api_document_class.call('JsonSchema')
15
+ RestDescription = api_document_class.call('RestDescription')
16
+ RestMethod = api_document_class.call('RestMethod')
17
+ RestResource = api_document_class.call('RestResource')
18
+
19
+ # not generated
20
+ RestMethodRequest = api_document_class.call('RestMethod', 'properties', 'request')
21
+ RestMethodResponse = api_document_class.call('RestMethod', 'properties', 'response')
22
+
23
+ # google does a weird thing where it defines a schema with a $ref property where a json-schema is to be used in the document (method request and response fields), instead of just setting the schema to be the json-schema schema. we'll share a module across those schema classes that really represent schemas. is this confusingly meta enough?
24
+ module SchemaLike
25
+ def to_openapi
26
+ dup_doc = ::JSON.parse(::JSON.generate(object.content))
27
+ # openapi does not want an id field on schemas
28
+ dup_doc.delete('id')
29
+ if dup_doc['properties'].is_a?(Hash)
30
+ required_properties = dup_doc['properties'].select do |key, value|
31
+ value.is_a?(Hash) ? value.delete('required') : nil
32
+ end.keys
33
+ # put required before properties
34
+ unless required_properties.empty?
35
+ dup_doc = dup_doc.map do |k, v|
36
+ base = k == 'properties' ? {'required' => required_properties } : {}
37
+ base.merge({k => v})
38
+ end.inject({}, &:update)
39
+ end
40
+ end
41
+ dup_doc
42
+ end
43
+ end
44
+ [JsonSchema, RestMethodRequest, RestMethodResponse].each { |klass| klass.send(:include, SchemaLike) }
45
+
46
+ class RestDescription
47
+ def to_openapi_document(options = {})
48
+ Scorpio::OpenAPI::Document.new(to_openapi_hash(options))
49
+ end
50
+
51
+ def to_openapi_hash(options = {})
52
+ # we will be modifying the api document (RestDescription). clone self and modify that one.
53
+ ad = self.class.new(::JSON.parse(::JSON.generate(object.document)))
54
+ ad_methods = []
55
+ if ad['methods']
56
+ ad_methods += ad['methods'].map do |mn, m|
57
+ m.tap do
58
+ m.send(:define_singleton_method, :resource_name) { }
59
+ m.send(:define_singleton_method, :method_name) { mn }
60
+ end
61
+ end
62
+ end
63
+ ad_methods += ad.resources.map do |rn, r|
64
+ (r['methods'] || {}).map do |mn, m|
65
+ m.tap do
66
+ m.send(:define_singleton_method, :resource_name) { rn }
67
+ m.send(:define_singleton_method, :method_name) { mn }
68
+ end
69
+ end
70
+ end.inject([], &:+)
71
+
72
+ paths = ad_methods.group_by { |m| m['path'] }.map do |path, path_methods|
73
+ unless path =~ %r(\A/)
74
+ path = '/' + path
75
+ end
76
+ operations = path_methods.group_by { |m| m['httpMethod'] }.map do |http_method, http_method_methods|
77
+ if http_method_methods.size > 1
78
+ #raise("http method #{http_method} at path #{path} not unique: #{http_method_methods.pretty_inspect}")
79
+ end
80
+ method = http_method_methods.first
81
+ unused_path_params = Addressable::Template.new(path).variables
82
+ {http_method.downcase => {}.tap do |operation|
83
+ #operation['tags'] = []
84
+ #operation['summary'] =
85
+ operation['description'] = method['description'] if method['description']
86
+ if method.resource_name && options[:x]
87
+ operation['x-resource'] = method.resource_name
88
+ operation['x-resource-method'] = method.method_name
89
+ end
90
+ #operation['externalDocs'] =
91
+ operation['operationId'] = method['id'] || (method.resource_name ? "#{method.resource_name}.#{method.method_name}" : method.method_name)
92
+ #operation['produces'] =
93
+ #operation['consumes'] =
94
+ if method['parameters']
95
+ operation['parameters'] = method['parameters'].map do |name, parameter|
96
+ {}.tap do |op_param|
97
+ op_param['description'] = parameter.description if parameter.description
98
+ op_param['name'] = name
99
+ op_param['in'] = if parameter.location
100
+ parameter.location
101
+ elsif unused_path_params.include?(name)
102
+ 'path'
103
+ else
104
+ 'query'
105
+ # unused: header, formdata, body
106
+ end
107
+ unused_path_params.delete(name) if op_param['in'] == 'path'
108
+ op_param['required'] = parameter.key?('required') ? parameter['required'] : op_param['in'] == 'path' ? true : false
109
+ op_param['type'] = parameter.type || 'string'
110
+ op_param['format'] = parameter.format if parameter.format
111
+ end
112
+ end
113
+ end
114
+ if unused_path_params.any?
115
+ operation['parameters'] ||= []
116
+ operation['parameters'] += unused_path_params.map do |param_name|
117
+ {
118
+ 'name' => param_name,
119
+ 'in' => 'path',
120
+ 'required' => true,
121
+ 'type' => 'string',
122
+ }
123
+ end
124
+ end
125
+ if method['request']
126
+ operation['parameters'] ||= []
127
+ operation['parameters'] << {
128
+ 'name' => 'body',
129
+ 'in' => 'body',
130
+ 'required' => true,
131
+ 'schema' => method['request'],
132
+ }
133
+ end
134
+ if method['response']
135
+ operation['responses'] = {
136
+ 'default' => {
137
+ 'description' => 'default response',
138
+ 'schema' => method['response'],
139
+ },
140
+ }
141
+ end
142
+ end}
143
+ end.inject({}, &:update)
144
+
145
+ {path => operations}
146
+ end.inject({}, &:update)
147
+
148
+ openapi = {
149
+ 'swagger' => '2.0',
150
+ 'info' => { #/definitions/info
151
+ 'title' => ad.title || ad.name,
152
+ 'description' => ad.description,
153
+ 'version' => ad.version || '',
154
+ #'termsOfService' => '',
155
+ 'contact' => {
156
+ 'name' => ad.ownerName,
157
+ #'url' =>
158
+ #'email' => '',
159
+ }.reject { |_, v| v.nil? },
160
+ #'license' => {
161
+ #'name' => '',
162
+ #'url' => '',
163
+ #},
164
+ },
165
+ 'host' => ad.rootUrl ? Addressable::URI.parse(ad.rootUrl).host : ad.baseUrl ? Addressable::URI.parse(ad.rootUrl).host : ad.name, # uhh ... got nothin' better
166
+ 'basePath' => begin
167
+ path = ad.servicePath || ad.basePath || (ad.baseUrl ? Addressable::URI.parse(ad.baseUrl).path : '/')
168
+ path =~ %r(\A/) ? path : "/" + path
169
+ end,
170
+ 'schemes' => ad.rootUrl ? [Addressable::URI.parse(ad.rootUrl).scheme] : ad.baseUrl ? [Addressable::URI.parse(ad.rootUrl).scheme] : [], #/definitions/schemesList
171
+ 'consumes' => ['application/json'], # we'll just make this assumption
172
+ 'produces' => ['application/json'],
173
+ 'paths' => paths, #/definitions/paths
174
+ }
175
+ if ad.schemas
176
+ openapi['definitions'] = ad.schemas
177
+ ad.schemas.each do |name, schema|
178
+ openapi = ycomb do |rec|
179
+ proc do |object|
180
+ if object.respond_to?(:to_hash)
181
+ object.merge(object.map do |k, v|
182
+ if k == '$ref' && (v == schema['id'] || v == "#/schemas/#{name}" || v == name)
183
+ {k => "#/definitions/#{name}"}
184
+ else
185
+ ycomb do |toopenapirec|
186
+ proc do |toopenapiobject|
187
+ toopenapiobject = toopenapiobject.to_openapi if toopenapiobject.respond_to?(:to_openapi)
188
+ if toopenapiobject.respond_to?(:to_hash)
189
+ toopenapiobject.map { |k, v| {toopenapirec.call(k) => toopenapirec.call(v)} }.inject({}, &:update)
190
+ elsif toopenapiobject.respond_to?(:to_ary)
191
+ toopenapiobject.map(&toopenapirec)
192
+ elsif toopenapiobject.is_a?(Symbol)
193
+ toopenapiobject.to_s
194
+ elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| toopenapiobject.is_a?(c) }
195
+ toopenapiobject
196
+ else
197
+ raise(TypeError, "bad (not jsonifiable) object: #{toopenapiobject.pretty_inspect}")
198
+ end
199
+ end
200
+ end.call({k => rec.call(v)})
201
+ end
202
+ end.inject({}, &:merge))
203
+ elsif object.respond_to?(:to_ary)
204
+ object.map(&rec)
205
+ else
206
+ object
207
+ end
208
+ end
209
+ end.call(openapi)
210
+ end
211
+ end
212
+ # check we haven't got anything that shouldn't go in a openapi document
213
+ openapi = ycomb do |rec|
214
+ proc do |object|
215
+ object = object.to_openapi if object.respond_to?(:to_openapi)
216
+ if object.respond_to?(:to_hash)
217
+ object.map { |k, v| {rec.call(k) => rec.call(v)} }.inject({}, &:update)
218
+ elsif object.respond_to?(:to_ary)
219
+ object.map(&rec)
220
+ elsif object.is_a?(Symbol)
221
+ object.to_s
222
+ elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| object.is_a?(c) }
223
+ object
224
+ else
225
+ raise(TypeError, "bad (not jsonifiable) object: #{object.pretty_inspect}")
226
+ end
227
+ end
228
+ end.call(openapi)
229
+ end
230
+ end
231
+ end
232
+ end