docit 0.2.1 → 0.3.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: 3fe9c345834352ce5e4219a291760ced965cdbad10084c28cbc2aabc2823a5b5
4
- data.tar.gz: 86aa4599733b72c52fe66335c4581e4f69b408af1bbb00c0c2d5f42b1b7a51fe
3
+ metadata.gz: 3ac8acf7fd2b736bb2c89c63a63bca482239783457d7572d864e0257a3a2c6cd
4
+ data.tar.gz: 1605b07b832dc73f4e7f00e0840aa72267cbb60262e17baba6842134ee576e19
5
5
  SHA512:
6
- metadata.gz: 9ad86a75a50a1a1177cf7cbde2bf49f12eb7cc36e1df9fd268ff2b6519c7b01efffc680aab2484a83f3dbdfb410740633a7cabbf8072d873403deafc6c14ccd3
7
- data.tar.gz: 2fbf560890376ef66614e12f961496b77ab5bfb2e994ffaeb006cc2a5ddd9ef82128115cf7d1fba39a89a889b0aec8342d487525eedad276a9b8244a6ee574d3
6
+ metadata.gz: a72fa34e950fdf918daab680c287311401a4059fa3b8d0bf16a0ebcef210ed607b697d3ad58a3343bf7d105a2a11d717efd0f8ee239464a82f58ca5f934b1e79
7
+ data.tar.gz: 96516820d968faae672a4caa7b6c52c9d79356b6366d09edaa019badb2c6949c0b4087115e6f306f559904b5dbcb02b594c2e0e58e864458c0c460e381d4d894
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-04-16
4
+
5
+ ### Added
6
+ - Scalar API Reference as a second documentation UI alongside Swagger UI
7
+ - New routes: `/api-docs/scalar` and `/api-docs/swagger` for direct access to each UI
8
+ - `config.default_ui` option (`:scalar` or `:swagger`) to control which UI renders at the root `/api-docs` path
9
+ - Navigation bar across both UIs for one-click switching between Scalar and Swagger
10
+ - Modular renderer architecture (`Docit::UI::BaseRenderer`, `SwaggerRenderer`, `ScalarRenderer`) for easy extension
11
+
12
+ ### Changed
13
+ - Default documentation UI is now Scalar (previously Swagger UI)
14
+ - `config.description` now defaults to a welcome message instead of an empty string
15
+ - Install generator template now documents the `default_ui` option
16
+ - UI controller refactored from monolithic HTML generation to thin dispatcher with pluggable renderers
17
+
3
18
  ## [0.2.1] - 2026-04-11
4
19
 
5
20
  ### Fixed
data/README.md CHANGED
@@ -5,6 +5,12 @@
5
5
 
6
6
  Decorator-style API documentation for Ruby on Rails. Write OpenAPI 3.0.3 docs with clean controller DSL macros, separate doc modules, or AI-assisted scaffolding for undocumented endpoints.
7
7
 
8
+ ### Scalar (default)
9
+ ![Scalar API Reference](docs/images/scalar_image.png)
10
+
11
+ ### Swagger
12
+ ![Swagger UI](docs/images/swagger_image.png)
13
+
8
14
  ## Table Of Contents
9
15
 
10
16
  - Getting started
