openapi-sourcetools 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b790daa9dc40626f360f32ba98853c2b5410e5214d0d2ef32334df1858a9262
4
- data.tar.gz: 842e1b54d72995112b38f678f3b0f853d9e0e6ea03652d56797715cb215e2c35
3
+ metadata.gz: 44a305843fc718a7b958b3afbaf42f6f215f5a23e23c5485799222b7030a1757
4
+ data.tar.gz: 68d37c654b903c17d8aa64d96fdcecbca2c3e7c56b2b5629e2e80f243d3993e1
5
5
  SHA512:
6
- metadata.gz: 5fa1f5ac001251e2d5b56b701bac7d8489efd01fdbbfc2884a0be84c7c5f86dd1a85d1e1cff6120ae09d0d462912711a70ad269d2c5c740c4639a6e495057e78
7
- data.tar.gz: ec9dc7d99bfcd12387d163a88c7502052c44cc0159c7bd5865d86a17af0511e979224289ec56f7f049400f4280e02fa583498a553791be9f1b762153cac4b903
6
+ metadata.gz: 274b22fbac33a899c3a2102772d2b3e6bd44aab656b67e74a1e65fbd07edcab86bc7fcde94e41184a51e3435ec7cbc5d826c407bbbbed322ff0e2eeec1168156
7
+ data.tar.gz: a41c84712831166ef6ab0b2233a9c491ea1d87b73d6f1d67bab2ef821a937d2216e99a471ed9210427cb7b8c9824160c257cc6ef856a971d4804812a9b9a5a5a
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/apiobjects'
8
+ require_relative '../lib/openapi/sourcetools/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def main
14
+ input_name = nil
15
+ output_name = nil
16
+ path = %w[components mediaTypes]
17
+ components = OpenAPISourceTools::ApiObjects::Components.new(path, 'MediaType')
18
+ replacer = OpenAPISourceTools::ApiObjects::ValueSubValueReplacer.new('content', components)
19
+
20
+ parser = OptionParser.new do |opts|
21
+ opts.summary_indent = ' '
22
+ opts.summary_width = 26
23
+ opts.banner = 'Usage: openapi-addmediatypes [options]'
24
+ opts.separator ''
25
+ opts.separator 'Options:'
26
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
27
+ input_name = f
28
+ end
29
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
30
+ output_name = f
31
+ end
32
+ components.add_options(opts)
33
+ opts.on('-h', '--help', 'Print this help and exit.') do
34
+ $stdout.puts %(#{opts}
35
+
36
+ Loads API document in OpenAPI format and moves media types under components
37
+ and replaces the original with reference.
38
+
39
+ #{components.help}
40
+ )
41
+ exit 0
42
+ end
43
+ end
44
+ parser.order!
45
+
46
+ doc = OpenAPISourceTools::Common.load_source(input_name)
47
+ return 2 if doc.nil?
48
+
49
+ components.items = doc.dig(*path) || {}
50
+ replacer.replace(doc.dig('components', 'headers') || {})
51
+ replacer.replace(doc.dig('components', 'responses') || {})
52
+ replacer.replace(doc.dig('components', 'parameters') || {})
53
+ replacer.replace(doc.fetch('paths', {}))
54
+ OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
55
+
56
+ OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
57
+ end
58
+
59
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/apiobjects'
8
+ require_relative '../lib/openapi/sourcetools/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def main
14
+ input_name = nil
15
+ output_name = nil
16
+ path = %w[components requestBodies]
17
+ components = OpenAPISourceTools::ApiObjects::Components.new(path, 'RequestBody')
18
+ replacer = OpenAPISourceTools::ApiObjects::ValueReplacer.new('requestBody', components)
19
+
20
+ parser = OptionParser.new do |opts|
21
+ opts.summary_indent = ' '
22
+ opts.summary_width = 26
23
+ opts.banner = 'Usage: openapi-addrequestbodies [options]'
24
+ opts.separator ''
25
+ opts.separator 'Options:'
26
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
27
+ input_name = f
28
+ end
29
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
30
+ output_name = f
31
+ end
32
+ components.add_options(opts)
33
+ opts.on('-h', '--help', 'Print this help and exit.') do
34
+ $stdout.puts %(#{opts}
35
+
36
+ Loads API document in OpenAPI format and moves request body under components
37
+ and replaces the original with reference.
38
+
39
+ #{components.help}
40
+ )
41
+ exit 0
42
+ end
43
+ end
44
+ parser.order!
45
+
46
+ doc = OpenAPISourceTools::Common.load_source(input_name)
47
+ return 2 if doc.nil?
48
+
49
+ components.items = doc.dig(*path) || {}
50
+ replacer.replace(doc.dig('components', 'pathItems') || {})
51
+ replacer.replace(doc.fetch('paths', {}))
52
+ OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
53
+
54
+ OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
55
+ end
56
+
57
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
@@ -29,31 +29,39 @@ def replace_inlines(obj, components, top_level_name = nil)
29
29
  return true
30
30
  end
31
31
  # Is inlined, process parts recursively.
32
- items = obj['allOf']
33
- items = obj['anyOf'] if items.nil?
34
- items = obj['oneOf'] if items.nil?
35
- # All above need special treatment in code generation templates.
36
- return replace_inlines(items, components) unless items.nil?
37
- t = obj['type']
38
- if t.nil? # Some kind of intermediate-level object.
39
- (obj.keys.sort! { |a, b| a.to_s <=> b.to_s }).each do |key|
32
+ %w[allOf anyOf oneOf items prefixItems additionalProperties unevaluatedItems
33
+ unevaluatedProperties if then else not
34
+ ].each do |prop_name|
35
+ return false unless replace_inlines(obj[prop_name], components)
36
+ end
37
+ %w[properties patternProperties dependentSchemas].each do |prop_name|
38
+ props = obj[prop_name]
39
+ next if props.nil?
40
+ props.keys.sort!.each do |name|
41
+ return false unless replace_inlines(props[name], components)
42
+ end
43
+ end
44
+ looks_like_type = false
45
+ # required is omitted since it is in schema and in parameters.
46
+ %w[type items prefixItems properties patternProperties additionalProperties
47
+ unevaluatedItems unevaluatedProperties enum const minimum maximum multipleOf
48
+ exclusiveMinimum exclusiveMaximum maxLength minLength pattern maxItems minItems
49
+ uniqueItems maxContains minContains maxProperties minProperties propertyNames
50
+ dependentRequired format contentEncoding contentMediaType contentSchema
51
+ ].each do |kw|
52
+ if obj.key?(kw)
53
+ looks_like_type = true
54
+ break
55
+ end
56
+ end
57
+ unless looks_like_type
58
+ # Some kind of intermediate-level object.
59
+ obj.keys.sort! { |a, b| a.to_s <=> b.to_s }.each do |key|
40
60
  next if key == 'securitySchemes'
