docit 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.
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ # Converts the Registry of operations and Configuration into an OpenAPI 3.0.3 spec hash.
5
+ class SchemaGenerator
6
+ def self.generate
7
+ new.generate
8
+ end
9
+
10
+ def generate
11
+ config = Docit.configuration
12
+
13
+ spec = {
14
+ openapi: "3.0.3",
15
+ info: {
16
+ title: config.title,
17
+ version: config.version,
18
+ description: config.description
19
+ },
20
+ paths: build_paths,
21
+ components: {
22
+ securitySchemes: config.security_schemes
23
+ }
24
+ }
25
+
26
+ tag_defs = config.tags
27
+ spec[:tags] = tag_defs if tag_defs.any?
28
+
29
+ server_defs = config.servers
30
+ spec[:servers] = server_defs if server_defs.any?
31
+
32
+ schemas = build_component_schemas
33
+ spec[:components][:schemas] = schemas if schemas.any?
34
+
35
+ spec
36
+ end
37
+
38
+ private
39
+
40
+ def build_paths
41
+ paths = {}
42
+
43
+ Docit::Registry.operations.each do |operation|
44
+ routes = RouteInspector.routes_for(operation.controller, operation.action)
45
+ next if routes.empty?
46
+
47
+ routes.each do |route|
48
+ path = route[:path]
49
+ method = route[:method]
50
+
51
+ paths[path] ||= {}
52
+ paths[path][method] = build_operation(operation)
53
+ end
54
+ end
55
+
56
+ paths
57
+ end
58
+
59
+ def build_operation(operation)
60
+ result = {
61
+ summary: operation._summary || operation.action.humanize,
62
+ description: operation._description || "",
63
+ tags: operation._tags,
64
+ responses: build_responses(operation._responses)
65
+ }
66
+
67
+ params = operation._parameters.params
68
+ result[:parameters] = params if params.any?
69
+
70
+ result[:requestBody] = build_request_body(operation._request_body) if operation._request_body
71
+ result[:deprecated] = true if operation._deprecated
72
+ result[:security] = operation._security.map { |s| { s.to_s => [] } } if operation._security.any?
73
+
74
+ result
75
+ end
76
+
77
+ def build_responses(response_builders)
78
+ return { "200" => { description: "Success" } } if response_builders.empty?
79
+
80
+ response_builders.each_with_object({}) do |builder, hash|
81
+ entry = { description: builder.description }
82
+
83
+ if builder.schema_ref
84
+ schema = { "$ref" => "#/components/schemas/#{builder.schema_ref}" }
85
+ content = { "application/json" => { schema: schema } }
86
+ content["application/json"][:examples] = build_examples(builder.examples) if builder.examples.any?
87
+ entry[:content] = content
88
+ elsif builder.properties.any?
89
+ schema = {
90
+ type: "object",
91
+ properties: build_properties(builder.properties)
92
+ }
93
+ content = { "application/json" => { schema: schema } }
94
+ content["application/json"][:examples] = build_examples(builder.examples) if builder.examples.any?
95
+ entry[:content] = content
96
+ end
97
+
98
+ hash[builder.status.to_s] = entry
99
+ end
100
+ end
101
+
102
+ def build_request_body(request_body_builder)
103
+ if request_body_builder.schema_ref
104
+ schema = { "$ref" => "#/components/schemas/#{request_body_builder.schema_ref}" }
105
+ else
106
+ schema = {
107
+ type: "object",
108
+ properties: build_properties(request_body_builder.properties)
109
+ }
110
+
111
+ required_properties = request_body_builder.required_properties
112
+ schema[:required] = required_properties if required_properties.any?
113
+ end
114
+
115
+ {
116
+ required: request_body_builder.required,
117
+ content: {
118
+ request_body_builder.content_type => { schema: schema }
119
+ }
120
+ }
121
+ end
122
+
123
+ def build_properties(props)
124
+ props.each_with_object({}) do |prop, hash|
125
+ schema = build_property_schema(prop)
126
+ hash[prop[:name].to_s] = schema
127
+ end
128
+ end
129
+
130
+ def build_property_schema(prop)
131
+ type = prop[:type].to_s
132
+
133
+ if type == "file"
134
+ schema = { type: "string", format: "binary" }
135
+ schema[:description] = prop[:description] if prop[:description]
136
+ schema
137
+ elsif type == "array"
138
+ schema = { type: "array" }
139
+ if prop[:children]
140
+ schema[:items] = {
141
+ type: "object",
142
+ properties: build_properties(prop[:children])
143
+ }
144
+ else
145
+ items_type = prop[:items]&.to_s || "string"
146
+ schema[:items] = { type: items_type }
147
+ end
148
+ schema[:description] = prop[:description] if prop[:description]
149
+ schema[:example] = prop[:example] if prop[:example]
150
+ schema
151
+ elsif type == "object" && prop[:children]
152
+ schema = {
153
+ type: "object",
154
+ properties: build_properties(prop[:children])
155
+ }
156
+ schema[:description] = prop[:description] if prop[:description]
157
+ schema[:example] = prop[:example] if prop[:example]
158
+ schema
159
+ else
160
+ schema = { type: type }
161
+ schema[:format] = prop[:format].to_s if prop[:format]
162
+ schema[:enum] = prop[:enum] if prop[:enum]
163
+ schema[:example] = prop[:example] if prop[:example]
164
+ schema[:description] = prop[:description] if prop[:description]
165
+ schema
166
+ end
167
+ end
168
+
169
+ def build_component_schemas
170
+ Docit.schemas.each_with_object({}) do |(name, definition), hash|
171
+ schema = {
172
+ type: "object",
173
+ properties: build_properties(definition.properties)
174
+ }
175
+ hash[name.to_s] = schema
176
+ end
177
+ end
178
+
179
+ def build_examples(examples)
180
+ examples.each_with_object({}) do |ex, hash|
181
+ entry = { value: ex[:value] }
182
+ entry[:description] = ex[:description] if ex[:description]
183
+ hash[ex[:name].to_s] = entry
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ VERSION = "0.1.0"
5
+ end
data/lib/docit.rb ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "docit/version"
4
+ require_relative "docit/configuration"
5
+ require_relative "docit/registry"
6
+ require_relative "docit/builders/request_body_builder"
7
+ require_relative "docit/builders/response_builder"
8
+ require_relative "docit/builders/parameter_builder"
9
+ require_relative "docit/operation"
10
+ require_relative "docit/schema_definition"
11
+ require_relative "docit/doc_file"
12
+ require_relative "docit/route_inspector"
13
+ require_relative "docit/schema_generator"
14
+ require_relative "docit/dsl"
15
+
16
+ # Docit is a decorator-style API documentation gem for Ruby on Rails.
17
+ # It generates OpenAPI 3.0.3 specs from clean DSL macros on your controllers.
18
+ module Docit
19
+ class Error < StandardError; end
20
+
21
+ class << self
22
+ def configure
23
+ yield configuration
24
+ end
25
+
26
+ def configuration
27
+ @configuration ||= Configuration.new
28
+ end
29
+
30
+ def reset_configuration!
31
+ @configuration = Configuration.new
32
+ end
33
+
34
+ def schemas
35
+ @schemas ||= {}
36
+ end
37
+
38
+ def define_schema(name, &block)
39
+ definition = SchemaDefinition.new(name)
40
+ definition.instance_eval(&block) if block_given?
41
+ schemas[name.to_sym] = definition
42
+ end
43
+
44
+ def reset_schemas!
45
+ @schemas = {}
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docit
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc "Creates a Docit initializer and mounts the engine in routes"
7
+ source_root File.expand_path("templates", __dir__)
8
+
9
+ def copy_initializer
10
+ template "initializer.rb", "config/initializers/docit.rb"
11
+ end
12
+
13
+ def mount_engine
14
+ route 'mount Docit::Engine => "/api-docs"'
15
+ end
16
+
17
+ def print_instructions
18
+ say ""
19
+ say "Docit installed successfully!", :green
20
+ say ""
21
+ say "Next steps:"
22
+ say " 1. Edit config/initializers/docit.rb to customize your API docs"
23
+ say " 2. Add swagger_doc blocks to your controller actions"
24
+ say " 3. Visit /api-docs to see your Swagger UI"
25
+ say ""
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ Docit.configure do |config|
4
+ # The title shown in Swagger UI
5
+ config.title = "<%= Rails.application.class.module_parent_name rescue 'My API' %>"
6
+
7
+ # API version
8
+ config.version = "1.0.0"
9
+
10
+ # Description shown in Swagger UI
11
+ config.description = "API documentation powered by Docit"
12
+
13
+ # Authentication scheme (options: :bearer, :basic, :api_key)
14
+ # config.auth :bearer
15
+
16
+ # For API key auth:
17
+ # config.auth :api_key, name: "X-API-Key", location: "header"
18
+ end
data/sig/docit.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Docit
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - S13G
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: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ description: Docit lets you write OpenAPI 3.0 docs as clean DSL macros directly on
27
+ your Rails controller actions. No RSpec integration required, no external doc files.
28
+ Just annotate and go.
29
+ email:
30
+ - siegdomain1@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
36
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
37
+ - ".github/PULL_REQUEST_TEMPLATE.md"
38
+ - ".github/workflows/ci.yml"
39
+ - CHANGELOG.md
40
+ - CODE_OF_CONDUCT.md
41
+ - CONTRIBUTING.md
42
+ - LICENSE.txt
43
+ - README.md
44
+ - Rakefile
45
+ - app/controllers/docit/ui_controller.rb
46
+ - config/routes.rb
47
+ - lib/docit.rb
48
+ - lib/docit/builders/parameter_builder.rb
49
+ - lib/docit/builders/request_body_builder.rb
50
+ - lib/docit/builders/response_builder.rb
51
+ - lib/docit/configuration.rb
52
+ - lib/docit/doc_file.rb
53
+ - lib/docit/dsl.rb
54
+ - lib/docit/engine.rb
55
+ - lib/docit/operation.rb
56
+ - lib/docit/registry.rb
57
+ - lib/docit/route_inspector.rb
58
+ - lib/docit/schema_definition.rb
59
+ - lib/docit/schema_generator.rb
60
+ - lib/docit/version.rb
61
+ - lib/generators/docit/install/install_generator.rb
62
+ - lib/generators/docit/install/templates/initializer.rb
63
+ - sig/docit.rbs
64
+ homepage: https://github.com/S13G/docket
65
+ licenses:
66
+ - MIT
67
+ metadata:
68
+ homepage_uri: https://github.com/S13G/docket
69
+ source_code_uri: https://github.com/S13G/docket
70
+ changelog_uri: https://github.com/S13G/docket/blob/main/CHANGELOG.md
71
+ documentation_uri: https://rubydoc.info/gems/docit
72
+ rubygems_mfa_required: 'true'
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 3.2.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 4.0.3
88
+ specification_version: 4
89
+ summary: Decorator-style Swagger/OpenAPI documentation generator for Ruby on Rails.
90
+ test_files: []