@@ -32,6 +38,7 @@ Decorator-style API documentation for Ruby on Rails. Write OpenAPI 3.0.3 docs wi
32
38
  - [Supported providers](#supported-providers)
33
39
  - [What the AI generates](#what-the-ai-generates)
34
40
  - Runtime and development
41
+ - [Documentation UIs](#documentation-uis)
35
42
  - [How it works](#how-it-works)
36
43
  - [Mounting at a different path](#mounting-at-a-different-path)
37
44
  - [JSON spec only](#json-spec-only)
@@ -61,13 +68,13 @@ rails generate docit:install
61
68
  The install generator does everything in one step:
62
69
 
63
70
  1. Creates `config/initializers/docit.rb` with default settings
64
- 2. Mounts the Swagger UI engine at `/api-docs` in your routes
71
+ 2. Mounts the documentation engine at `/api-docs` in your routes
65
72
  3. Asks how you'd like to set up your docs:
66
73
  - **AI automatic docs** — configure an AI provider, then Docit scans your routes and generates complete documentation for every endpoint
67
74
  - **Manual docs** — Docit scans your routes and creates scaffolded doc files with TODO placeholders, injects `use_docs` into controllers, and lets you fill in the details
68
75
  - **Skip** — just install the base config and set up docs later
69
76
 
70
- Visit `/api-docs` to see your interactive API documentation.
77
+ Visit `/api-docs` to see your interactive API documentation (Scalar by default, Swagger UI also available at `/api-docs/swagger`).
71
78
 
72
79
  If you choose AI setup, Docit stores your provider config in `.docit_ai.yml` with restricted file permissions and adds that file to `.gitignore` when possible.
73
80
 
@@ -81,17 +88,20 @@ Docit.configure do |config|
81
88
  config.version = "1.0.0"
82
89
  config.description = "Backend API documentation"
83
90
 
91
+ # Documentation UI: :scalar (default) or :swagger
92
+ config.default_ui = :scalar
93
+
84
94
  # Authentication: pick one (or multiple):
85
95
  config.auth :bearer # Bearer token (JWT by default)
86
96
  config.auth :basic # HTTP Basic
87
97
  config.auth :api_key, name: "X-API-Key", # API key in header
88
98
  location: "header"
89
99
 
90
- # Tag descriptions (shown in Swagger UI sidebar):
100
+ # Tag descriptions (shown in the documentation sidebar):
91
101
  config.tag "Users", description: "User account management"
92
102
  config.tag "Auth", description: "Authentication endpoints"
93
103
 
94
- # Server URLs (shown in Swagger UI server dropdown):
104
+ # Server URLs (shown in the server dropdown):
95
105
  config.server "https://api.example.com", description: "Production"
96
106
  config.server "https://staging.example.com", description: "Staging"
97
107
  config.server "http://localhost:3000", description: "Development"
@@ -467,11 +477,24 @@ For each undocumented endpoint, Docit:
467
477
 
468
478
  Do not use AI autodoc on controllers that contain secrets, proprietary business rules, or internal comments you do not want sent to an external provider.
469
479
 
480
+ ## Documentation UIs
481
+
482
+ Docit ships with two documentation UIs, both reading from the same OpenAPI spec:
483
+
484
+ | Path | UI | Notes |
485
+ |------|------|-------|
486
+ | `/api-docs` | Default (Scalar) | Configurable via `config.default_ui` |
487
+ | `/api-docs/scalar` | Scalar API Reference | Modern UI with built-in API client, dark mode, code samples |
488
+ | `/api-docs/swagger` | Swagger UI | Classic OpenAPI explorer |
489
+ | `/api-docs/spec` | Raw JSON | OpenAPI 3.0.3 spec |
490
+
491
+ Both UIs include a navigation bar to switch between them. Set `config.default_ui = :swagger` to make Swagger the default at `/api-docs`.
492
+
470
493
  ## How it works
471
494
 
472
495
  1. `swagger_doc` registers an **Operation** for each controller action in a global **Registry**
473
496
  2. When someone visits `/api-docs/spec`, Docit's **SchemaGenerator** combines all registered operations with your Rails routes (via **RouteInspector**) to produce an OpenAPI 3.0.3 JSON document
474
- 3. The **Engine** serves Swagger UI at `/api-docs`, pointing it at the generated spec
497
+ 3. The **Engine** serves the configured documentation UI at `/api-docs`, pointing it at the generated spec
475
498
 
476
499
  The DSL is included in all controllers automatically via a Rails Engine initializer — no manual `include` needed if you're using `ActionController::API` or `ActionController::Base`.
477
500
 
@@ -4,10 +4,16 @@ require "json"
4
4
 
5
5
  module Docit
6
6
  class UiController < ActionController::Base
7
- SWAGGER_UI_VERSION = "5.32.2"
8
-
9
7
  def index
10
- render html: swagger_ui_html.html_safe, layout: false
8
+ render_ui(Docit.configuration.default_ui)
9
+ end
10
+
11
+ def swagger
12
+ render_ui(:swagger)
13
+ end
14
+
15
+ def scalar
16
+ render_ui(:scalar)
11
17
  end
12
18
 
13
19
  def spec
@@ -17,52 +23,23 @@ module Docit
17
23
 
18
24
  private
19
25
 
20
- def swagger_ui_html
21
- spec_url = "#{request.base_url}#{Docit::Engine.routes.url_helpers.spec_path}"
22
- spec_url_json = JSON.generate(spec_url)
23
- escaped_title = ERB::Util.html_escape(Docit.configuration.title)
26
+ RENDERERS = {
27
+ swagger: UI::SwaggerRenderer,
28
+ scalar: UI::ScalarRenderer
29
+ }.freeze
30
+
31
+ def render_ui(ui_name)
32
+ renderer = RENDERERS.fetch(ui_name).new(spec_url: spec_url, nav_paths: nav_paths)
33
+ render html: renderer.render.html_safe, layout: false
34
+ end
35
+
36
+ def spec_url
37
+ "#{request.base_url}#{Docit::Engine.routes.url_helpers.spec_path}"
38
+ end
24
39
 
25
- <<~HTML
26
- <!DOCTYPE html>
27
- <html lang="en">
28
- <head>
29
- <meta charset="UTF-8">
30
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
31
- <title>#{escaped_title}</title>
32
- <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@#{SWAGGER_UI_VERSION}/swagger-ui.css" />
33
- <style>
34
- html { box-sizing: border-box; overflow-y: scroll; }
35
- *, *:before, *:after { box-sizing: inherit; }
36
- body { margin: 0; background: #fafafa; }
37
- </style>
38
- </head>
39
- <body>
40
- <div id="swagger-ui"></div>
41
- <script src="https://unpkg.com/swagger-ui-dist@#{SWAGGER_UI_VERSION}/swagger-ui-bundle.js"></script>
42
- <script>
43
- SwaggerUIBundle({
44
- url: #{spec_url_json},
45
- dom_id: '#swagger-ui',
46
- presets: [
47
- SwaggerUIBundle.presets.apis,
48
- SwaggerUIBundle.SwaggerUIStandalonePreset
49
- ],
50
- layout: "BaseLayout",
51
- deepLinking: true,
52
- showExtensions: true,
53
- showCommonExtensions: true,
54
- requestInterceptor: function(req) {
55
- var url = new URL(req.url);
56
- url.protocol = window.location.protocol;
57
- url.host = window.location.host;
58
- req.url = url.toString();
59
- return req;
60
- }
61
- })
62
- </script>
63
- </body>
64
- </html>
65
- HTML
40
+ def nav_paths
41
+ helpers = Docit::Engine.routes.url_helpers
42
+ { swagger: helpers.swagger_path, scalar: helpers.scalar_path }
66
43
  end
67
44
  end
68
45
  end
data/config/routes.rb CHANGED
@@ -2,5 +2,7 @@
2
2
 
3
3
  Docit::Engine.routes.draw do
4
4
  root to: "ui#index"
5
+ get "swagger", to: "ui#swagger"
6
+ get "scalar", to: "ui#scalar"
5
7
  get "spec", to: "ui#spec", defaults: { format: :json }
6
8
  end
Binary file
Binary file
@@ -3,6 +3,8 @@
3
3
  module Docit
4
4
  module Ai
5
5
  class AutodocRunner
6
+ MAX_INVALID_OUTPUT_RETRIES = 2
7
+
6
8
  attr_reader :results
7
9
 
8
10
  def initialize(controller_filter: nil, dry_run: false, input: $stdin, output: $stdout)
@@ -50,7 +52,7 @@ module Docit
50
52
  end
51
53
 
52
54
  def check_base_setup!
53
- if !(defined?(Rails) && Rails.respond_to?(:root) && Rails.root)
55
+ if defined?(Rails) == false || Rails.respond_to?(:root) == false || Rails.root.nil?
54
56
  raise Docit::Error, "Docit requires a Rails application. Run this command from your app root."
55
57
  end
56
58
 
@@ -82,20 +84,20 @@ module Docit
82
84
  @output.puts "Docit will send controller source code to #{config.provider.capitalize} to generate documentation."
83
85
  @output.puts "Review the endpoints first if they contain secrets or proprietary logic."
84
86
 
85
- return if !(@input.respond_to?(:tty?) && @input.tty?)
86
-
87
- loop do
88
- @output.print "Continue? (y/n): "
89
- choice = @input.gets.to_s.strip.downcase
90
-
91
- case choice
92
- when "y", "yes"
93
- @output.puts ""
94
- return
95
- when "n", "no"
96
- raise Docit::Error, "Autodoc cancelled."
97
- else
98
- @output.puts "Please enter y or n."
87
+ if @input.respond_to?(:tty?) && @input.tty?
88
+ loop do
89
+ @output.print "Continue? (y/n): "
90
+ choice = @input.gets.to_s.strip.downcase
91
+
92
+ case choice
93
+ when "y", "yes"
94
+ @output.puts ""
95
+ return
96
+ when "n", "no"
97
+ raise Docit::Error, "Autodoc cancelled."
98
+ else
99
+ @output.puts "Please enter y or n."
100
+ end
99
101
  end
100
102
  end
101
103
  end
@@ -115,17 +117,31 @@ module Docit
115
117
  @output.puts " skipped (controller source file not found)"
116
118
  next
117
119
  end
118
-
119
- prompt = builder.build
120
120
  retries = 0
121
+ invalid_output_retries = 0
122
+ validation_error = nil
121
123
 
122
124
  begin
125
+ prompt = builder.build(validation_error: validation_error)
123
126
  doc_block = client.generate(prompt).strip
124
127
  doc_block = strip_markdown_fences(doc_block)
128
+ DocBlockValidator.new(
129
+ controller: gap[:controller],
130
+ action: gap[:action],
131
+ doc_block: doc_block
132
+ ).validate!
125
133
 
126
134
  generated[gap[:controller]] << doc_block
127
135
  @results[:generated] += 1
128
136
  @output.puts " done"
137
+ rescue Docit::Ai::InvalidDocBlockError => e
138
+ invalid_output_retries += 1
139
+ if invalid_output_retries <= MAX_INVALID_OUTPUT_RETRIES
140
+ validation_error = e.message
141
+ retry
142
+ end
143
+
144
+ @output.puts " failed (invalid doc DSL: #{e.message})"
129
145
  rescue Docit::Ai::RateLimitError => e
130
146
  retries += 1
131
147
  if retries <= max_retries
@@ -168,11 +184,11 @@ module Docit
168
184
 
169
185
  def inject_tags(generated)
170
186
  all_tags = generated.values.flatten.join("\n").scan(/tags\s+["']([^"']+)["']/).flatten
171
- return unless all_tags.any?
172
-
173
- injected = TagInjector.new(tags: all_tags).inject
174
- injected.each { |tag| @output.puts " Added tag \"#{tag}\" to config/initializers/docit.rb" }
175
- @results[:tags] = injected
187
+ if all_tags.any?
188
+ injected = TagInjector.new(tags: all_tags).inject
189
+ injected.each { |tag| @output.puts " Added tag \"#{tag}\" to config/initializers/docit.rb" }
190
+ @results[:tags] = injected
191
+ end
176
192
  end
177
193
 
178
194
  def strip_markdown_fences(text)
@@ -6,6 +6,11 @@ module Docit
6
6
  module Ai
7
7
  class Configuration
8
8
  CONFIG_FILE = ".docit_ai.yml"
9
+ GENERATED_FILE_COMMENT = <<~TEXT
10
+ # If you want to change the model and start Docit setup again,
11
+ # rerun: rails generate docit:install
12
+
13
+ TEXT
9
14
 
10
15
  PROVIDERS = %w[openai anthropic groq].freeze
11
16
 
@@ -51,11 +56,13 @@ module Docit
51
56
  config = new(provider: provider, model: model, api_key: api_key)
52
57
  raise Error, "Invalid configuration" if config.valid? == false
53
58
 
54
- File.write(config_path, {
59
+ yaml = {
55
60
  "provider" => config.provider,
56
61
  "model" => config.model,
57
62
  "api_key" => config.api_key
58
- }.to_yaml)
63
+ }.to_yaml
64
+
65
+ File.write(config_path, GENERATED_FILE_COMMENT + yaml)
59
66
  File.chmod(0o600, config_path)
60
67
 
61
68
  config
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ module Ai
5
+ class InvalidDocBlockError < Error; end
6
+
7
+ class DocBlockValidator
8
+ def initialize(controller:, action:, doc_block:)
9
+ @controller = controller
10
+ @action = action.to_sym
11
+ @doc_block = doc_block
12
+ end
13
+
14
+ def validate!
15
+ doc_module = Module.new
16
+ doc_module.extend(Docit::DocFile)
17
+ doc_module.module_eval(@doc_block, "(generated Docit block)", 1)
18
+
19
+ validate_actions!(doc_module.actions)
20
+
21
+ operation = Docit::Operation.new(controller: @controller, action: @action)
22
+ operation.instance_eval(&doc_module[@action])
23
+
24
+ true
25
+ rescue SyntaxError, StandardError => e
26
+ raise InvalidDocBlockError, error_message_for(e)
27
+ end
28
+
29
+ private
30
+
31
+ def validate_actions!(actions)
32
+ return if actions == [@action]
33
+
34
+ raise InvalidDocBlockError, "Generated output did not define a doc block" if actions.empty?
35
+
36
+ action_list = actions.map { |action| ":#{action}" }.join(", ")
37
+ raise InvalidDocBlockError,
38
+ "Generated output must define only doc :#{@action}, got #{action_list}"
39
+ end
40
+
41
+ def error_message_for(error)
42
+ return error.message if error.is_a?(InvalidDocBlockError)
43
+
44
+ "#{error.class}: #{error.message}"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -66,7 +66,7 @@ module Docit
66
66
  @gap = gap
67
67
  end
68
68
 
69
- def build
69
+ def build(validation_error: nil)
70
70
  <<~PROMPT
71
71
  You are generating Docit DSL documentation for a Ruby on Rails API endpoint.
72
72
 
@@ -89,12 +89,18 @@ module Docit
89
89
  Rules:
90
90
  - Output ONLY the `doc :#{@gap[:action]} do ... end` block
91
91
  - No module wrapper, no explanation, no markdown fences
92
+ - Use exactly `doc :#{@gap[:action]} do ... end` for the requested action
93
+ - Use ONLY the DSL methods listed above
92
94
  - Infer parameters from the path (e.g., {id} → path parameter)
93
95
  - Infer request body from params usage in the controller
94
96
  - Infer response structure from render calls
97
+ - Inside `request_body` and `response` blocks, define nested data only with `property ..., type: :object` or `property ..., type: :array`
98
+ - Never call standalone helpers such as `object`, `array`, `string`, `integer`, `number`, or `boolean`
99
+ - Return valid Ruby that can be `instance_eval`'d as-is
95
100
  - Use realistic examples
96
101
  - Include appropriate error responses
97
102
  - Use the controller name to determine appropriate tags
103
+ #{validation_feedback(validation_error)}
98
104
  PROMPT
99
105
  end
100
106
 
@@ -114,10 +120,22 @@ module Docit
114
120
  end
115
121
 
116
122
  def controller_file_path
117
- return nil if defined?(Rails) == false || Rails.respond_to?(:root) == false || Rails.root.nil?
123
+ return nil unless defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
118
124
 
119
125
  relative = @gap[:controller].underscore
120
- Rails.root.join("app", "controllers", "#{relative}.rb").to_s
126
+ ::Rails.root.join("app", "controllers", "#{relative}.rb").to_s
127
+ end
128
+
129
+ def validation_feedback(validation_error)
130
+ return "" if validation_error.nil? || validation_error.empty?
131
+
132
+ <<~FEEDBACK
133
+
134
+ Previous attempt failed Docit validation:
135
+ - #{validation_error}
136
+
137
+ Regenerate the block and fix that error.
138
+ FEEDBACK
121
139
  end
122
140
  end
123
141
  end
data/lib/docit/ai.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "ai/openai_client"
6
6
  require_relative "ai/anthropic_client"
7
7
  require_relative "ai/groq_client"
8
8
  require_relative "ai/gap_detector"
9
+ require_relative "ai/doc_block_validator"
9
10
  require_relative "ai/prompt_builder"
10
11
  require_relative "ai/doc_writer"
11
12
  require_relative "ai/tag_injector"
@@ -3,18 +3,31 @@
3
3
  module Docit
4
4
  # Holds global API documentation settings: metadata, authentication, tags, and servers.
5
5
  class Configuration
6
+ SUPPORTED_UIS = %i[scalar swagger].freeze
7
+
6
8
  attr_accessor :title, :version, :description, :base_url
9
+ attr_reader :default_ui
7
10
 
8
11
  def initialize
9
12
  @title = "API Documentation"
10
13
  @version = "1.0.0"
11
- @description = ""
14
+ @description = "Welcome to the API documentation. Browse the endpoints below to get started."
12
15
  @base_url = "/"
16
+ @default_ui = :scalar
13
17
  @security_schemes = {}
14
18
  @tags = []
15
19
  @servers = []
16
20
  end
17
21
 
22
+ def default_ui=(value)
23
+ ui = value.to_sym
24
+ unless SUPPORTED_UIS.include?(ui)
25
+ raise ArgumentError, "Unsupported UI: #{value}. Must be one of: #{SUPPORTED_UIS.join(", ")}"
26
+ end
27
+
28
+ @default_ui = ui
29
+ end
30
+
18
31
  def auth(type, **options)
19
32
  case type.to_s.downcase
20
33
  when "basic"
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ module UI
5
+ class BaseRenderer
6
+ attr_reader :spec_url, :title, :nav_paths
7
+
8
+ def initialize(spec_url:, nav_paths: {})
9
+ @spec_url = spec_url
10
+ @nav_paths = nav_paths
11
+ @title = ERB::Util.html_escape(Docit.configuration.title)
12
+ end
13
+
14
+ def render
15
+ raise NotImplementedError, "#{self.class}#render must be implemented"
16
+ end
17
+
18
+ private
19
+
20
+ def nav_bar(active:)
21
+ swagger_active = active == :swagger
22
+ scalar_active = active == :scalar
23
+
24
+ <<~HTML
25
+ <nav style="
26
+ display: flex; align-items: center; gap: 8px;
27
+ padding: 6px 16px;
28
+ background: #1a1a2e; color: #fff;
29
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
30
+ font-size: 13px; position: sticky; top: 0; z-index: 9999;
31
+ ">
32
+ <span style="font-weight: 600; margin-right: auto;">#{title}</span>
33
+ #{nav_link("Swagger", nav_paths[:swagger], active: swagger_active)}
34
+ #{nav_link("Scalar", nav_paths[:scalar], active: scalar_active)}
35
+ </nav>
36
+ HTML
37
+ end
38
+
39
+ def nav_link(label, path, active:)
40
+ escaped_path = ERB::Util.html_escape(path)
41
+ style = if active
42
+ "color: #fff; text-decoration: none; padding: 4px 12px; border-radius: 4px; background: rgba(255,255,255,0.15); font-weight: 500;"
43
+ else
44
+ "color: rgba(255,255,255,0.7); text-decoration: none; padding: 4px 12px; border-radius: 4px;"
45
+ end
46
+
47
+ %(<a href="#{escaped_path}" style="#{style}">#{label}</a>)
48
+ end
49
+
50
+ def spec_url_json
51
+ JSON.generate(spec_url)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ module UI
5
+ class ScalarRenderer < BaseRenderer
6
+ def render
7
+ <<~HTML
8
+ <!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title>#{title}</title>
14
+ <style>
15
+ body { margin: 0; }
16
+ </style>
17
+ </head>
18
+ <body>
19
+ #{nav_bar(active: :scalar)}
20
+ <script id="api-reference"></script>
21
+ <script>
22
+ document.getElementById('api-reference').dataset.configuration = JSON.stringify({
23
+ spec: { url: #{spec_url_json} },
24
+ theme: "elysiajs",
25
+ showSidebar: true,
26
+ hideDownloadButton: false,
27
+ hideModels: false,
28
+ searchHotKey: "k"
29
+ })
30
+ </script>
31
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
32
+ </body>
33
+ </html>
34
+ HTML
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ module UI
5
+ class SwaggerRenderer < BaseRenderer
6
+ VERSION = "5.32.2"
7
+
8
+ def render
9
+ <<~HTML
10
+ <!DOCTYPE html>
11
+ <html lang="en">
12
+ <head>
13
+ <meta charset="UTF-8">
14
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
15
+ <title>#{title}</title>
16
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@#{VERSION}/swagger-ui.css" />
17
+ <style>
18
+ html { box-sizing: border-box; overflow-y: scroll; }
19
+ *, *:before, *:after { box-sizing: inherit; }
20
+ body { margin: 0; background: #fafafa; }
21
+ </style>
22
+ </head>
23
+ <body>
24
+ #{nav_bar(active: :swagger)}
25
+ <div id="swagger-ui"></div>
26
+ <script src="https://unpkg.com/swagger-ui-dist@#{VERSION}/swagger-ui-bundle.js"></script>
27
+ <script>
28
+ SwaggerUIBundle({
29
+ url: #{spec_url_json},
30
+ dom_id: '#swagger-ui',
31
+ presets: [
32
+ SwaggerUIBundle.presets.apis,
33
+ SwaggerUIBundle.SwaggerUIStandalonePreset
34
+ ],
35
+ layout: "BaseLayout",
36
+ deepLinking: true,
37
+ showExtensions: true,
38
+ showCommonExtensions: true,
39
+ requestInterceptor: function(req) {
40
+ var url = new URL(req.url);
41
+ url.protocol = window.location.protocol;
42
+ url.host = window.location.host;
43
+ req.url = url.toString();
44
+ return req;
45
+ }
46
+ })
47
+ </script>
48
+ </body>
49
+ </html>
50
+ HTML
51
+ end
52
+ end
53
+ end
54
+ end
data/lib/docit/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Docit
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/docit.rb CHANGED
@@ -12,6 +12,9 @@ require_relative "docit/doc_file"
12
12
  require_relative "docit/route_inspector"
13
13
  require_relative "docit/schema_generator"
14
14
  require_relative "docit/dsl"
15
+ require_relative "docit/ui/base_renderer"
16
+ require_relative "docit/ui/swagger_renderer"
17
+ require_relative "docit/ui/scalar_renderer"
15
18
 
16
19
  # Docit is a decorator-style API documentation gem for Ruby on Rails.
17
20
  # It generates OpenAPI 3.0.3 specs from clean DSL macros on your controllers.
@@ -1,15 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Docit.configure do |config|
4
- # The title shown in Swagger UI
4
+ # The title shown in the API documentation UI
5
5
  config.title = "<%= Rails.application.class.module_parent_name rescue 'My API' %>"
6
6
 
7
7
  # API version
8
8
  config.version = "1.0.0"
9
9
 
10
- # Description shown in Swagger UI
10
+ # Description shown on the introduction page
11
11
  config.description = "API documentation powered by Docit"
12
12
 
13
+ # Documentation UI: :scalar (default) or :swagger
14
+ # config.default_ui = :scalar
15
+
13
16
  # Authentication scheme (options: :bearer, :basic, :api_key)
14
17
  # config.auth :bearer
15
18
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - S13G
@@ -43,12 +43,15 @@ files:
43
43
  - Rakefile
44
44
  - app/controllers/docit/ui_controller.rb
45
45
  - config/routes.rb
46
+ - docs/images/scalar_image.png
47
+ - docs/images/swagger_image.png
46
48
  - lib/docit.rb
47
49
  - lib/docit/ai.rb
48
50
  - lib/docit/ai/anthropic_client.rb
49
51
  - lib/docit/ai/autodoc_runner.rb
50
52
  - lib/docit/ai/client.rb
51
53
  - lib/docit/ai/configuration.rb
54
+ - lib/docit/ai/doc_block_validator.rb
52
55
  - lib/docit/ai/doc_writer.rb
53
56
  - lib/docit/ai/gap_detector.rb
54
57
  - lib/docit/ai/groq_client.rb
@@ -68,6 +71,9 @@ files:
68
71
  - lib/docit/route_inspector.rb
69
72
  - lib/docit/schema_definition.rb
70
73
  - lib/docit/schema_generator.rb
74
+ - lib/docit/ui/base_renderer.rb
75
+ - lib/docit/ui/scalar_renderer.rb
76
+ - lib/docit/ui/swagger_renderer.rb
71
77
  - lib/docit/version.rb
72
78
  - lib/generators/docit/ai_setup/ai_setup_generator.rb
73
79
  - lib/generators/docit/install/install_generator.rb
@@ -80,7 +86,7 @@ licenses:
80
86
  metadata:
81
87
  homepage_uri: https://github.com/S13G/docit
82
88
  source_code_uri: https://github.com/S13G/docit
83
- changelog_uri: https://github.com/S13G/docit/blob/main/CHANGELOG.md
89
+ changelog_uri: https://github.com/S13G/docit/blob/master/CHANGELOG.md
84
90
  documentation_uri: https://rubydoc.info/gems/docit
85
91
  bug_tracker_uri: https://github.com/S13G/docit/issues
86
92
  rubygems_mfa_required: 'true'