Class: Aspera::Schema::Documentation
- Inherits:
-
Object
- Object
- Aspera::Schema::Documentation
- Defined in:
- lib/aspera/schema/documentation.rb
Overview
Generate documentation from Schema, for Transfer Spec, or async Conf spec
Constant Summary collapse
- JSON_TYPE_TO_DOC =
Map JSON Schema type names to user-friendly display names
{ 'string' => 'String', 'integer' => 'Integer', 'number' => 'Number', 'boolean' => 'Bool', 'array' => 'Array', 'object' => 'Hash' }.freeze
Instance Attribute Summary collapse
- #columns ⇒ Array<String> readonly
Instance Method Summary collapse
-
#build(schema = nil) ⇒ Documentation
Generate a documentation table from a JSON schema for transfer specifications.
-
#initialize(formatter, schema, include_option: false, agent_columns: false, code_highlight: false) ⇒ Documentation
constructor
A new instance of Documentation.
- #rows ⇒ Object
-
#table ⇒ Array<Array<String>>
First row is the titles (for Markdown table generation).
Constructor Details
#initialize(formatter, schema, include_option: false, agent_columns: false, code_highlight: false) ⇒ Documentation
Returns a new instance of Documentation.
24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/aspera/schema/documentation.rb', line 24 def initialize(formatter, schema, include_option: false, agent_columns: false, code_highlight: false) @formatter = formatter @schema = schema @include_option = include_option @agent_columns = agent_columns @code_highlight = code_highlight @columns = %w[name type description] @columns.insert(-2, *Agent::Factory::ALL.values.map { |i| i[:short].to_s }.sort) if @agent_columns # Sections: each entry is {header: row_or_nil, rows: []} # A flat schema produces a single section with no header. @sections = [{header: nil, rows: []}] end |
Instance Attribute Details
#columns ⇒ Array<String> (readonly)
45 46 47 |
# File 'lib/aspera/schema/documentation.rb', line 45 def columns @columns end |
Instance Method Details
#build(schema = nil) ⇒ Documentation
Generate a documentation table from a JSON schema for transfer specifications
Recursively processes a JSON schema to create a formatted table for manual documentation. Handles nested objects, arrays, and extracts metadata (descriptions, types, enums, deprecations).
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/aspera/schema/documentation.rb', line 60 def build(schema = nil) code = @code_highlight ? ->(c) { "`#{c}`" } : ->(c) { c } schema ||= @schema render_title = ->(title) { title.gsub(Markdown::FORMATS) { @formatter.markdown_text(Regexp.last_match) } } on_variant = ->(variant_reader, discriminant_property, discriminant_value) do title = variant_reader.current['title'] || variant_reader.current['description'] header = if discriminant_property && discriminant_value desc = render_title.call("`#{discriminant_value}`") desc += ": #{render_title.call(title)}" if title @formatter.check_row({ 'name' => render_title.call("**#{discriminant_property}**"), 'type' => code.call(JSON_TYPE_TO_DOC['string']), 'description' => desc }) elsif title @formatter.check_row({'name' => "**#{render_title.call(title)}**", 'type' => ' ', 'description' => ' '}) end @sections.push({header: header, rows: []}) end schema.each_property(on_variant: on_variant) do |property_schema, _name, property_full_name| node = property_schema.current # Manual table item_type = if node['type'].is_a?(Array) node['type'].map { |t| JSON_TYPE_TO_DOC.fetch(t, t) }.join(', ') elsif node['type'].eql?('array') && node.dig('items', 'type').is_a?(String) "#{JSON_TYPE_TO_DOC.fetch(node['type'], node['type'])}[#{JSON_TYPE_TO_DOC.fetch(node.dig('items', 'type'), node.dig('items', 'type'))}]" else JSON_TYPE_TO_DOC.fetch(node['type'], node['type']) end item = { 'name' => code.call(property_full_name), 'type' => code.call(item_type), 'description' => [] } # Render Markdown formatting and split lines item['description'] = node['description'] .gsub(Markdown::FORMATS) { @formatter.markdown_text(Regexp.last_match) } .split("\n") if node.key?('description') item['description'].unshift("DEPRECATED: #{node['x-deprecation']}") if node.key?('x-deprecation') # Add flags for supported agents in doc agents = [] Agent::Factory::ALL.each_key do |sym| agents.push(sym) if node['x-agents'].nil? || node['x-agents'].include?(sym.to_s) end Aspera.assert(agents.include?(:direct)) { "#{property_full_name}: x-cli-option requires agent direct (or nil)" } if node['x-cli-option'] if @agent_columns Agent::Factory::ALL.each do |sym, names| item[names[:short].to_s] = @formatter.tick(agents.include?(sym)) end else item['description'].push("(#{agents.map { |i| Agent::Factory::ALL[i][:short].to_s.upcase }.sort.join(', ')})") unless agents.length.eql?(Agent::Factory::ALL.length) end # Only keep lines that are usable in supported agents next if agents.empty? item['description'].push("Allowed values: #{node['enum'].map { |v| value_text(v) }.join(', ')}.") if node.key?('enum') item['description'].push("Default: #{value_text(node['default'])}.") if node.key?('default') item['description'].push("Example: #{value_text(node['example'])}.") if node.key?('example') if @include_option envvar_prefix = '' cli_option = if node.key?('x-cli-envvar') envvar_prefix = 'env:' node['x-cli-envvar'] elsif node['x-cli-switch'] false_part = node.key?('x-cli-false') ? " / #{node['x-cli-false']}" : '' "#{node['x-cli-option']}#{false_part}" elsif node['x-cli-option'] arg_type = node.key?('enum') ? '{enum}' : "{#{[node['type']].flatten.join('|')}}" conversion_tag = node.key?('x-cli-convert') ? 'conversion' : nil sep = node['x-cli-option'].start_with?('--') ? '=' : ' ' "#{node['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}" end short = node.key?('x-cli-short') ? "(#{node['x-cli-short']})" : nil item['description'].push("(#{'special:' if node['x-cli-special']}#{envvar_prefix}#{@formatter.markdown_text("`#{cli_option}`")})#{short}") if cli_option end @sections.last[:rows].push(@formatter.check_row(item)) end self end |
#rows ⇒ Object
37 38 39 40 41 42 |
# File 'lib/aspera/schema/documentation.rb', line 37 def rows @sections.flat_map do |section| sorted = section[:rows].sort_by { |i| i['name'] } section[:header] ? [section[:header]] + sorted : sorted end end |
#table ⇒ Array<Array<String>>
First row is the titles (for Markdown table generation)
49 50 51 |
# File 'lib/aspera/schema/documentation.rb', line 49 def table [@columns] + rows.map { |row| @columns.map { |field| row[field] } } end |