Module: Spree::Api::OpenAPI::PathSorter
- Defined in:
- lib/spree/api/openapi/path_sorter.rb
Overview
Reorders the ‘paths:` block of a generated OpenAPI YAML so paths are grouped by tag, in the same order as the top-level `tags:` array.
rswag emits paths in the order specs are loaded (alphabetical by filename), which doesn’t match the curated ‘tags:` array order in swagger_helper.rb. Mintlify groups sidebar sections by the *first appearance* of each tag in `paths:` — so after this sort runs, the sidebar matches the `tags:` array.
The reorder is line-based to preserve the exact text formatting that rswag/Psych produced (indentation, multi-line string wrapping, etc.).
Class Method Summary collapse
- .build_block(lines, from, to_exclusive) ⇒ Object
-
.collect_path_blocks(lines, start_idx, end_idx_exclusive) ⇒ Object
Collect each path block as { tag:, text: } from lines.
-
.extract_primary_tag(slice) ⇒ Object
Within a path block, find the first ‘tags:` line and return the first tag value following it (the path block’s “primary” tag).
-
.extract_tag_rank(lines) ⇒ Object
Parse the top-level ‘tags:` block and return { tag_name => index } in declared order.
- .path_header?(line) ⇒ Boolean
-
.sort_file!(path) ⇒ Object
Sort the ‘paths:` block in `path` so each path’s first-operation primary tag follows the order defined in the document’s top-level ‘tags:` array.
-
.sort_text(yaml) ⇒ Object
Pure-string variant: sort a YAML document’s ‘paths:` block by tag order.
Class Method Details
.build_block(lines, from, to_exclusive) ⇒ Object
99 100 101 102 103 104 105 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 99 def build_block(lines, from, to_exclusive) slice = lines[from...to_exclusive] { tag: extract_primary_tag(slice), text: slice.join } end |
.collect_path_blocks(lines, start_idx, end_idx_exclusive) ⇒ Object
Collect each path block as { tag:, text: } from lines. A path block starts on a line beginning with two spaces + a quote.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 80 def collect_path_blocks(lines, start_idx, end_idx_exclusive) blocks = [] current_start = nil (start_idx...end_idx_exclusive).each do |i| if path_header?(lines[i]) blocks << build_block(lines, current_start, i) if current_start current_start = i end end blocks << build_block(lines, current_start, end_idx_exclusive) if current_start blocks end |
.extract_primary_tag(slice) ⇒ Object
Within a path block, find the first ‘tags:` line and return the first tag value following it (the path block’s “primary” tag).
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 109 def extract_primary_tag(slice) slice.each_with_index do |line, idx| next unless line.match?(/\A {6}tags:\s*$/) slice[(idx + 1)..].each do |next_line| if next_line.start_with?(' - ') value = next_line[8..].strip return value unless value.empty? end break if next_line.match?(/\A {0,4}\S/) end end nil end |
.extract_tag_rank(lines) ⇒ Object
Parse the top-level ‘tags:` block and return { tag_name => index } in declared order. Returns {} if not found.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 60 def extract_tag_rank(lines) start = lines.index { |l| l.start_with?('tags:') } return {} unless start rank = {} i = start + 1 while i < lines.size line = lines[i] if line.start_with?('- name: ') rank[line.sub('- name: ', '').strip] = rank.size elsif line.match?(/\A[A-Za-z]/) || line.start_with?('x-') break end i += 1 end rank end |
.path_header?(line) ⇒ Boolean
95 96 97 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 95 def path_header?(line) line.start_with?(' "/') || line.start_with?(' /') end |
.sort_file!(path) ⇒ Object
Sort the ‘paths:` block in `path` so each path’s first-operation primary tag follows the order defined in the document’s top-level ‘tags:` array. Returns true if the file was rewritten, false if already sorted.
22 23 24 25 26 27 28 29 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 22 def sort_file!(path) original = File.read(path) sorted = sort_text(original) return false if sorted == original File.write(path, sorted) true end |
.sort_text(yaml) ⇒ Object
Pure-string variant: sort a YAML document’s ‘paths:` block by tag order.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/spree/api/openapi/path_sorter.rb', line 32 def sort_text(yaml) lines = yaml.lines tag_rank = extract_tag_rank(lines) return yaml if tag_rank.empty? paths_start = lines.index { |l| l.start_with?('paths:') } return yaml unless paths_start paths_end_exclusive = (paths_start + 1...lines.size).find do |i| line = lines[i] line.match?(/\A[A-Za-z]/) || line.start_with?('x-') end || lines.size blocks = collect_path_blocks(lines, paths_start + 1, paths_end_exclusive) return yaml if blocks.empty? unknown_rank = tag_rank.size sorted_blocks = blocks.each_with_index.sort_by do |block, original_index| [tag_rank.fetch(block[:tag], unknown_rank), original_index] end.map(&:first) prefix = lines[0..paths_start].join suffix = lines[paths_end_exclusive..].to_a.join prefix + sorted_blocks.map { |b| b[:text] }.join + suffix end |