41
61
  return false unless replace_inlines(obj[key], components)
42
62
  end
43
63
  return true
44
64
  end
45
- case t
46
- when 'array'
47
- return false unless replace_inlines(obj['items'], components)
48
- when 'object'
49
- %w[properties patternProperties].each do |prop_name|
50
- props = obj.fetch(prop_name, {})
51
- props.keys.sort!.each do |name|
52
- return false unless replace_inlines(props[name], components)
53
- end
54
- end
55
- return false unless replace_inlines(obj['additionalProperties'], components)
56
- end
57
65
  r = components.ref_string(top_level_name) || components.reference(obj)
58
66
  components.store_anchor(obj, r)
59
67
  components.to_reference_object(obj, r) if top_level_name.nil?
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2024-2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/common'
8
+ require_relative '../lib/openapi/sourcetools/config'
9
+ require_relative '../lib/openapi/sourcetools/gen'
10
+ require 'optparse'
11
+ include OpenAPISourceTools
12
+
13
+
14
+ def main
15
+ prefix = nil
16
+ keys = nil
17
+ read = false
18
+
19
+ parser = OptionParser.new do |opts|
20
+ opts.summary_indent = ' '
21
+ opts.summary_width = 26
22
+ opts.banner = 'Usage: openapi-generate-checkconfig [options]'
23
+ opts.separator ''
24
+ opts.separator 'Options:'
25
+ opts.on('-c', '--config NAME', 'Use config prefix NAME. (config:NAME)') do |c|
26
+ prefix = c
27
+ end
28
+ opts.on('-s', '--separator STRING', 'Use separator STRING. (separator:STRING)') do |s|
29
+ Gen.separator = s unless s.empty?
30
+ end
31
+ opts.on('-r', '--[no-]read', "Show if file is read by read_contents, default #{Common.yesno(read)}.") do |b|
32
+ read = b
33
+ end
34
+ opts.on('-k', '--keys KEYS', 'Show all values of KEYS.') do |ks|
35
+ keys = ks.split('/').reject(&:empty?) unless ks.empty?
36
+ keys = nil if !keys.nil? && keys.empty?
37
+ end
38
+ opts.on('-h', '--help', 'Print this help and exit.') do
39
+ $stdout.puts %(#{opts}
40
+ Displays configurations files that are found using specific prefix and an
41
+ optional separator character or string.
42
+
43
+ If KEYS, separated by "/", is given, the value of that key path is displayed.
44
+ That and read check require the configuration files to be YAML/JSON.
45
+ )
46
+ exit 0
47
+ end
48
+ end
49
+ parser.order!
50
+
51
+ return Common.aargh('Config must be given.', 1) if prefix.nil?
52
+ cfgs = ConfigLoader.find_files(name_prefix: prefix)
53
+ ConfigLoader.read_contents(cfgs) if read || !keys.nil?
54
+ root_dir, _p = ConfigLoader.prepare_prefix(prefix, Gen.wd)
55
+ drop = root_dir.size + 1
56
+ cfgs.each do |cfg|
57
+ ks = cfg.keys.join('/')
58
+ info = "#{cfg.root}: #{ks.empty? ? '(root)' : ks}, #{cfg.path[drop..]}"
59
+ ki = nil
60
+ unless keys.nil? || cfg.content.nil?
61
+ v = cfg.content.dig(*keys)
62
+ ki = v.to_s unless v.nil?
63
+ end
64
+ ri = read ? !cfg.content.nil? : nil
65
+ next if (read || !keys.nil?) && ki.nil? && ri.nil?
66
+ puts info
67
+ puts " #{ki}" unless ki.nil?
68
+ puts " File is #{ri ? '' : 'not '}read." unless ri.nil?
69
+ end
70
+ 0
71
+ end
72
+
73
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
data/bin/openapi-order ADDED
@@ -0,0 +1,574 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/common'
8
+ require_relative '../lib/openapi/sourcetools/order'
9
+ require 'optparse'
10
+ require 'yaml'
11
+ require 'json'
12
+
13
+ # Functions related to order instructions.
14
+
15
+ def metadata
16
+ 'openapi-order-metadata'
17
+ end
18
+
19
+ def metadata_key
20
+ 'key'
21
+ end
22
+
23
+ def metadata_regexp
24
+ 'regexp'
25
+ end
26
+
27
+ def default_order_data(order_key, regexp_prefix)
28
+ <<EOF
29
+ ---
30
+ #{metadata}:
31
+ #{metadata_key}: "#{order_key}"
32
+ #{metadata_regexp}: "#{regexp_prefix}"
33
+ # Generic orders for any location of a key.
34
+ '**':
35
+ servers:
36
+ #{order_key}: &ServerObject_order
37
+ - url
38
+ - name
39
+ - description
40
+ - variables
41
+ '*':
42
+ #{order_key}: &ServerObject
43
+ - name
44
+ - description
45
+ variables:
46
+ #{order_key}:
47
+ - '*'
48
+ '*':
49
+ #{order_key}: &ServerVariableObject
50
+ - description
51
+ - enum
52
+ - default
53
+ server: *ServerObject
54
+ parameters:
55
+ #{order_key}: &ParameterObject_order
56
+ - name
57
+ - in
58
+ '*': &ParameterObject
59
+ #{order_key}:
60
+ - '$ref'
61
+ - name
62
+ - in
63
+ - required
64
+ - deprecated
65
+ - description
66
+ - content
67
+ - schema
68
+ - '*'
69
+ - #{regexp_prefix}^example[s]?$
70
+ schema: &Schema
71
+ #{order_key}:
72
+ - '$ref'
73
+ - title
74
+ - description
75
+ - deprecated
76
+ - type
77
+ - format
78
+ - enum
79
+ - const
80
+ - default
81
+ - readOnly
82
+ - writeOnly
83
+ # Numeric types.
84
+ - minimum
85
+ - exclusiveMinimum
86
+ - exclusiveMaximum
87
+ - maximum
88
+ - multipleOf
89
+ # Strings.
90
+ - minLength
91
+ - maxLength
92
+ - pattern
93
+ # Core.
94
+ - allOf
95
+ - anyOf
96
+ - oneOf
97
+ - not
98
+ - if
99
+ - then
100
+ - else
101
+ # Objects.
102
+ - required
103
+ - dependentRequired
104
+ - minProperties
105
+ - maxProperties
106
+ - properties
107
+ - #{regexp_prefix}^.+Properties$
108
+ - propertyNames
109
+ - dependentSchemas
110
+ - discriminator
111
+ # Arrays.
112
+ - minItems
113
+ - maxItems
114
+ - uniqueItems
115
+ - items
116
+ - #{regexp_prefix}^.+Items$
117
+ - contains
118
+ - minContains
119
+ - maxContains
120
+ # Content
121
+ - #{regexp_prefix}^content.*$
122
+ - examples
123
+ required:
124
+ #{order_key}:
125
+ - '*'
126
+ dependentRequired:
127
+ #{order_key}:
128
+ - '*'
129
+ '*':
130
+ #{order_key}:
131
+ - '*'
132
+ allOf: &Schema_map
133
+ '*': *Schema
134
+ anyOf: *Schema_map
135
+ oneOf: *Schema_map
136
+ not: *Schema
137
+ if: *Schema
138
+ then: *Schema
139
+ else: *Schema
140
+ properties: &Schema_ordered_map
141
+ #{order_key}:
142
+ - '*'
143
+ '*': *Schema
144
+ patternProperties: *Schema_ordered_map
145
+ additionalProperties: *Schema
146
+ propertyNames: *Schema
147
+ items: *Schema
148
+ contains: *Schema
149
+ prefixItems: *Schema_map
150
+ unevaluatedItems: *Schema
151
+ unevaluatedProperties: *Schema
152
+ dependentSchemas: *Schema_ordered_map
153
+ discriminator:
154
+ #{order_key}:
155
+ - propertyName
156
+ - mapping
157
+ - defaultMapping
158
+ content:
159
+ #{order_key}:
160
+ - '*'
161
+ '*': &MediaTypeObject
162
+ #{order_key}:
163
+ - schema
164
+ - #{regexp_prefix}.*Schema$
165
+ - #{regexp_prefix}^(.*E|e)ncoding$
166
+ - '*'
167
+ - #{regexp_prefix}^example[s]?$
168
+ headers: &HeaderObject
169
+ #{order_key}:
170
+ - description
171
+ - required
172
+ - '*'
173
+ - schema
174
+ '*': *ParameterObject
175
+ responses:
176
+ #{order_key}:
177
+ - '*'
178
+ - default
179
+ '*': &ResponseObject
180
+ #{order_key}:
181
+ - '$ref'
182
+ - summary
183
+ - description
184
+ - headers
185
+ - content
186
+ - links
187
+ links:
188
+ #{order_key}:
189
+ - '*'
190
+ '*': &LinkObject
191
+ #{order_key}:
192
+ - description
193
+ - #{regexp_prefix}^operation.*
194
+ - parameters
195
+ - requestBody
196
+ requestBody: &RequestBodyObject
197
+ #{order_key}:
198
+ - description
199
+ - required
200
+ - content
201
+ security:
202
+ #{order_key}: &SecurityRequirementObject_order
203
+ - '*'
204
+ webhooks:
205
+ #{order_key}:
206
+ - '*'
207
+ '*': &PathItemObject
208
+ #{order_key}:
209
+ - '$ref'
210
+ - summary
211
+ - description
212
+ - servers
213
+ - parameters
214
+ - '*'
215
+ - additionalOperations
216
+ - re:^x-openapi-sourcetools.*$
217
+ '*':
218
+ #{order_key}: &OperationObject
219
+ - '$ref'
220
+ - operationId
221
+ - summary
222
+ - description
223
+ - parameters
224
+ - requestBody
225
+ - responses
226
+ additionalOperations:
227
+ #{order_key}:
228
+ - '*'
229
+ '*': *OperationObject
230
+ callbacks:
231
+ #{order_key}:
232
+ - '*'
233
+ '*': *PathItemObject
234
+ #{order_key}:
235
+ - openapi
236
+ - '$self'
237
+ - jsonSchemaDialect
238
+ - info
239
+ - servers
240
+ - '*'
241
+ - paths
242
+ - components
243
+ info:
244
+ #{order_key}:
245
+ - title
246
+ - version
247
+ - summary
248
+ - description
249
+ - contact
250
+ - license
251
+ - termsOfService
252
+ contact:
253
+ #{order_key}:
254
+ - name
255
+ license:
256
+ #{order_key}:
257
+ - name
258
+ servers:
259
+ #{order_key}: *ServerObject_order
260
+ '*': *ServerObject
261
+ security:
262
+ #{order_key}: *SecurityRequirementObject_order
263
+ tags: # Do not order tags array but do order Tag Objects.
264
+ '*':
265
+ #{order_key}:
266
+ - name
267
+ - summary
268
+ - description
269
+ paths:
270
+ #{order_key}:
271
+ - '*'
272
+ '*': *PathItemObject
273
+ components:
274
+ #{order_key}:
275
+ - schemas
276
+ - '*'
277
+ - securitySchemes
278
+ - links
279
+ - callbacks
280
+ - examples
281
+ # Regular expressions are for names from various openapi-add* programs.
282
+ # Variations for number of digits ensure ordering by number.
283
+ schemas:
284
+ #{order_key}:
285
+ - '*'
286
+ - #{regexp_prefix}^Schema[0-9]{1}x$
287
+ - #{regexp_prefix}^Schema[0-9]{2}x$
288
+ - #{regexp_prefix}^Schema[0-9]{3,}x$
289
+ '*': *Schema
290
+ responses:
291
+ #{order_key}:
292
+ - '*'
293
+ - #{regexp_prefix}^Response[0-9]{1}x$
294
+ - #{regexp_prefix}^Response[0-9]{2}x$
295
+ - #{regexp_prefix}^Response[0-9]{3,}x$
296
+ '*': *ResponseObject
297
+ parameters:
298
+ #{order_key}:
299
+ - '*'
300
+ - #{regexp_prefix}^Parameter[0-9]{1}x$
301
+ - #{regexp_prefix}^Parameter[0-9]{2}x$
302
+ - #{regexp_prefix}^Parameter[0-9]{3,}x$
303
+ '*': *ParameterObject
304
+ requestBodies:
305
+ #{order_key}:
306
+ - '*'
307
+ - #{regexp_prefix}^RequestBody[0-9]{1}x$
308
+ - #{regexp_prefix}^RequestBody[0-9]{2}x$
309
+ - #{regexp_prefix}^RequestBody[0-9]{3,}x$
310
+ '*': *RequestBodyObject
311
+ headers:
312
+ #{order_key}:
313
+ - '*'
314
+ - #{regexp_prefix}^Header[0-9]{1}x$
315
+ - #{regexp_prefix}^Header[0-9]{2}x$
316
+ - #{regexp_prefix}^Header[0-9]{3,}x$
317
+ '*': *HeaderObject
318
+ securitySchemes:
319
+ #{order_key}:
320
+ - '*'
321
+ links:
322
+ #{order_key}:
323
+ - '*'
324
+ '*': *LinkObject
325
+ callbacks:
326
+ #{order_key}:
327
+ - '*'
328
+ '*': *PathItemObject
329
+ pathItems:
330
+ #{order_key}:
331
+ - '*'
332
+ '*': *PathItemObject
333
+ mediaTypes:
334
+ #{order_key}:
335
+ - '*'
336
+ - #{regexp_prefix}^MediaType[0-9]{1}x$
337
+ - #{regexp_prefix}^MediaType[0-9]{2}x$
338
+ - #{regexp_prefix}^MediaType[0-9]{3,}x$
339
+ '*': *MediaTypeObject
340
+ examples:
341
+ #{order_key}:
342
+ - '*'
343
+ EOF
344
+ end
345
+
346
+ def default_order(order_key, regexp_prefix)
347
+ YAML.load(default_order_data(order_key, regexp_prefix), aliases: true)
348
+ end
349
+
350
+ # This needs to extract all path/order pairs, store and sort.
351
+
352
+ def extract_orders(path, order, collection, order_key, regexp_prefix)
353
+ return true unless order.is_a?(Hash)
354
+ order.each do |key, value|
355
+ if key == order_key
356
+ unless value.is_a?(Array)
357
+ OpenAPISourceTools::Common.aargh("Not an array: #{path.join('.')}.#{order_key} =\n#{YAML.dump(value)}")
358
+ return false
359
+ end
360
+ value.each_with_index do |v, k|
361
+ next if v.is_a?(String)
362
+ OpenAPISourceTools::Common.aargh("Not a string: #{path}.#{order_key}[#{k}] =\n#{YAML.dump(v)}")
363
+ return false
364
+ end
365
+ key_path = OpenAPISourceTools::Ordering::KeyPath.new(path, regexp_prefix)
366
+ key_order = OpenAPISourceTools::Ordering::Order.new(value, regexp_prefix)
367
+ collection.push(OpenAPISourceTools::Ordering::KeyPathOrder.new(key_path, key_order))
368
+ else
369
+ key = key.to_s unless key.is_a?(String)
370
+ path.push(key)
371
+ return false unless extract_orders(path, value, collection, order_key, regexp_prefix)
372
+ path.pop
373
+ end
374
+ end
375
+ end
376
+
377
+ # Functions related to applying the order to the document.
378
+
379
+ def reorder(orders, path, item)
380
+ return false unless OpenAPISourceTools::Ordering::Order.orderable?(item)
381
+ orders.each do |order|
382
+ next unless order.path.match?(path)
383
+ order.order.apply(item)
384
+ break
385
+ end
386
+ true
387
+ end
388
+
389
+ def order_document(orders, path, item)
390
+ return unless reorder(orders, path, item) # Scalars do not pass.
391
+ if item.is_a?(Array)
392
+ item.each_with_index do |element, idx|
393
+ # This needs something and using index as string matches the '*'
394
+ # used when elements of the array are meant to be processed. By
395
+ # using the index alone, the '*' can be replaced by a regular
396
+ # expression that matches specific indices, if so desired.
397
+ path.push(idx.to_s)
398
+ order_document(orders, path, element)
399
+ path.pop
400
+ end
401
+ else
402
+ item.each do |key, value|
403
+ key = key.to_s unless key.is_a?(String)
404
+ path.push(key)
405
+ order_document(orders, path, value)
406
+ path.pop
407
+ end
408
+ end
409
+ end
410
+
411
+ # Functions related to saving the output.
412
+
413
+ def to_json(doc, _line_width)
414
+ JSON.dump(doc)
415
+ end
416
+
417
+ def to_pretty_json(doc, _line_width)
418
+ JSON.pretty_generate(doc)
419
+ end
420
+
421
+ def to_yaml(doc, line_width)
422
+ YAML.dump(doc, line_width: line_width)
423
+ end
424
+
425
+ def main
426
+ input_name = nil
427
+ output_name = nil
428
+ order_name = nil
429
+ order_key = 'x-order'
430
+ regexp_prefix = 're:'
431
+ format = 'yaml'
432
+ formats = {
433
+ 'yaml' => method(:to_yaml),
434
+ 'json' => method(:to_json),
435
+ 'pretty_json' => method(:to_pretty_json)
436
+ }
437
+ line_width = 80
438
+ output_default = false
439
+
440
+ parser = OptionParser.new do |opts|
441
+ opts.summary_indent = ' '
442
+ opts.summary_width = 20
443
+ opts.banner = 'Usage: openapi-order [options]'
444
+ opts.separator ''
445
+ opts.separator 'Options:'
446
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
447
+ input_name = f
448
+ end
449
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
450
+ output_name = f
451
+ end
452
+ opts.on('--order FILE', 'Read order instructions from FILE.') do |f|
453
+ order_name = f
454
+ end
455
+ opts.on('-f', '--format FORMAT', "Output format: #{formats.keys.sort!.join(', ')}, default: #{format}") do |f|
456
+ unless formats.key?(f.downcase)
457
+ exit(OpenAPISourceTools::Common.aargh("Unknown format: #{f}", 1))
458
+ end
459
+ format = f
460
+ end
461
+ opts.on('-w', '--width WIDTH', "Set YAML line width, default: #{line_width}") do |w|
462
+ line_width = w.to_i
463
+ end
464
+ opts.separator ''
465
+ opts.separator 'Order options:'
466
+ opts.on('--default', 'Output default order to --output, ignore input.') do
467
+ output_default = true
468
+ end
469
+ opts.on('-k', '--key KEY', "Use KEY as default order key, default: #{order_key}") do |k|
470
+ order_key = k
471
+ end
472
+ opts.on('-r', '--regexp PREFIX', "Use PREFIX to indicate regular expression, default: #{regexp_prefix}") do |p|
473
+ regexp_prefix = p
474
+ end
475
+ opts.on('-h', '--help', 'Print this help and exit.') do
476
+ $stdout.puts %(#{opts}
477
+ Loads YAML document and applies ordering to object keys, and arrays with
478
+ objects, when an order matches the key path to object.
479
+
480
+ Can be used on generic YAML or JSON files. Default order is for OpenAPI
481
+ documents.
482
+
483
+ The order instructions key "#{order_key}" value must be an array of strings.
484
+ The values can be any combination of the following:
485
+ - Literal key name.
486
+ - Regular expression starting with "#{regexp_prefix}".
487
+ - "*" that matches any key that does not match anything else.
488
+ First match against a value determines the position, and "*" receives all
489
+ unmatched keys. If order array has no "*", every unmatched key will be placed
490
+ last. Only the first "*" has any effect. Multiple keys placed in the same
491
+ position are sorted within that position.
492
+
493
+ The path to the "#{order_key}" can have any combination of the following:
494
+ - Literal key name.
495
+ - Regular expression starting with "#{regexp_prefix}".
496
+ - "*" that matches one key of any value.
497
+ - "**" that matches one or more keys of any value.
498
+ The path is used to determine if a value in the document should be ordered
499
+ using the instructions. At top level the path is empty and applies to the root
500
+ of the document. Long paths are considered to be more specific than short
501
+ paths, because they supposedly contain more literal keys or limiting regular
502
+ expressions so thay are intended for a specific case. Of paths of same length
503
+ one with "**" is considered less specific than one without.
504
+
505
+ The path to a value in the document is matched against paths to "#{order_key}"
506
+ to find the most specific order. For elements in an array, the last item in
507
+ the path is the element index as a string. Hence you can use a "*" parallel
508
+ to "#{order_key}" to match any index, but also literals or regular expressions
509
+ to match specific index can be used.
510
+
511
+ For object value in the document, the order is used to sort the keys of the
512
+ object. Absence of a key in the object has no effect. Any non-string key is
513
+ converted to string for ordering purposes. Original keys are retained.
514
+
515
+ For array value in the document, the order is used to select values of keys
516
+ from array elements to use in comparison for sorting the array.
517
+ - Using known object property names is recommended.
518
+ - Values of keys should be scalars and have the same type for same key.
519
+ - Missing key places the element after one that has the key.
520
+ - Object and array values are considered to be a missing value.
521
+ - Elements that are not objects end up last because they have no keys.
522
+ Mixture of objects, arrays, and scalars is not expected in the intended use.
523
+
524
+ Scalar value in the document is not affected.
525
+
526
+ Non-string keys in the document and order paths are converted to strings for
527
+ processing.
528
+
529
+ The --key, --regexp, --format, and --output apply to --default output. If
530
+ format is YAML, the internal YAML document is output as is.
531
+
532
+ To avoid errors, the value of --key should not appear in the document so that
533
+ it only appears as order instructions key in the order document.
534
+
535
+ In case the document has a key that starts with "#{regexp_prefix}" you can change it in the
536
+ order file or use "#{regexp_prefix}^#{regexp_prefix}the_rest$" as an anchored regular
537
+ expression that only matches the key. The ":" is part of the prefix itself,
538
+ so provide it if you want to keep it. Useful for readability.
539
+
540
+ When reading an order file, --key and --regexp are over-ridden by root-level
541
+ object "#{metadata}" keys "#{metadata_key}" and "#{metadata_regexp}" values.
542
+ )
543
+ exit 0
544
+ end
545
+ end
546
+ parser.order!
547
+
548
+ if output_default
549
+ if format == 'yaml'
550
+ doc = default_order_data(order_key, regexp_prefix)
551
+ else
552
+ doc = default_order(order_key, regexp_prefix)
553
+ doc = formats[format].call(doc, line_width)
554
+ end
555
+ else
556
+ doc = OpenAPISourceTools::Common.load_source(input_name)
557
+ return 2 if doc.nil?
558
+
559
+ order = order_name.nil? ? default_order(order_key, regexp_prefix) : OpenAPISourceTools::Common.load_source(order_name)
560
+ return 4 if order.nil?
561
+ md = order.fetch(metadata, {})
562
+ order_key = md.fetch(metadata_key, order_key)
563
+ regexp_prefix = md.fetch(metadata_regexp, regexp_prefix)
564
+ path_orders = []
565
+ return 4 unless extract_orders([], order, path_orders, order_key, regexp_prefix)
566
+ path_orders.sort!.reverse! # Longest key pattern sequence first.
567
+ order_document(path_orders, [], doc)
568
+ doc = formats[format].call(doc, line_width)
569
+ end
570
+
571
+ OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
572
+ end
573
+
574
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
@@ -240,17 +240,11 @@ module OpenAPISourceTools
240
240
  pk = @parts[k]
