oas_rage 0.2.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 +7 -0
- data/.release-please-config.json +64 -0
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +12 -0
- data/.rubocop_todo.yml +12 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +8 -0
- data/README.md +38 -0
- data/Rakefile +12 -0
- data/lib/oas_rage/configuration.rb +30 -0
- data/lib/oas_rage/oas_route_builder.rb +79 -0
- data/lib/oas_rage/route_extractor.rb +105 -0
- data/lib/oas_rage/utils.rb +19 -0
- data/lib/oas_rage/version.rb +5 -0
- data/lib/oas_rage/web/assets/rapidoc-min.js +3915 -0
- data/lib/oas_rage/web/view.rb +35 -0
- data/lib/oas_rage/web/views/index.html.erb +145 -0
- data/lib/oas_rage.rb +37 -0
- data/sig/oas_rage.rbs +4 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 105b36f0df7a0ce3a829ba070e83b30cf6d7bfc512ef45950c11bf62eb8f0760
|
4
|
+
data.tar.gz: 3c01928efdc49854f1b3bc5e65f7404a21a2e4ab8b169cabfada0ba45ca6e5bf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 88a773990c11d3d748b206c2bd9a188d3962606253d465a8c92ce912e450a79ddbabb729901acc19b5784b45b67558718f1490fd798bd8bdb4401138600482a1
|
7
|
+
data.tar.gz: eac5dc00f10a0c19a7b799d10c310ccddb2ca4a232429a2f6f041765662011c9773fbf06ab93fb5d5fb4c87efe24ff4c71bab327edeb0dd2866db3bb2ce0c24b
|
@@ -0,0 +1,64 @@
|
|
1
|
+
{
|
2
|
+
"release-type": "ruby",
|
3
|
+
"changelog-sections": [
|
4
|
+
{
|
5
|
+
"type": "feat",
|
6
|
+
"section": "Features"
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"type": "feature",
|
10
|
+
"section": "Features"
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"type": "fix",
|
14
|
+
"section": "Bug Fixes"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"type": "perf",
|
18
|
+
"section": "Performance Improvements"
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"type": "revert",
|
22
|
+
"section": "Reverts"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
"type": "docs",
|
26
|
+
"section": "Documentation"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"type": "style",
|
30
|
+
"section": "Styles",
|
31
|
+
"hidden": true
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"type": "chore",
|
35
|
+
"section": "Miscellaneous Chores",
|
36
|
+
"hidden": true
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"type": "refactor",
|
40
|
+
"section": "Code Refactoring"
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"type": "test",
|
44
|
+
"section": "Tests"
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"type": "build",
|
48
|
+
"section": "Build System",
|
49
|
+
"hidden": true
|
50
|
+
},
|
51
|
+
{
|
52
|
+
"type": "ci",
|
53
|
+
"section": "Continuous Integration",
|
54
|
+
"hidden": true
|
55
|
+
}
|
56
|
+
],
|
57
|
+
"packages": {
|
58
|
+
".": {
|
59
|
+
"release-type": "ruby",
|
60
|
+
"package-name": "oas_rage",
|
61
|
+
"version-file": "lib/oas_rage/version.rb"
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2025-06-10 00:03:15 UTC using RuboCop version 1.76.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 2
|
10
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
11
|
+
Metrics/AbcSize:
|
12
|
+
Max: 19
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.4.4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [0.2.0](https://github.com/a-chacon/oas_rage/compare/oas_rage-v0.1.0...oas_rage/v0.2.0) (2025-06-10)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* first release, minimal features ([406f64f](https://github.com/a-chacon/oas_rage/commit/406f64f634288beb2f99cf466de09de02f607597))
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+

|
2
|
+

|
3
|
+

|
4
|
+

|
5
|
+

|
6
|
+

|
7
|
+
|
8
|
+
# 📃Open API Specification For Rage
|
9
|
+
|
10
|
+
OasRage is a tool for generating **automatic interactive documentation for your Rage APIs**. It generates an **OAS 3.1** document and displays it using **[RapiDoc](https://rapidocweb.com)**.
|
11
|
+
|
12
|
+
Built for the high-performance [Rage](https://github.com/rage-rb/rage) framework, OasRage leverages Rage's compatibility with Rails and its modern Ruby features, including fiber scheduling for non-blocking I/O. It relies on the [OasCore](https://github.com/a-chacon/oas_core) gem for seamless OpenAPI integration.
|
13
|
+
|
14
|
+

|
15
|
+
|
16
|
+
## Documentation
|
17
|
+
|
18
|
+
For details on how to install, configure, and use OasRage, please refer to the [OasCore MDBook](http://a-chacon.com/oas_core).
|
19
|
+
|
20
|
+
## Contributing
|
21
|
+
|
22
|
+
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star⭐! Thanks again!
|
23
|
+
|
24
|
+
If you plan a big feature, first open an issue to discuss it before any development.
|
25
|
+
|
26
|
+
1. Fork the Project
|
27
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
28
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
29
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
30
|
+
5. Open a Pull Request
|
31
|
+
|
32
|
+
## License
|
33
|
+
|
34
|
+
The gem is available as open source under the terms of the [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text).
|
35
|
+
|
36
|
+
## Star History
|
37
|
+
|
38
|
+
[](https://www.star-history.com/#a-chacon/oas_rails&Date)
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OasRage
|
4
|
+
class Configuration < OasCore::Configuration
|
5
|
+
attr_accessor :autodiscover_request_body, :autodiscover_responses, :ignored_actions, :rapidoc_theme, :layout
|
6
|
+
attr_reader :route_extractor, :include_mode
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
@route_extractor = RouteExtractor
|
11
|
+
@include_mode = :all
|
12
|
+
@autodiscover_request_body = true
|
13
|
+
@autodiscover_responses = true
|
14
|
+
@ignored_actions = []
|
15
|
+
@rapidoc_theme = :rails
|
16
|
+
@layout = nil
|
17
|
+
|
18
|
+
# TODO: implement
|
19
|
+
# autodiscover_request_body
|
20
|
+
# autodiscover_responses
|
21
|
+
end
|
22
|
+
|
23
|
+
def include_mode=(value)
|
24
|
+
valid_modes = %i[all with_tags explicit]
|
25
|
+
raise ArgumentError, "include_mode must be one of #{valid_modes}" unless valid_modes.include?(value)
|
26
|
+
|
27
|
+
@include_mode = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OasRage
|
4
|
+
class OasRouteBuilder
|
5
|
+
def self.build_from_rage_route(rage_route)
|
6
|
+
new(rage_route).build
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(rage_route)
|
10
|
+
@rage_route = rage_route
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
OasCore::OasRoute.new(
|
15
|
+
controller_class: controller_class,
|
16
|
+
controller_action: controller_action,
|
17
|
+
controller: controller,
|
18
|
+
method_name: method,
|
19
|
+
verb: verb,
|
20
|
+
path: path,
|
21
|
+
docstring: docstring,
|
22
|
+
source_string: source_string,
|
23
|
+
tags: tags
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def controller_class
|
30
|
+
controller_name = @rage_route[:meta][:raw_handler].split('#').first
|
31
|
+
"#{Utils.camelize(controller_name)}Controller"
|
32
|
+
end
|
33
|
+
|
34
|
+
def controller_action
|
35
|
+
"#{controller_class}##{@rage_route[:meta][:raw_handler].split('#').last}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def controller
|
39
|
+
@rage_route[:meta][:raw_handler].split('#').first
|
40
|
+
end
|
41
|
+
|
42
|
+
def method
|
43
|
+
@rage_route[:meta][:raw_handler].split('#').last
|
44
|
+
end
|
45
|
+
|
46
|
+
def verb
|
47
|
+
@rage_route[:method]
|
48
|
+
end
|
49
|
+
|
50
|
+
def path
|
51
|
+
@rage_route[:path].to_s.gsub('(.:format)', '').gsub(/:\w+/) { |match| "{#{match[1..]}}" }
|
52
|
+
end
|
53
|
+
|
54
|
+
def source_string
|
55
|
+
Utils.constantize(controller_class).instance_method(method).source
|
56
|
+
end
|
57
|
+
|
58
|
+
def docstring
|
59
|
+
comment_lines = Utils.constantize(controller_class).instance_method(method).comment.lines
|
60
|
+
processed_lines = comment_lines.map { |line| line.sub(/^# /, '') }
|
61
|
+
::YARD::Docstring.parser.parse(processed_lines.join).to_docstring
|
62
|
+
end
|
63
|
+
|
64
|
+
def tags
|
65
|
+
method_comment = Utils.constantize(controller_class).instance_method(method).comment
|
66
|
+
class_comment = Utils.constantize(controller_class).instance_method(method).class_comment
|
67
|
+
|
68
|
+
method_tags = parse_tags(method_comment)
|
69
|
+
class_tags = parse_tags(class_comment)
|
70
|
+
|
71
|
+
method_tags + class_tags
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_tags(comment)
|
75
|
+
lines = comment.lines.map { |line| line.sub(/^# /, '') }
|
76
|
+
::YARD::Docstring.parser.parse(lines.join).tags
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OasRage
|
4
|
+
class RouteExtractor
|
5
|
+
def initialize
|
6
|
+
@host_routes = nil
|
7
|
+
@host_paths = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def host_routes_by_path(path)
|
11
|
+
host_routes.select { |r| r.path == path }
|
12
|
+
end
|
13
|
+
|
14
|
+
def host_routes
|
15
|
+
@host_routes ||= extract_host_routes
|
16
|
+
end
|
17
|
+
|
18
|
+
# Clear instance variable @host_routes
|
19
|
+
#
|
20
|
+
# This method clears the instance variable @host_routes
|
21
|
+
# to force a re-extraction of the routes.
|
22
|
+
def clear_cache
|
23
|
+
@host_routes = nil
|
24
|
+
@host_paths = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def host_paths
|
28
|
+
@host_paths ||= host_routes.map(&:path).uniq.sort
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def extract_host_routes
|
34
|
+
routes = valid_routes.map { |r| OasRouteBuilder.build_from_rage_route(r) }
|
35
|
+
routes.select! { |route| route.tags.any? } if OasRage.config.include_mode == :with_tags
|
36
|
+
if OasRage.config.include_mode == :explicit
|
37
|
+
routes.select! do |route|
|
38
|
+
route.tags.any? do |t|
|
39
|
+
t.tag_name == 'oas_include'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
routes
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_routes
|
47
|
+
Rage.__router.routes.select do |route|
|
48
|
+
valid_api_route?(route)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid_api_route?(route)
|
53
|
+
return false unless valid_route_implementation?(route)
|
54
|
+
# return false unless route.path.spec.to_s.start_with?(OasRage.config.api_path)
|
55
|
+
return false if ignore_custom_actions(route)
|
56
|
+
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
# Checks if a route has a valid implementation.
|
61
|
+
#
|
62
|
+
# This method verifies that both the controller and the action specified
|
63
|
+
# in the route exist. It checks if the controller class is defined and
|
64
|
+
# if the action method is implemented within that controller.
|
65
|
+
#
|
66
|
+
# @param route [ActionDispatch::Journey::Route] The route to check.
|
67
|
+
# @return [Boolean] true if both the controller and action exist, false otherwise.
|
68
|
+
def valid_route_implementation?(route)
|
69
|
+
raw_handler = route[:meta][:raw_handler]
|
70
|
+
return false unless raw_handler.is_a?(String) && raw_handler.include?('#')
|
71
|
+
|
72
|
+
controller_name, action_name = raw_handler.split('#')
|
73
|
+
controller_name = "#{Utils.camelize(controller_name)}Controller"
|
74
|
+
|
75
|
+
controller_class = Utils.safe_constantize(controller_name)
|
76
|
+
return false unless controller_class
|
77
|
+
|
78
|
+
controller_class.instance_methods.include?(action_name.to_sym)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ignore user-specified paths in initializer configuration.
|
82
|
+
# Sanitize api_path by removing the "/" if it starts with that, and adding "/" if it ends without that.
|
83
|
+
# Support controller name only to ignore all controller actions.
|
84
|
+
# Support ignoring "controller#action"
|
85
|
+
# Ignoring "controller#action" AND "api_path/controller#action"
|
86
|
+
def ignore_custom_actions(route)
|
87
|
+
api_path = "#{OasRage.config.api_path.sub(%r{\A/}, '')}/".sub(%r{/+$}, '/')
|
88
|
+
ignored_actions = OasRage.config.ignored_actions.flat_map do |custom_route|
|
89
|
+
if custom_route.start_with?(api_path)
|
90
|
+
[custom_route]
|
91
|
+
else
|
92
|
+
["#{api_path}#{custom_route}", custom_route]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
raw_handler = route[:meta][:raw_handler]
|
97
|
+
return false unless raw_handler.is_a?(String) && raw_handler.include?('#')
|
98
|
+
|
99
|
+
controller_action = raw_handler
|
100
|
+
controller_only = raw_handler.split('#').first
|
101
|
+
|
102
|
+
ignored_actions.include?(controller_action) || ignored_actions.include?(controller_only)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OasRage
|
4
|
+
module Utils
|
5
|
+
def self.camelize(str)
|
6
|
+
str.split('_').map(&:capitalize).join
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.safe_constantize(str)
|
10
|
+
Object.const_get(str)
|
11
|
+
rescue NameError
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.constantize(str)
|
16
|
+
Object.const_get(str)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|