oas_rails 0.2.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -1
- data/app/controllers/oas_rails/oas_rails_controller.rb +1 -1
- data/lib/generators/oas_rails/config/templates/oas_rails_initializer.rb +11 -0
- data/lib/oas_rails/builders/content_builder.rb +55 -0
- data/lib/oas_rails/builders/operation_builder.rb +32 -0
- data/lib/oas_rails/builders/parameter_builder.rb +28 -0
- data/lib/oas_rails/builders/parameters_builder.rb +39 -0
- data/lib/oas_rails/builders/path_item_builder.rb +22 -0
- data/lib/oas_rails/builders/request_body_builder.rb +60 -0
- data/lib/oas_rails/builders/response_builder.rb +40 -0
- data/lib/oas_rails/builders/responses_builder.rb +58 -0
- data/lib/oas_rails/configuration.rb +25 -5
- data/lib/oas_rails/esquema_builder.rb +37 -0
- data/lib/oas_rails/extractors/oas_route_extractor.rb +66 -0
- data/lib/oas_rails/extractors/render_response_extractor.rb +148 -0
- data/lib/oas_rails/extractors/route_extractor.rb +125 -0
- data/lib/oas_rails/oas_route.rb +1 -99
- data/lib/oas_rails/spec/components.rb +85 -0
- data/lib/oas_rails/spec/contact.rb +18 -0
- data/lib/oas_rails/spec/hashable.rb +39 -0
- data/lib/oas_rails/{info.rb → spec/info.rb} +30 -24
- data/lib/oas_rails/spec/license.rb +18 -0
- data/lib/oas_rails/spec/media_type.rb +84 -0
- data/lib/oas_rails/spec/operation.rb +25 -0
- data/lib/oas_rails/spec/parameter.rb +34 -0
- data/lib/oas_rails/spec/path_item.rb +33 -0
- data/lib/oas_rails/spec/paths.rb +26 -0
- data/lib/oas_rails/spec/reference.rb +16 -0
- data/lib/oas_rails/spec/request_body.rb +21 -0
- data/lib/oas_rails/spec/response.rb +20 -0
- data/lib/oas_rails/spec/responses.rb +25 -0
- data/lib/oas_rails/spec/server.rb +17 -0
- data/lib/oas_rails/spec/specable.rb +51 -0
- data/lib/oas_rails/spec/specification.rb +50 -0
- data/lib/oas_rails/spec/tag.rb +18 -0
- data/lib/oas_rails/utils.rb +39 -0
- data/lib/oas_rails/version.rb +1 -1
- data/lib/oas_rails.rb +47 -26
- metadata +32 -18
- data/lib/oas_rails/contact.rb +0 -12
- data/lib/oas_rails/license.rb +0 -11
- data/lib/oas_rails/media_type.rb +0 -76
- data/lib/oas_rails/oas_base.rb +0 -30
- data/lib/oas_rails/operation.rb +0 -134
- data/lib/oas_rails/parameter.rb +0 -47
- data/lib/oas_rails/path_item.rb +0 -25
- data/lib/oas_rails/paths.rb +0 -19
- data/lib/oas_rails/request_body.rb +0 -29
- data/lib/oas_rails/response.rb +0 -12
- data/lib/oas_rails/responses.rb +0 -20
- data/lib/oas_rails/route_extractor.rb +0 -119
- data/lib/oas_rails/server.rb +0 -10
- data/lib/oas_rails/specification.rb +0 -72
- data/lib/oas_rails/tag.rb +0 -17
@@ -0,0 +1,148 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Extractors
|
3
|
+
# Extracts and processes render responses from a given source.
|
4
|
+
module RenderResponseExtractor
|
5
|
+
class << self
|
6
|
+
# Extracts responses from the provided source string.
|
7
|
+
#
|
8
|
+
# @param source [String] The source string containing render calls.
|
9
|
+
# @return [Array<Response>] An array of Response objects extracted from the source.
|
10
|
+
def extract_responses_from_source(specification, source:)
|
11
|
+
render_calls = extract_render_calls(source)
|
12
|
+
return [Builders::ResponseBuilder.new(specification).with_description("No content").with_code(204).build] if render_calls.empty?
|
13
|
+
|
14
|
+
render_calls.map { |render_content, status| process_render_content(specification, render_content.strip, status) }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Extracts render calls from the source string.
|
20
|
+
#
|
21
|
+
# @param source [String] The source string containing render calls.
|
22
|
+
# @return [Array<Array<String, String>>] An array of arrays, each containing render content and status.
|
23
|
+
def extract_render_calls(source)
|
24
|
+
source.scan(/render json: ((?:\{.*?\}|\S+))(?:, status: :(\w+))?(?:,.*?)?$/m)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Processes the render content and status to build a Response object.
|
28
|
+
#
|
29
|
+
# @param content [String] The content extracted from the render call.
|
30
|
+
# @param status [String] The status code associated with the render call.
|
31
|
+
# @return [Response] A Response object based on the processed content and status.
|
32
|
+
def process_render_content(specification, content, status)
|
33
|
+
schema, examples = build_schema_and_examples(content)
|
34
|
+
status_int = Utils.status_to_integer(status)
|
35
|
+
content = Builders::ContentBuilder.new(specification, :outgoing).with_schema(schema).with_examples(examples).build
|
36
|
+
|
37
|
+
Builders::ResponseBuilder.new(specification).with_code(status_int).with_description(Utils.status_code_to_text(status_int)).with_content(content).build
|
38
|
+
end
|
39
|
+
|
40
|
+
# Builds schema and examples based on the content type.
|
41
|
+
#
|
42
|
+
# @param content [String] The content extracted from the render call.
|
43
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
44
|
+
def build_schema_and_examples(content)
|
45
|
+
if content.start_with?('{')
|
46
|
+
[Utils.hash_to_json_schema(parse_hash_structure(content)), {}]
|
47
|
+
else
|
48
|
+
process_non_hash_content(content)
|
49
|
+
end
|
50
|
+
rescue StandardError => e
|
51
|
+
Rails.logger.debug("Error building schema: #{e.message}")
|
52
|
+
[{}]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Processes non-hash content (e.g., model or method calls) to build schema and examples.
|
56
|
+
#
|
57
|
+
# @param content [String] The content extracted from the render call.
|
58
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
59
|
+
def process_non_hash_content(content)
|
60
|
+
maybe_a_model, errors = content.gsub('@', "").split(".")
|
61
|
+
klass = maybe_a_model.singularize.camelize(:upper).constantize
|
62
|
+
|
63
|
+
if klass.ancestors.include?(ActiveRecord::Base)
|
64
|
+
schema = EsquemaBuilder.build_outgoing_schema(klass:)
|
65
|
+
if test_singularity(maybe_a_model)
|
66
|
+
build_singular_model_schema_and_examples(maybe_a_model, errors, klass, schema)
|
67
|
+
else
|
68
|
+
build_array_model_schema_and_examples(maybe_a_model, klass, schema)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
[{}]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Builds schema and examples for singular models.
|
76
|
+
#
|
77
|
+
# @param maybe_a_model [String] The model name or variable.
|
78
|
+
# @param errors [String, nil] Errors related to the model.
|
79
|
+
# @param klass [Class] The class associated with the model.
|
80
|
+
# @param schema [Hash] The schema for the model.
|
81
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
82
|
+
def build_singular_model_schema_and_examples(_maybe_a_model, errors, klass, schema)
|
83
|
+
if errors.nil?
|
84
|
+
[schema, Spec::MediaType.search_for_examples_in_tests(klass, context: :outgoing)]
|
85
|
+
else
|
86
|
+
# TODO: this is not building the real schema.
|
87
|
+
[
|
88
|
+
{
|
89
|
+
type: "object",
|
90
|
+
properties: {
|
91
|
+
success: { type: "boolean" },
|
92
|
+
errors: {
|
93
|
+
type: "object",
|
94
|
+
additionalProperties: {
|
95
|
+
type: "array",
|
96
|
+
items: { type: "string" }
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
},
|
101
|
+
{}
|
102
|
+
]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Builds schema and examples for array models.
|
107
|
+
#
|
108
|
+
# @param maybe_a_model [String] The model name or variable.
|
109
|
+
# @param klass [Class] The class associated with the model.
|
110
|
+
# @param schema [Hash] The schema for the model.
|
111
|
+
# @return [Array<Hash, Hash>] An array where the first element is the schema and the second is the examples.
|
112
|
+
def build_array_model_schema_and_examples(maybe_a_model, klass, schema)
|
113
|
+
examples = { maybe_a_model => { value: Spec::MediaType.search_for_examples_in_tests(klass, context: :outgoing).values.map { |p| p.dig(:value, maybe_a_model.singularize.to_sym) } } }
|
114
|
+
[{ type: "array", items: schema }, examples]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Determines if a string represents a singular model.
|
118
|
+
#
|
119
|
+
# @param str [String] The string to test.
|
120
|
+
# @return [Boolean] True if the string is a singular model, false otherwise.
|
121
|
+
def test_singularity(str)
|
122
|
+
str.pluralize != str && str.singularize == str
|
123
|
+
end
|
124
|
+
|
125
|
+
# Parses a hash literal to determine its structure.
|
126
|
+
#
|
127
|
+
# @param hash_literal [String] The hash literal string.
|
128
|
+
# @return [Hash<Symbol, String>] A hash representing the structure of the input.
|
129
|
+
def parse_hash_structure(hash_literal)
|
130
|
+
structure = {}
|
131
|
+
|
132
|
+
hash_literal.scan(/(\w+):\s*(\S+)/) do |key, value|
|
133
|
+
structure[key.to_sym] = case value
|
134
|
+
when 'true', 'false'
|
135
|
+
'Boolean'
|
136
|
+
when /^\d+$/
|
137
|
+
'Number'
|
138
|
+
else
|
139
|
+
'Object'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
structure
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Extractors
|
3
|
+
class RouteExtractor
|
4
|
+
RAILS_DEFAULT_CONTROLLERS = %w[
|
5
|
+
rails/info
|
6
|
+
rails/mailers
|
7
|
+
active_storage/blobs
|
8
|
+
active_storage/disk
|
9
|
+
active_storage/direct_uploads
|
10
|
+
active_storage/representations
|
11
|
+
rails/conductor/continuous_integration
|
12
|
+
rails/conductor/multiple_databases
|
13
|
+
rails/conductor/action_mailbox
|
14
|
+
rails/conductor/action_text
|
15
|
+
action_cable
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
RAILS_DEFAULT_PATHS = %w[
|
19
|
+
/rails/action_mailbox/
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def host_routes_by_path(path)
|
24
|
+
@host_routes ||= extract_host_routes
|
25
|
+
@host_routes.select { |r| r.path == path }
|
26
|
+
end
|
27
|
+
|
28
|
+
def host_routes
|
29
|
+
@host_routes ||= extract_host_routes
|
30
|
+
end
|
31
|
+
|
32
|
+
# Clear Class Instance Variable @host_routes
|
33
|
+
#
|
34
|
+
# This method clear the class instance variable @host_routes
|
35
|
+
# to force a extraction of the routes again.
|
36
|
+
def clear_cache
|
37
|
+
@host_routes = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def host_paths
|
41
|
+
@host_paths ||= host_routes.map(&:path).uniq.sort
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_route(route)
|
45
|
+
route.gsub('(.:format)', '').gsub(/:\w+/) { |match| "{#{match[1..]}}" }
|
46
|
+
end
|
47
|
+
|
48
|
+
# THIS CODE IS NOT IN USE BUT CAN BE USEFULL WITH GLOBAL TAGS OR AUTH TAGS
|
49
|
+
# def get_controller_comments(controller_path)
|
50
|
+
# YARD.parse_string(File.read(controller_path))
|
51
|
+
# controller_class = YARD::Registry.all(:class).first
|
52
|
+
# if controller_class
|
53
|
+
# class_comment = controller_class.docstring.all
|
54
|
+
# method_comments = controller_class.meths.map do |method|
|
55
|
+
# {
|
56
|
+
# name: method.name,
|
57
|
+
# comment: method.docstring.all
|
58
|
+
# }
|
59
|
+
# end
|
60
|
+
# YARD::Registry.clear
|
61
|
+
# {
|
62
|
+
# class_comment: class_comment,
|
63
|
+
# method_comments: method_comments
|
64
|
+
# }
|
65
|
+
# else
|
66
|
+
# YARD::Registry.clear
|
67
|
+
# nil
|
68
|
+
# end
|
69
|
+
# rescue StandardError
|
70
|
+
# nil
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# def get_controller_comment(controller_path)
|
74
|
+
# get_controller_comments(controller_path)&.dig(:class_comment) || ''
|
75
|
+
# rescue StandardError
|
76
|
+
# ''
|
77
|
+
# end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def extract_host_routes
|
82
|
+
valid_routes.map { |r| OasRoute.new_from_rails_route(rails_route: r) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def valid_routes
|
86
|
+
Rails.application.routes.routes.select do |route|
|
87
|
+
valid_api_route?(route)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def valid_api_route?(route)
|
92
|
+
return false unless valid_route_implementation?(route)
|
93
|
+
return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) }
|
94
|
+
return false if RAILS_DEFAULT_PATHS.any? { |path| route.path.spec.to_s.include?(path) }
|
95
|
+
return false unless route.path.spec.to_s.start_with?(OasRails.config.api_path)
|
96
|
+
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
# Checks if a route has a valid implementation.
|
101
|
+
#
|
102
|
+
# This method verifies that both the controller and the action specified
|
103
|
+
# in the route exist. It checks if the controller class is defined and
|
104
|
+
# if the action method is implemented within that controller.
|
105
|
+
#
|
106
|
+
# @param route [ActionDispatch::Journey::Route] The route to check.
|
107
|
+
# @return [Boolean] true if both the controller and action exist, false otherwise.
|
108
|
+
def valid_route_implementation?(route)
|
109
|
+
controller_name = route.defaults[:controller]&.camelize
|
110
|
+
action_name = route.defaults[:action]
|
111
|
+
|
112
|
+
return false if controller_name.blank? || action_name.blank?
|
113
|
+
|
114
|
+
controller_class = "#{controller_name}Controller".safe_constantize
|
115
|
+
|
116
|
+
if controller_class.nil?
|
117
|
+
false
|
118
|
+
else
|
119
|
+
controller_class.instance_methods.include?(action_name.to_sym)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/oas_rails/oas_route.rb
CHANGED
@@ -19,7 +19,7 @@ module OasRails
|
|
19
19
|
@controller_path = controller_path_extractor(@rails_route.defaults[:controller])
|
20
20
|
@method = @rails_route.defaults[:action]
|
21
21
|
@verb = @rails_route.verb
|
22
|
-
@path = RouteExtractor.clean_route(@rails_route.path.spec.to_s)
|
22
|
+
@path = Extractors::RouteExtractor.clean_route(@rails_route.path.spec.to_s)
|
23
23
|
@docstring = extract_docstring
|
24
24
|
@source_string = extract_source_string
|
25
25
|
end
|
@@ -41,103 +41,5 @@ module OasRails
|
|
41
41
|
def controller_path_extractor(controller)
|
42
42
|
Rails.root.join("app/controllers/#{controller}_controller.rb").to_s
|
43
43
|
end
|
44
|
-
|
45
|
-
def detect_request_body
|
46
|
-
klass = @controller.singularize.camelize.constantize
|
47
|
-
RequestBody.from_model_class(klass:, required: true)
|
48
|
-
end
|
49
|
-
|
50
|
-
def extract_responses_from_source
|
51
|
-
render_calls = @source_string.scan(/render json: ((?:\{.*?\}|\S+))(?:, status: :(\w+))?(?:,.*?)?$/m)
|
52
|
-
|
53
|
-
return [Response.new(code: 204, description: "No Content", content: {})] if render_calls.empty?
|
54
|
-
|
55
|
-
render_calls.map do |render_content, status|
|
56
|
-
content = render_content.strip
|
57
|
-
|
58
|
-
# TODO: manage when is an array of errors
|
59
|
-
schema = {}
|
60
|
-
begin
|
61
|
-
schema = if content.start_with?('{')
|
62
|
-
Utils.hash_to_json_schema(parse_hash_structure(content))
|
63
|
-
else
|
64
|
-
# It's likely a variable or method call
|
65
|
-
maybe_a_model, errors = content.gsub('@', "").split(".")
|
66
|
-
klass = maybe_a_model.singularize.camelize(:upper).constantize
|
67
|
-
return {} unless klass.ancestors.include? ActiveRecord::Base
|
68
|
-
|
69
|
-
e = Esquema::Builder.new(klass).build_schema.as_json
|
70
|
-
if test_singularity(maybe_a_model)
|
71
|
-
if errors.nil?
|
72
|
-
e
|
73
|
-
else
|
74
|
-
{
|
75
|
-
type: "object",
|
76
|
-
properties: {
|
77
|
-
success: {
|
78
|
-
type: "boolean"
|
79
|
-
},
|
80
|
-
errors: {
|
81
|
-
type: "object",
|
82
|
-
additionalProperties: {
|
83
|
-
type: "array",
|
84
|
-
items: {
|
85
|
-
type: "string"
|
86
|
-
}
|
87
|
-
}
|
88
|
-
}
|
89
|
-
}
|
90
|
-
} end
|
91
|
-
else
|
92
|
-
{ type: "array", items: e }
|
93
|
-
end
|
94
|
-
end
|
95
|
-
rescue StandardError => e
|
96
|
-
Rails.logger.debug("Error building schema: #{e.message}")
|
97
|
-
end
|
98
|
-
|
99
|
-
status_int = status_to_integer(status)
|
100
|
-
Response.new(code: status_int, description: status_code_to_text(status_int), content: { "application/json": MediaType.new(schema:) })
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def test_singularity(str)
|
105
|
-
str.pluralize != str && str.singularize == str
|
106
|
-
end
|
107
|
-
|
108
|
-
def parse_hash_structure(hash_literal)
|
109
|
-
structure = {}
|
110
|
-
|
111
|
-
hash_literal.scan(/(\w+):\s*(\S+)/) do |key, value|
|
112
|
-
structure[key.to_sym] = case value
|
113
|
-
when 'true', 'false'
|
114
|
-
'Boolean'
|
115
|
-
when /^\d+$/
|
116
|
-
'Number'
|
117
|
-
when '@user.errors'
|
118
|
-
'Object'
|
119
|
-
else
|
120
|
-
'Object'
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
structure
|
125
|
-
end
|
126
|
-
|
127
|
-
def status_to_integer(status)
|
128
|
-
return 200 if status.nil?
|
129
|
-
|
130
|
-
if status.to_s =~ /^\d+$/
|
131
|
-
status.to_i
|
132
|
-
else
|
133
|
-
status = "unprocessable_content" if status == "unprocessable_entity"
|
134
|
-
Rack::Utils::SYMBOL_TO_STATUS_CODE[status.to_sym]
|
135
|
-
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def status_code_to_text(status_code)
|
140
|
-
Rack::Utils::HTTP_STATUS_CODES[status_code] || "Unknown Status Code"
|
141
|
-
end
|
142
44
|
end
|
143
45
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
class Components
|
4
|
+
include Specable
|
5
|
+
|
6
|
+
attr_accessor :schemas, :parameters, :security_schemes, :request_bodies, :responses, :headers, :examples, :links, :callbacks
|
7
|
+
|
8
|
+
def initialize(specification)
|
9
|
+
@specification = specification
|
10
|
+
@schemas = {}
|
11
|
+
@parameters = {}
|
12
|
+
@security_schemes = OasRails.config.security_schemas
|
13
|
+
@request_bodies = {}
|
14
|
+
@responses = {}
|
15
|
+
@headers = {}
|
16
|
+
@examples = {}
|
17
|
+
@links = {}
|
18
|
+
@callbacks = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def oas_fields
|
22
|
+
[:request_bodies, :examples, :responses, :schemas, :parameters, :security_schemes]
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_response(response)
|
26
|
+
key = response.hash_key
|
27
|
+
@responses[key] = response unless @responses.key? key
|
28
|
+
|
29
|
+
response_reference(key)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_parameter(parameter)
|
33
|
+
key = parameter.hash_key
|
34
|
+
@parameters[key] = parameter unless @parameters.key? key
|
35
|
+
|
36
|
+
parameter_reference(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_request_body(request_body)
|
40
|
+
key = request_body.hash_key
|
41
|
+
@request_bodies[key] = request_body unless @request_bodies.key? key
|
42
|
+
|
43
|
+
request_body_reference(key)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_schema(schema)
|
47
|
+
key = Hashable.generate_hash(schema)
|
48
|
+
@schemas[key] = schema if @schemas[key].nil?
|
49
|
+
|
50
|
+
schema_reference(key)
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_example(example)
|
54
|
+
key = Hashable.generate_hash(example)
|
55
|
+
@examples[key] = example if @examples[key].nil?
|
56
|
+
|
57
|
+
example_reference(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_reference(type, name)
|
61
|
+
"#/components/#{type}/#{name}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def schema_reference(name)
|
65
|
+
Reference.new(create_reference('schemas', name))
|
66
|
+
end
|
67
|
+
|
68
|
+
def response_reference(name)
|
69
|
+
Reference.new(create_reference('responses', name))
|
70
|
+
end
|
71
|
+
|
72
|
+
def parameter_reference(name)
|
73
|
+
Reference.new(create_reference('parameters', name))
|
74
|
+
end
|
75
|
+
|
76
|
+
def example_reference(name)
|
77
|
+
Reference.new(create_reference('examples', name))
|
78
|
+
end
|
79
|
+
|
80
|
+
def request_body_reference(name)
|
81
|
+
Reference.new(create_reference('requestBodies', name))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
class Contact
|
4
|
+
include Specable
|
5
|
+
attr_accessor :name, :url, :email
|
6
|
+
|
7
|
+
def initialize(**kwargs)
|
8
|
+
@name = kwargs[:name] || ''
|
9
|
+
@url = kwargs[:url] || ''
|
10
|
+
@email = kwargs[:email] || ''
|
11
|
+
end
|
12
|
+
|
13
|
+
def oas_fields
|
14
|
+
[:name, :url, :email]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Hashable
|
6
|
+
def hash_key
|
7
|
+
Hashable.generate_hash(hash_representation)
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash_representation
|
11
|
+
public_instance_variables.sort.to_h { |var| [var, instance_variable_get(var)] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.generate_hash(obj)
|
15
|
+
Digest::MD5.hexdigest(hash_representation_recursive(obj).to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def public_instance_variables
|
19
|
+
instance_variables.select do |var|
|
20
|
+
method_name = var.to_s.delete('@')
|
21
|
+
respond_to?(method_name) || respond_to?("#{method_name}=")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.hash_representation_recursive(obj)
|
26
|
+
case obj
|
27
|
+
when Hash
|
28
|
+
obj.transform_values { |v| hash_representation_recursive(v) }
|
29
|
+
when Array
|
30
|
+
obj.map { |v| hash_representation_recursive(v) }
|
31
|
+
when Hashable
|
32
|
+
obj.hash_representation
|
33
|
+
else
|
34
|
+
obj
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,28 +1,33 @@
|
|
1
1
|
module OasRails
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
2
|
+
module Spec
|
3
|
+
class Info
|
4
|
+
include Specable
|
5
|
+
attr_accessor :title, :summary, :description, :terms_of_service, :contact, :license, :version
|
6
|
+
|
7
|
+
def initialize(**kwargs)
|
8
|
+
@title = kwargs[:title] || default_title
|
9
|
+
@summary = kwargs[:summary] || default_summary
|
10
|
+
@description = kwargs[:description] || default_description
|
11
|
+
@terms_of_service = kwargs[:terms_of_service] || ''
|
12
|
+
@contact = Spec::Contact.new
|
13
|
+
@license = Spec::License.new
|
14
|
+
@version = kwargs[:version] || '0.0.1'
|
15
|
+
end
|
16
|
+
|
17
|
+
def oas_fields
|
18
|
+
[:title, :summary, :description, :terms_of_service, :contact, :license, :version]
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_title
|
22
|
+
"OasRails #{VERSION}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_summary
|
26
|
+
"OasRails: Automatic Interactive API Documentation for Rails"
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_description
|
30
|
+
"# Welcome to OasRails
|
26
31
|
|
27
32
|
OasRails automatically generates interactive documentation for your Rails APIs using the OpenAPI Specification 3.1 (OAS 3.1) and displays it with a nice UI.
|
28
33
|
|
@@ -55,6 +60,7 @@ Explore your API documentation and enjoy the power of OasRails!
|
|
55
60
|
For more information and advanced usage, visit the [OasRails GitHub repository](https://github.com/a-chacon/oas_rails).
|
56
61
|
|
57
62
|
"
|
63
|
+
end
|
58
64
|
end
|
59
65
|
end
|
60
66
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OasRails
|
2
|
+
module Spec
|
3
|
+
class License
|
4
|
+
include Specable
|
5
|
+
|
6
|
+
attr_accessor :name, :url
|
7
|
+
|
8
|
+
def initialize(**kwargs)
|
9
|
+
@name = kwargs[:name] || 'GPL 3.0'
|
10
|
+
@url = kwargs[:url] || 'https://www.gnu.org/licenses/gpl-3.0.html#license-text'
|
11
|
+
end
|
12
|
+
|
13
|
+
def oas_fields
|
14
|
+
[:name, :url]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|