transducer 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ec4069db4028d341c58598e45bdefdd0749e92092e4362b491845f350dc7e3df
4
+ data.tar.gz: 6cc2eeca6343cdcf575f76f1405bbe10066be7d6b1934193ed3b8b10215b1ff8
5
+ SHA512:
6
+ metadata.gz: c2ecf6366f6121751bf0bf2a160264b087b6bb592f6f92a802f7b46acf0040fbd52f9068de01f97197ed566f1dc954c38d8fe718db51bd71f46d5d6300e5a17a
7
+ data.tar.gz: 4d1480b86c0134a6155c29a625ffccc49fcb833c64ab96cddf42f13d41bdce553a082c5cf454744351c1b7cec577e5ed86c9ea9e42611c1950d3bc0ee9ac7a75
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.0 - 2025-12-11
6
+
7
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # Transducer
2
+
3
+ Generate Markdown documentation from OpenAPI specifications.
4
+
5
+ Transducer is a Ruby gem that reads OpenAPI YAML specifications (versions 3.0.x and 3.1.x) and generates well-formatted, readable Markdown documentation.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'transducer'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle install
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install transducer
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Command Line
30
+
31
+ Generate Markdown documentation from an OpenAPI specification:
32
+
33
+ ```bash
34
+ $ transducer generate input.yaml -o output.md
35
+ ```
36
+
37
+ Options:
38
+ - `-o, --output`: Output file path (default: `docs/openapi.md`)
39
+ - `-t, --template`: Custom ERB template file path
40
+
41
+ #### Using Custom Templates
42
+
43
+ You can customize the output format using ERB templates:
44
+
45
+ ```bash
46
+ $ transducer generate input.yaml --template=custom.md.erb --output=api.md
47
+ ```
48
+
49
+ Template variables available:
50
+ - `data`: Parsed OpenAPI specification hash
51
+ - `formatter`: Formatter instance for formatting endpoints, parameters, schemas, etc.
52
+
53
+ Example custom template:
54
+
55
+ ```erb
56
+ # <%= data['info']['title'] %>
57
+
58
+ <% data['paths']&.each do |path, methods| %>
59
+ <% methods.each do |method, details| %>
60
+ ### <%= method.upcase %> <%= path %>
61
+ <%= formatter.format_endpoint(path, method, details) %>
62
+ <% end %>
63
+ <% end %>
64
+ ```
65
+
66
+ Display version information:
67
+
68
+ ```bash
69
+ $ transducer version
70
+ ```
71
+
72
+ Display help:
73
+
74
+ ```bash
75
+ $ transducer help
76
+ $ transducer help generate
77
+ ```
78
+
79
+ ### Programmatic Usage
80
+
81
+ You can also use Transducer programmatically in your Ruby code:
82
+
83
+ ```ruby
84
+ require 'transducer'
85
+
86
+ # Parse OpenAPI specification
87
+ parser = Transducer::Parser.new('path/to/openapi.yaml')
88
+ data = parser.parse
89
+
90
+ # Generate Markdown with default template
91
+ generator = Transducer::Generator.new(data)
92
+ markdown = generator.generate
93
+
94
+ # Or use a custom template
95
+ generator = Transducer::Generator.new(data, template_path: 'custom.md.erb')
96
+ markdown = generator.generate
97
+
98
+ # Write to file
99
+ generator.to_file('output.md')
100
+ ```
101
+
102
+ ## Output Format
103
+
104
+ The generated Markdown documentation includes:
105
+
106
+ - API Title and Description: From the `info` section
107
+ - Version and Base URL: API version and server URL
108
+ - Table of Contents: Automatically generated with links to all sections
109
+ - Endpoints: All API endpoints with:
110
+ - HTTP method and path
111
+ - Description
112
+ - Parameters (path, query, header)
113
+ - Request body schema and examples
114
+ - Response codes with schemas and examples
115
+ - Schemas: Component schemas with:
116
+ - Type information
117
+ - Property descriptions
118
+ - Required fields
119
+ - Examples
120
+
121
+ ### Example Output
122
+
123
+ ```markdown
124
+ # Simple API
125
+
126
+ A simple API for testing
127
+
128
+ | | |
129
+ |---|---|
130
+ | Version | 1.0.0 |
131
+ | Base URL | https://api.example.com/v1 |
132
+
133
+ ## Table of Contents
134
+
135
+ - [Endpoints](#endpoints)
136
+ - [GET /users](#get-users)
137
+ - [POST /users](#post-users)
138
+ - [Schemas](#schemas)
139
+ - [User](#user)
140
+
141
+ ## Endpoints
142
+
143
+ ### GET /users
144
+
145
+ Returns a list of all users
146
+
147
+ **Parameters**:
148
+
149
+ | Name | Location | Type | Required | Description |
150
+ |------|----------|------|----------|-------------|
151
+ | limit | query | integer | No | Maximum number of users to return |
152
+
153
+ **Responses**:
154
+
155
+ #### 200 OK
156
+
157
+ Successful response
158
+ ...
159
+ ```
160
+
161
+ ## Supported OpenAPI Versions
162
+
163
+ - OpenAPI 3.0.x
164
+ - OpenAPI 3.1.x
165
+
166
+ OpenAPI 2.0 (Swagger) is not currently supported.
167
+
168
+ ## Development
169
+
170
+ After checking out the repo, run `bundle install` to install dependencies.
171
+
172
+ Run tests:
173
+
174
+ ```bash
175
+ $ bundle exec rspec
176
+ ```
177
+
178
+ Run RuboCop:
179
+
180
+ ```bash
181
+ $ bundle exec rubocop
182
+ ```
183
+
184
+ Run all checks (tests + RuboCop):
185
+
186
+ ```bash
187
+ $ bundle exec rake
188
+ ```
189
+
190
+ To install this gem onto your local machine:
191
+
192
+ ```bash
193
+ $ bundle exec rake install
194
+ ```
195
+
196
+ To release a new version:
197
+
198
+ 1. Update the version number in `lib/transducer/version.rb`
199
+ 2. Update `CHANGELOG.md`
200
+ 3. Run `bundle exec rake release`
201
+
202
+ ## Contributing
203
+
204
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ydah/transducer.
205
+
206
+ 1. Fork it
207
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
208
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
209
+ 4. Push to the branch (`git push origin my-new-feature`)
210
+ 5. Create new Pull Request
211
+
212
+ ## License
213
+
214
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/exe/transducer ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/transducer'
5
+
6
+ Transducer::CLI.start(ARGV)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Transducer
6
+ class CLI < Thor
7
+ def self.exit_on_failure?
8
+ true
9
+ end
10
+
11
+ desc 'generate INPUT', 'Generate Markdown documentation from OpenAPI specification'
12
+ method_option :output, aliases: '-o', type: :string, default: 'docs/openapi.md',
13
+ desc: 'Output file path for the generated Markdown'
14
+ method_option :template, aliases: '-t', type: :string,
15
+ desc: 'Custom ERB template file path'
16
+ def generate(input_file)
17
+ unless File.exist?(input_file)
18
+ error "Input file not found: #{input_file}"
19
+ exit 1
20
+ end
21
+
22
+ output_file = options[:output]
23
+ template_path = options[:template]
24
+
25
+ parser = Parser.new(input_file)
26
+ data = parser.parse
27
+
28
+ generator = Generator.new(data, template_path: template_path)
29
+ generator.to_file(output_file)
30
+
31
+ success "Documentation generated successfully: #{output_file}"
32
+ rescue FileError => e
33
+ error "File error: #{e.message}"
34
+ exit 1
35
+ rescue ParseError => e
36
+ error "Parse error: #{e.message}"
37
+ exit 2
38
+ rescue ValidationError => e
39
+ error "Validation error: #{e.message}"
40
+ exit 3
41
+ rescue StandardError => e
42
+ error "Unexpected error: #{e.message}"
43
+ exit 4
44
+ end
45
+
46
+ desc 'version', 'Display version information'
47
+ def version
48
+ puts "Transducer version #{Transducer::VERSION}"
49
+ end
50
+
51
+ private
52
+
53
+ def success(message)
54
+ puts "✓ #{message}"
55
+ end
56
+
57
+ def error(message)
58
+ warn "✗ #{message}"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Transducer
4
+ class Formatter
5
+ def format_endpoint(path, method, details)
6
+ output = []
7
+ output << "### #{method.upcase} #{path}"
8
+ output << ''
9
+ output << details['description'] if details['description']
10
+ output << ''
11
+ output << format_parameters(details['parameters']) if details['parameters']
12
+ output << format_request_body(details['requestBody']) if details['requestBody']
13
+ output << format_responses(details['responses']) if details['responses']
14
+ output.join("\n")
15
+ end
16
+
17
+ def format_parameters(parameters)
18
+ return '' if parameters.nil? || parameters.empty?
19
+
20
+ output = []
21
+ output << '**Parameters**:'
22
+ output << ''
23
+ output << '| Name | Location | Type | Required | Description |'
24
+ output << '|------|----------|------|----------|-------------|'
25
+
26
+ parameters.each do |param|
27
+ name = param['name'] || 'N/A'
28
+ location = param['in'] || 'N/A'
29
+ type = extract_type(param['schema'])
30
+ required = param['required'] ? 'Yes' : 'No'
31
+ description = param['description'] || ''
32
+ output << "| #{name} | #{location} | #{type} | #{required} | #{description} |"
33
+ end
34
+
35
+ output << ''
36
+ output.join("\n")
37
+ end
38
+
39
+ def format_request_body(request_body)
40
+ return '' unless request_body
41
+
42
+ output = []
43
+ output << '**Request Body**:'
44
+ output << ''
45
+
46
+ if request_body['description']
47
+ output << request_body['description']
48
+ output << ''
49
+ end
50
+
51
+ request_body['content']&.each do |content_type, details|
52
+ output << "**Content-Type**: `#{content_type}`"
53
+ output << ''
54
+ output << format_schema(details['schema']) if details['schema']
55
+ next unless details['example']
56
+
57
+ output << '**Example**:'
58
+ output << ''
59
+ output << '```json'
60
+ output << JSON.pretty_generate(details['example'])
61
+ output << '```'
62
+ output << ''
63
+ end
64
+
65
+ output.join("\n")
66
+ end
67
+
68
+ def format_responses(responses)
69
+ return '' unless responses
70
+
71
+ output = []
72
+ output << '**Responses**:'
73
+ output << ''
74
+
75
+ responses.each do |status_code, response|
76
+ output << "#### #{status_code} #{status_name(status_code)}"
77
+ output << ''
78
+ output << response['description'] if response['description']
79
+ output << ''
80
+
81
+ next unless response['content']
82
+
83
+ response['content'].each do |content_type, details|
84
+ output << "**Content-Type**: `#{content_type}`"
85
+ output << ''
86
+ if details['schema']
87
+ output << '**Schema**:'
88
+ output << ''
89
+ output << '```json'
90
+ output << JSON.pretty_generate(details['schema'])
91
+ output << '```'
92
+ output << ''
93
+ end
94
+ next unless details['example']
95
+
96
+ output << '**Example**:'
97
+ output << ''
98
+ output << '```json'
99
+ output << JSON.pretty_generate(details['example'])
100
+ output << '```'
101
+ output << ''
102
+ end
103
+ end
104
+
105
+ output.join("\n")
106
+ end
107
+
108
+ def format_schema(schema)
109
+ return '' unless schema
110
+
111
+ output = []
112
+
113
+ if schema['$ref']
114
+ ref_name = schema['$ref'].split('/').last
115
+ output << "**Schema**: `#{ref_name}`"
116
+ output << ''
117
+ return output.join("\n")
118
+ end
119
+
120
+ output << "**Type**: #{schema['type']}" if schema['type']
121
+ output << ''
122
+
123
+ if schema['properties']
124
+ output << '**Properties**:'
125
+ output << ''
126
+ output << '| Name | Type | Required | Description |'
127
+ output << '|------|------|----------|-------------|'
128
+
129
+ required_fields = schema['required'] || []
130
+ schema['properties'].each do |prop_name, prop_details|
131
+ type = extract_type(prop_details)
132
+ is_required = required_fields.include?(prop_name) ? 'Yes' : 'No'
133
+ description = prop_details['description'] || ''
134
+ output << "| #{prop_name} | #{type} | #{is_required} | #{description} |"
135
+ end
136
+ output << ''
137
+ end
138
+
139
+ output.join("\n")
140
+ end
141
+
142
+ private
143
+
144
+ def extract_type(schema)
145
+ return 'N/A' unless schema
146
+
147
+ if schema['$ref']
148
+ schema['$ref'].split('/').last
149
+ elsif schema['type']
150
+ type = schema['type']
151
+ if type == 'array' && schema['items']
152
+ item_type = extract_type(schema['items'])
153
+ "array[#{item_type}]"
154
+ else
155
+ type
156
+ end
157
+ else
158
+ 'object'
159
+ end
160
+ end
161
+
162
+ def status_name(code)
163
+ names = {
164
+ '200' => 'OK',
165
+ '201' => 'Created',
166
+ '204' => 'No Content',
167
+ '400' => 'Bad Request',
168
+ '401' => 'Unauthorized',
169
+ '403' => 'Forbidden',
170
+ '404' => 'Not Found',
171
+ '500' => 'Internal Server Error'
172
+ }
173
+ names[code.to_s] || ''
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'erb'
5
+
6
+ module Transducer
7
+ class Generator
8
+ attr_reader :data, :formatter, :template_path
9
+
10
+ def initialize(parsed_data, template_path: nil)
11
+ @data = parsed_data
12
+ @formatter = Formatter.new
13
+ @template_path = template_path
14
+ end
15
+
16
+ def generate
17
+ if @template_path
18
+ render_template
19
+ else
20
+ generate_default
21
+ end
22
+ end
23
+
24
+ def to_file(output_path)
25
+ require 'fileutils'
26
+ FileUtils.mkdir_p(File.dirname(output_path))
27
+
28
+ File.write(output_path, generate)
29
+ rescue Errno::EACCES
30
+ raise FileError, "Permission denied: #{output_path}"
31
+ rescue StandardError => e
32
+ raise FileError, "Failed to write file: #{e.message}"
33
+ end
34
+
35
+ private
36
+
37
+ def render_template
38
+ raise FileError, "Template file not found: #{@template_path}" unless File.exist?(@template_path)
39
+
40
+ template_content = File.read(@template_path)
41
+ erb = ERB.new(template_content, trim_mode: '-')
42
+ erb.result(binding)
43
+ rescue Errno::EACCES
44
+ raise FileError, "Permission denied reading template: #{@template_path}"
45
+ rescue StandardError => e
46
+ raise FileError, "Failed to render template: #{e.message}"
47
+ end
48
+
49
+ def generate_default
50
+ output = []
51
+ output << generate_header
52
+ output << generate_table_of_contents
53
+ output << generate_endpoints
54
+ output << generate_schemas
55
+ output.compact.join("\n\n")
56
+ end
57
+
58
+ def generate_header
59
+ info = @data['info']
60
+ output = []
61
+ output << "# #{info['title']}"
62
+ output << ''
63
+ output << info['description'] if info['description']
64
+ output << ''
65
+ output << '| | |'
66
+ output << '|---|---|'
67
+ output << "| Version | #{info['version']} |"
68
+
69
+ output << "| Base URL | #{@data['servers'][0]['url']} |" if @data['servers'] && !@data['servers'].empty?
70
+
71
+ output.join("\n")
72
+ end
73
+
74
+ def generate_table_of_contents
75
+ output = []
76
+ output << '## Table of Contents'
77
+ output << ''
78
+ output << '- [Endpoints](#endpoints)'
79
+
80
+ @data['paths']&.each do |path, methods|
81
+ methods.each_key do |method|
82
+ next if method.start_with?('$')
83
+
84
+ anchor = "#{method}-#{path}".downcase.gsub(/[^a-z0-9\s-]/, '').gsub(/\s+/, '-')
85
+ output << " - [#{method.upcase} #{path}](##{anchor})"
86
+ end
87
+ end
88
+
89
+ if @data['components'] && @data['components']['schemas']
90
+ output << '- [Schemas](#schemas)'
91
+ @data['components']['schemas'].each_key do |schema_name|
92
+ anchor = schema_name.downcase.gsub(/[^a-z0-9\s-]/, '').gsub(/\s+/, '-')
93
+ output << " - [#{schema_name}](##{anchor})"
94
+ end
95
+ end
96
+
97
+ output.join("\n")
98
+ end
99
+
100
+ def generate_endpoints
101
+ return nil unless @data['paths']
102
+
103
+ output = []
104
+ output << '## Endpoints'
105
+ output << ''
106
+
107
+ @data['paths'].each do |path, methods|
108
+ methods.each do |method, details|
109
+ next if method.start_with?('$')
110
+
111
+ output << @formatter.format_endpoint(path, method, details)
112
+ output << ''
113
+ end
114
+ end
115
+
116
+ output.join("\n")
117
+ end
118
+
119
+ def generate_schemas
120
+ return nil unless @data['components'] && @data['components']['schemas']
121
+
122
+ output = []
123
+ output << '## Schemas'
124
+ output << ''
125
+
126
+ @data['components']['schemas'].each do |schema_name, schema|
127
+ output << "### #{schema_name}"
128
+ output << ''
129
+ output << schema['description'] if schema['description']
130
+ output << ''
131
+ output << @formatter.format_schema(schema)
132
+ output << ''
133
+
134
+ next unless schema['example']
135
+
136
+ output << '**Example**:'
137
+ output << ''
138
+ output << '```json'
139
+ output << JSON.pretty_generate(schema['example'])
140
+ output << '```'
141
+ output << ''
142
+ end
143
+
144
+ output.join("\n")
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'psych'
4
+
5
+ module Transducer
6
+ class Parser
7
+ attr_reader :file_path, :data, :errors
8
+
9
+ def initialize(file_path)
10
+ @file_path = file_path
11
+ @data = nil
12
+ @errors = []
13
+ end
14
+
15
+ def parse
16
+ raise FileError, "File not found: #{file_path}" unless File.exist?(file_path)
17
+
18
+ begin
19
+ @data = Psych.safe_load_file(file_path, permitted_classes: [Symbol, Date, Time])
20
+ rescue Psych::SyntaxError => e
21
+ raise ParseError, "YAML syntax error at line #{e.line}: #{e.message}"
22
+ end
23
+
24
+ validate_openapi_spec
25
+ @data
26
+ end
27
+
28
+ def valid?
29
+ @errors.empty?
30
+ end
31
+
32
+ private
33
+
34
+ def validate_openapi_spec
35
+ @errors = []
36
+
37
+ unless @data.is_a?(Hash)
38
+ @errors << 'OpenAPI specification must be a hash'
39
+ raise ValidationError, @errors.join(', ')
40
+ end
41
+
42
+ validate_openapi_version
43
+ validate_info
44
+ validate_paths
45
+
46
+ raise ValidationError, @errors.join(', ') unless valid?
47
+ end
48
+
49
+ def validate_openapi_version
50
+ unless @data['openapi']
51
+ @errors << 'Missing required field: openapi'
52
+ return
53
+ end
54
+
55
+ version = @data['openapi'].to_s
56
+ return if version.start_with?('3.0') || version.start_with?('3.1')
57
+
58
+ @errors << "Unsupported OpenAPI version: #{version}. Only 3.0.x and 3.1.x are supported."
59
+ end
60
+
61
+ def validate_info
62
+ unless @data['info']
63
+ @errors << 'Missing required field: info'
64
+ return
65
+ end
66
+
67
+ info = @data['info']
68
+ @errors << 'Missing required field: info.title' unless info['title']
69
+ @errors << 'Missing required field: info.version' unless info['version']
70
+ end
71
+
72
+ def validate_paths
73
+ @errors << 'Missing required field: paths' unless @data['paths']
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,61 @@
1
+ # <%= data['info']['title'] %>
2
+
3
+ <% if data['info']['description'] %>
4
+ <%= data['info']['description'] %>
5
+
6
+ <% end %>
7
+ **Version**: <%= data['info']['version'] %>
8
+ <% if data['servers'] && !data['servers'].empty? %>
9
+ **Base URL**: <%= data['servers'][0]['url'] %>
10
+ <% end %>
11
+
12
+ ## Table of Contents
13
+
14
+ - [Endpoints](#endpoints)
15
+ <% data['paths']&.each do |path, methods| %>
16
+ <% methods.each_key do |method| %>
17
+ <% next if method.start_with?('$') %>
18
+ <% anchor = "#{method}-#{path}".downcase.gsub(/[^a-z0-9\s-]/, '').gsub(/\s+/, '-') %>
19
+ - [<%= method.upcase %> <%= path %>](#<%= anchor %>)
20
+ <% end %>
21
+ <% end %>
22
+ <% if data['components'] && data['components']['schemas'] %>
23
+ - [Schemas](#schemas)
24
+ <% data['components']['schemas'].each_key do |schema_name| %>
25
+ <% anchor = schema_name.downcase.gsub(/[^a-z0-9\s-]/, '').gsub(/\s+/, '-') %>
26
+ - [<%= schema_name %>](#<%= anchor %>)
27
+ <% end %>
28
+ <% end %>
29
+
30
+ ## Endpoints
31
+
32
+ <% data['paths']&.each do |path, methods| %>
33
+ <% methods.each do |method, details| %>
34
+ <% next if method.start_with?('$') %>
35
+ <%= formatter.format_endpoint(path, method, details) %>
36
+
37
+ <% end %>
38
+ <% end %>
39
+ <% if data['components'] && data['components']['schemas'] %>
40
+
41
+ ## Schemas
42
+
43
+ <% data['components']['schemas'].each do |schema_name, schema| %>
44
+ ### <%= schema_name %>
45
+
46
+ <% if schema['description'] %>
47
+ <%= schema['description'] %>
48
+
49
+ <% end %>
50
+ <%= formatter.format_schema(schema) %>
51
+
52
+ <% if schema['example'] %>
53
+ **Example**:
54
+
55
+ ```json
56
+ <%= JSON.pretty_generate(schema['example']) %>
57
+ ```
58
+
59
+ <% end %>
60
+ <% end %>
61
+ <% end %>
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Transducer
4
+ VERSION = '0.1.0'
5
+ end
data/lib/transducer.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'transducer/version'
4
+ require_relative 'transducer/parser'
5
+ require_relative 'transducer/formatter'
6
+ require_relative 'transducer/generator'
7
+ require_relative 'transducer/cli'
8
+
9
+ module Transducer
10
+ class Error < StandardError; end
11
+ class ParseError < Error; end
12
+ class ValidationError < Error; end
13
+ class FileError < Error; end
14
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: transducer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: psych
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '4.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '4.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: thor
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.3'
40
+ description: A Ruby gem that reads OpenAPI YAML specifications and generates well-formatted
41
+ Markdown documentation
42
+ email:
43
+ - t.yudai92@gmail.com
44
+ executables:
45
+ - transducer
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - CHANGELOG.md
50
+ - LICENSE.txt
51
+ - README.md
52
+ - exe/transducer
53
+ - lib/transducer.rb
54
+ - lib/transducer/cli.rb
55
+ - lib/transducer/formatter.rb
56
+ - lib/transducer/generator.rb
57
+ - lib/transducer/parser.rb
58
+ - lib/transducer/templates/default.md.erb
59
+ - lib/transducer/version.rb
60
+ homepage: https://github.com/ydah/transducer
61
+ licenses:
62
+ - MIT
63
+ metadata:
64
+ homepage_uri: https://github.com/ydah/transducer
65
+ source_code_uri: https://github.com/ydah/transducer
66
+ changelog_uri: https://github.com/ydah/transducer/blob/main/CHANGELOG.md
67
+ rubygems_mfa_required: 'true'
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.0
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubygems_version: 3.6.9
83
+ specification_version: 4
84
+ summary: Generate Markdown documentation from OpenAPI specifications
85
+ test_files: []