241
241
  ppk = pp[k]
242
242
  if pk.key?('fixed')
243
- if ppk.key?('fixed')
244
- c = pk['fixed'] <=> ppk['fixed']
245
- else
246
- return -1
247
- end
243
+ return -1 unless ppk.key?('fixed')
244
+ c = pk['fixed'] <=> ppk['fixed']
248
245
  else
249
- if ppk.key?('fixed')
250
- return 1
251
- else
252
- c = pk.fetch('parameter', '') <=> ppk.fetch('parameter', '')
253
- end
246
+ return 1 if ppk.key?('fixed')
247
+ c = pk.fetch('parameter', '') <=> ppk.fetch('parameter', '')
254
248
  end
255
249
  return c unless c.zero?
256
250
  end
@@ -10,7 +10,7 @@ module OpenAPISourceTools
10
10
  # Common methods used in programs and elsewhere gathered into one place.
11
11
  module Common
12
12
  def self.aargh(message, return_value = nil)
13
- message = message.map(&:to_s).join("\n") if message.is_a? Array
13
+ message = message.join("\n") if message.is_a? Array
14
14
  $stderr.puts message
15
15
  return_value
16
16
  end
@@ -8,11 +8,11 @@ require_relative 'gen'
8
8
  require 'find'
9
9
  require 'yaml'
10
10
 
11
+ # Gem namespace.
11
12
  module OpenAPISourceTools
12
13
  # Configuration file find and load convenience functions.
13
14
  # See the first 3 methods. The rest are intended to be internal helpers.
14
15
  module ConfigLoader
15
-
16
16
  # A function to find all files with a given prefix.
17
17
  # Prefix is taken from Gen.config if nil.
18
18
  # Returns an array of ConfigFileInfo objects.
@@ -43,9 +43,10 @@ module OpenAPISourceTools
43
43
 
44
44
  # Maps an array of ConfigFileInfo objects to an array of their contents.
45
45
  def self.contents_array(config_file_infos)
46
- config_file_infos.map(&:content).reject(&:nil?)
46
+ config_file_infos.map(&:content).compact
47
47
  end
48
48
 
49
+ # The result with data about the config file.
49
50
  class ConfigFileInfo
50
51
  include Comparable
51
52
 
@@ -87,6 +88,10 @@ module OpenAPISourceTools
87
88
  return d unless d.zero?
88
89
  @path <=> other.path
89
90
  end
91
+
92
+ def to_s
93
+ "Root: #{@root} [#{@keys.join(', ')}] #{@path}"
94
+ end
90
95
  end
91
96
 
92
97
  def self.prepare_prefix(name_prefix, root)
@@ -10,6 +10,7 @@ require_relative 'loaders'
10
10
  require_relative 'gen'
11
11
 
12
12
 
13
+ # Gem namespace.
13
14
  module OpenAPISourceTools
14
15
  def self.executable_bits_on(mode)
15
16
  mode = mode.to_s(8).chars
@@ -82,7 +82,7 @@ module OpenAPISourceTools
82
82
  d = name.downcase
83
83
  if d.start_with?(YAML_PREFIX)
84
84
  name = name.slice(YAML_PREFIX.size...name.size)
85
- elsif (YAML_EXTS.index { |s| d.end_with?(s) }).nil?
85
+ elsif YAML_EXTS.index { |s| d.end_with?(s) }.nil?
86
86
  return false
87
87
  end
88
88
  n, _sep, f = name.partition(':')
@@ -140,9 +140,9 @@ module OpenAPISourceTools
140
140
  method(:req_loader),
141
141
  method(:eval_loader),
142
142
  method(:ruby_loader),
143
+ method(:config_loader),
143
144
  method(:yaml_loader),
144
145
  method(:bin_loader),
145
- method(:config_loader),
146
146
  method(:separator_loader)
147
147
  ]
148
148
  end
@@ -153,7 +153,7 @@ module OpenAPISourceTools
153
153
  - #{Loaders::EVAL_PREFIX}code : runs code to add gem tasks again.
154
154
  - ruby_file#{Loaders::RUBY_EXT} : changes to Ruby file directory and requires the file.
155
155
  - #{Loaders::YAML_PREFIX}name:filename : Loads YAML file into Gen.d.name.
156
- - name:filename.{#{(Loaders::YAML_EXTS.map { |s| s[1...s.size] }).join('|')}} : Loads YAML file into Gen.d.name.
156
+ - name:filename.{#{Loaders::YAML_EXTS.map { |s| s[1...s.size] }.join('|')}} : Loads YAML file into Gen.d.name.
157
157
  - #{Loaders::BIN_PREFIX}name:filename : Loads binary file into Gen.d.name.
158
158
  - #{Loaders::CONFIG_PREFIX}name : Sets Gen.config for next gem/Ruby file configuration loading.
159
159
  - #{Loaders::SEPARATOR_PREFIX}string : Sets Gen.separator to string.
@@ -0,0 +1,271 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright © 2026 Ismo Kärkkäinen
4
+ # Licensed under Universal Permissive License. See LICENSE.txt.
5
+
6
+ # Top-level module.
7
+ module OpenAPISourceTools
8
+ # Ordering methods and classes for openapi-order.
9
+ module Ordering
10
+ # Used as replacement for '*'.
11
+ ANY = Regexp.new('^.*$')
12
+
13
+ def self.array_item_compare(a, b)
14
+ a[:values].size.times do |k|
15
+ av = a[:values][k]
16
+ bv = b[:values][k]
17
+ if av.nil?
18
+ return 1 unless bv.nil?
19
+ else
20
+ return -1 if bv.nil?
21
+ r = av <=> bv
22
+ return r unless r.nil? || r.zero?
23
+ end
24
+ end
25
+ 0
26
+ end
27
+
28
+ # Allowed keys starting from root, with pattern, any or multiple any key.
29
+ # Initialized with the path to the order array.
30
+ class KeyPath
31
+ include Comparable
32
+
33
+ attr_reader :items, :multiple, :first_multiple
34
+
35
+ # the parent_keys is processed to an array of regular expressions and :multi.
36
+ # A '*' is eventually replaced with regular expression that accepts anything.
37
+ # A '**' is replaced by :multi that matches at least one key during match.
38
+ # A string starting with regexp_prefix is changed to a regular expression.
39
+ # Other string is turned to anchored regular expression that matches only the string.
40
+ # The order of '*' and '**' can be modified to minimize occurrences of :multi.
41
+ def initialize(parent_keys, regexp_prefix)
42
+ @multiple = false
43
+ @items = []
44
+ parent_keys.each do |key|
45
+ if key == '*'
46
+ if @items.last == :multi
47
+ # :multi, :one is same as :one, :multi as at least 2 arbitrary items.
48
+ @items.pop
49
+ @items.push(:one, :multi)
50
+ else
51
+ @items.push(:one)
52
+ end
53
+ elsif key == '**'
54
+ @multiple = true
55
+ if @items.last == :multi
56
+ # :multi, :multi is same as :one, :multi as at least 2 arbitrary items.
57
+ @items.pop
58
+ @items.push(:one, :multi)
59
+ else
60
+ @items.push(:multi)
61
+ end
62
+ elsif key.start_with?(regexp_prefix)
63
+ @items.push(Regexp.new(key[regexp_prefix.size..]))
64
+ else
65
+ @items.push(Regexp.new("^#{Regexp.escape(key)}$"))
66
+ end
67
+ end
68
+ @items.each_with_index do |item, idx|
69
+ @items[idx] = ANY if item == :one
70
+ end
71
+ @first_multiple = @items.index(:multi) || @items.size
72
+ end
73
+
74
+ # Finds indexes of regular expressions that match key, skipping given number.
75
+ def matching_item_indexes(key, skip_non_multis)
76
+ indexes = []
77
+ ([skip_non_multis, @first_multiple].min...@items.size).each do |idx|
78
+ item = @items[idx]
79
+ indexes.push(idx) if item == :multi || item.match?(key)
80
+ end
81
+ indexes
82
+ end
83
+
84
+ # Given possible matches, uses position_index to check for matches relevant
85
+ # to the current situation and checks recursively if a solution ending to
86
+ # the desired point can be found.
87
+ def search(index_arrays, array_index, position_index)
88
+ if array_index == index_arrays.size
89
+ # Last key is known to match last item, so if that were tested, it would pass.
90
+ return position_index == @items.size - 1
91
+ end
92
+ if @items[position_index] == :multi
93
+ # Multi absorbs current key and releases to next position.
94
+ return true if search(index_arrays, array_index + 1, position_index + 1)
95
+ # Multi absorbs current key and stays for more.
96
+ search(index_arrays, array_index + 1, position_index)
97
+ else
98
+ # Only current item to match with.
99
+ return false if index_arrays[array_index].index(position_index).nil?
100
+ search(index_arrays, array_index + 1, position_index + 1)
101
+ end
102
+ end
103
+
104
+ # Performs base checks whether match is possible and if so, obtains indexes
105
+ # od matching patterns and starts search using those.
106
+ def match?(key_path)
107
+ return true if @items.empty? && key_path.empty?
108
+ slack = key_path.size - @items.size
109
+ return false if slack.negative? # Path too short.
110
+ return false if slack.positive? && !@multiple # Path too long.
111
+ # Last key must match last item.
112
+ return false unless @items.last == :multi || @items.last.match?(key_path.last)
113
+ # First key must match first item.
114
+ return false unless @items.first == :multi || @items.first.match?(key_path.first)
115
+ return true if key_path.size <= 2 # Tested both already.
116
+ # Form arrays of possible match indexes for search. Omit first and last.
117
+ index_arrays = []
118
+ (1...(key_path.size - 1)).each do |idx|
119
+ key = key_path[idx]
120
+ idxs = matching_item_indexes(key, idx)
121
+ return false if idxs.empty?
122
+ index_arrays.push(idxs)
123
+ end
124
+ search(index_arrays, 0, @items.first == :multi ? 0 : 1)
125
+ end
126
+
127
+ def <=>(other)
128
+ # The purpose is to place longest "exact" items to the end.
129
+ # Hence items.size, and the more multiple, the earlier.
130
+ d = @items.size <=> other.items.size
131
+ return d unless d.zero?
132
+ -@items.count(:multi) <=> -other.items.count(:multi)
133
+ end
134
+ end
135
+
136
+ # Book-keeping class for pattern and its location.
137
+ class KeyPattern
138
+ attr_reader :pattern, :index, :priority
139
+
140
+ def initialize(pattern, index, priority)
141
+ @pattern = pattern
142
+ @index = index
143
+ @priority = priority
144
+ end
145
+
146
+ def match?(string)
147
+ @pattern.match?(string)
148
+ end
149
+
150
+ def any?
151
+ @index != @priority
152
+ end
153
+ end
154
+
155
+ # Handles ordering keys or an array using the given key order.
156
+ class Order
157
+ def self.orderable?(item)
158
+ return true if item.is_a?(Hash)
159
+ return true if item.is_a?(Array)
160
+ false
161
+ end
162
+
163
+ attr_reader :patterns
164
+
165
+ def initialize(keys, regexp_prefix)
166
+ @patterns = []
167
+ has_asterisk = false
168
+ keys.each_with_index do |string, idx|
169
+ if string.start_with?(regexp_prefix)
170
+ @patterns.push(KeyPattern.new(Regexp.new(string[regexp_prefix.size..]), idx, idx))
171
+ elsif string == '*'
172
+ @patterns.push(KeyPattern.new(Regexp.new('^.*$'), idx, keys.size)) unless has_asterisk # First used.
173
+ has_asterisk = true
174
+ else
175
+ @patterns.push(KeyPattern.new(Regexp.new("^#{Regexp.escape(string)}$"), idx, idx))
176
+ end
177
+ end
178
+ @patterns.push(KeyPattern.new(Regexp.new('^.*$'), @patterns.size, keys.size + 1)) unless has_asterisk
179
+ end
180
+
181
+ def apply_array(array)
182
+ return true if array.size < 2
183
+ values = []
184
+ array.each do |item|
185
+ amended = {
186
+ item: item,
187
+ values: []
188
+ }
189
+ unless item.is_a?(Hash)
190
+ amended[:values] = Array.new(@patterns.size, nil)
191
+ values.push(amended)
192
+ next
193
+ end
194
+ @patterns.each do |kp|
195
+ if kp.any?
196
+ amended[:values].push(nil)
197
+ next
198
+ end
199
+ # Find first matching key.
200
+ v = nil
201
+ item.each_key do |item_key|
202
+ next unless kp.match?(item_key)
203
+ v = item[item_key]
204
+ break
205
+ end
206
+ amended[:values].push((v.is_a?(Array) || v.is_a?(Hash)) ? nil : v)
207
+ end
208
+ values.push(amended)
209
+ end
210
+ values.sort! { |a, b| OpenAPISourceTools::Ordering.array_item_compare(a, b) }
211
+ values.each_with_index { |amended, i| array[i] = amended[:item] }
212
+ true
213
+ end
214
+
215
+ def apply_hash(hash)
216
+ return true if hash.size < 2
217
+ groups = @patterns.map do |kp|
218
+ {
219
+ kp: kp,
220
+ keys: []
221
+ }
222
+ end
223
+ s2orig = {}
224
+ hash.each_key do |key|
225
+ skey = key.is_a?(String) ? key : key.to_s
226
+ s2orig[skey] = key
227
+ best_priority = groups.size + 1
228
+ best_idx = nil
229
+ groups.each do |group|
230
+ kp = group[:kp]
231
+ next if best_priority <= kp.priority
232
+ next unless kp.match?(skey)
233
+ best_priority = kp.priority
234
+ best_idx = kp.index
235
+ end
236
+ groups[best_idx][:keys].push(skey) unless best_idx.nil?
237
+ end
238
+ ordered = {}
239
+ groups.each do |group|
240
+ group[:keys].sort!
241
+ group[:keys].each { |k| ordered[s2orig[k]] = hash[s2orig[k]] }
242
+ end
243
+ hash.clear
244
+ hash.merge!(ordered)
245
+ true
246
+ end
247
+
248
+ def apply(item)
249
+ return apply_array(item) if item.is_a?(Array)
250
+ return apply_hash(item) if item.is_a?(Hash)
251
+ false
252
+ end
253
+ end
254
+
255
+ # Holds KeyPath and the order that was specified.
256
+ class KeyPathOrder
257
+ include Comparable
258
+
259
+ attr_reader :path, :order
260
+
261
+ def initialize(key_path, order)
262
+ @path = key_path
263
+ @order = order
264
+ end
265
+
266
+ def <=>(other)
267
+ @path <=> other.path
268
+ end
269
+ end
270
+ end
271
+ end
@@ -7,8 +7,8 @@ require_relative 'common'
7
7
  require 'erb'
8
8
 
9
9
 
10
+ # Gem namespace.
10
11
  module OpenAPISourceTools
11
-
12
12
  # Required interface for tasks, with default implementation for some methods.
13
13
  module TaskInterface
14
14
  def generate(context_binding)
@@ -3,9 +3,10 @@
3
3
  # Copyright © 2025-2026 Ismo Kärkkäinen
4
4
  # Licensed under Universal Permissive License. See LICENSE.txt.
5
5
 
6
+ # Gem namespace.
6
7
  module OpenAPISourceTools
7
8
  NAME = 'openapi-sourcetools'
8
- VERSION = '0.11.0'
9
+ VERSION = '0.12.1'
9
10
 
10
11
  def self.info(separator = ': ')
11
12
  "#{NAME}#{separator}#{VERSION}"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi-sourcetools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismo Kärkkäinen
@@ -37,7 +37,9 @@ description: |-
37
37
  email: ismokarkkainen@icloud.com
38
38
  executables:
39
39
  - openapi-addheaders
40
+ - openapi-addmediatypes
40
41
  - openapi-addparameters
42
+ - openapi-addrequestbodies
41
43
  - openapi-addresponses
42
44
  - openapi-addschemas
43
45
  - openapi-addsecurityschemes
@@ -45,8 +47,10 @@ executables:
45
47
  - openapi-clearrefs
46
48
  - openapi-frequencies
47
49
  - openapi-generate
50
+ - openapi-generate-checkconfig
48
51
  - openapi-merge
49
52
  - openapi-modifypaths
53
+ - openapi-order
50
54
  - openapi-patterntests
51
55
  - openapi-processpaths
52
56
  extensions: []
@@ -54,7 +58,9 @@ extra_rdoc_files: []
54
58
  files:
55
59
  - LICENSE.txt
56
60
  - bin/openapi-addheaders
61
+ - bin/openapi-addmediatypes
57
62
  - bin/openapi-addparameters
63
+ - bin/openapi-addrequestbodies
58
64
  - bin/openapi-addresponses
59
65
  - bin/openapi-addschemas
60
66
  - bin/openapi-addsecurityschemes
@@ -62,8 +68,10 @@ files:
62
68
  - bin/openapi-clearrefs
63
69
  - bin/openapi-frequencies
64
70
  - bin/openapi-generate
71
+ - bin/openapi-generate-checkconfig
65
72
  - bin/openapi-merge
66
73
  - bin/openapi-modifypaths
74
+ - bin/openapi-order
67
75
  - bin/openapi-patterntests
68
76
  - bin/openapi-processpaths
69
77
  - lib/openapi/sourcetools.rb
@@ -75,6 +83,7 @@ files:
75
83
  - lib/openapi/sourcetools/generate.rb
76
84
  - lib/openapi/sourcetools/helper.rb
77
85
  - lib/openapi/sourcetools/loaders.rb
86
+ - lib/openapi/sourcetools/order.rb
78
87
  - lib/openapi/sourcetools/output.rb
79
88
  - lib/openapi/sourcetools/task.rb
80
89
  - lib/openapi/sourcetools/version.rb
@@ -90,14 +99,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
99
  requirements:
91
100
  - - ">="
92
101
  - !ruby/object:Gem::Version
93
- version: 3.2.5
102
+ version: 3.3.11
94
103
  required_rubygems_version: !ruby/object:Gem::Requirement
95
104
  requirements:
96
105
  - - ">="
97
106
  - !ruby/object:Gem::Version
98
107
  version: '0'
99
108
  requirements: []
100
- rubygems_version: 3.6.9
109
+ rubygems_version: 4.0.21
101
110
  specification_version: 4
102
111
  summary: Tools for creating source code from API specification.
103
112
  test_files: []