oas_rails 0.16.0 → 0.17.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/lib/oas_rails/builders/oas_route_builder.rb +2 -3
  4. data/lib/oas_rails/configuration.rb +14 -127
  5. data/lib/oas_rails/engine.rb +0 -5
  6. data/lib/oas_rails/version.rb +1 -1
  7. data/lib/oas_rails.rb +18 -64
  8. metadata +4 -54
  9. data/lib/oas_rails/builders/content_builder.rb +0 -62
  10. data/lib/oas_rails/builders/operation_builder.rb +0 -32
  11. data/lib/oas_rails/builders/parameter_builder.rb +0 -28
  12. data/lib/oas_rails/builders/parameters_builder.rb +0 -39
  13. data/lib/oas_rails/builders/path_item_builder.rb +0 -24
  14. data/lib/oas_rails/builders/request_body_builder.rb +0 -61
  15. data/lib/oas_rails/builders/response_builder.rb +0 -40
  16. data/lib/oas_rails/builders/responses_builder.rb +0 -81
  17. data/lib/oas_rails/extractors/oas_route_extractor.rb +0 -66
  18. data/lib/oas_rails/json_schema_generator.rb +0 -132
  19. data/lib/oas_rails/oas_route.rb +0 -21
  20. data/lib/oas_rails/spec/components.rb +0 -96
  21. data/lib/oas_rails/spec/contact.rb +0 -18
  22. data/lib/oas_rails/spec/hashable.rb +0 -39
  23. data/lib/oas_rails/spec/info.rb +0 -66
  24. data/lib/oas_rails/spec/license.rb +0 -18
  25. data/lib/oas_rails/spec/media_type.rb +0 -24
  26. data/lib/oas_rails/spec/operation.rb +0 -25
  27. data/lib/oas_rails/spec/parameter.rb +0 -37
  28. data/lib/oas_rails/spec/path_item.rb +0 -33
  29. data/lib/oas_rails/spec/paths.rb +0 -26
  30. data/lib/oas_rails/spec/reference.rb +0 -16
  31. data/lib/oas_rails/spec/request_body.rb +0 -21
  32. data/lib/oas_rails/spec/response.rb +0 -20
  33. data/lib/oas_rails/spec/responses.rb +0 -25
  34. data/lib/oas_rails/spec/server.rb +0 -17
  35. data/lib/oas_rails/spec/specable.rb +0 -54
  36. data/lib/oas_rails/spec/specification.rb +0 -50
  37. data/lib/oas_rails/spec/tag.rb +0 -18
  38. data/lib/oas_rails/yard/example_tag.rb +0 -12
  39. data/lib/oas_rails/yard/oas_rails_factory.rb +0 -169
  40. data/lib/oas_rails/yard/parameter_tag.rb +0 -14
  41. data/lib/oas_rails/yard/request_body_example_tag.rb +0 -11
  42. data/lib/oas_rails/yard/request_body_tag.rb +0 -16
  43. data/lib/oas_rails/yard/response_example_tag.rb +0 -12
  44. data/lib/oas_rails/yard/response_tag.rb +0 -13
@@ -1,37 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Parameter
4
- include Specable
5
- include Hashable
6
-
7
- STYLE_DEFAULTS = { query: 'form', path: 'simple', header: 'simple', cookie: 'form' }.freeze
8
-
9
- attr_accessor :name, :style, :description, :required, :schema
10
- attr_reader :in
11
-
12
- def initialize(specification)
13
- @specification = specification
14
- @name = ""
15
- @in = ""
16
- @description = ""
17
- @required = false
18
- @style = ""
19
- @schema = { type: 'string' }
20
- end
21
-
22
- def in=(value)
23
- @in = value
24
- @style = STYLE_DEFAULTS[@in.to_sym]
25
- @required = true if value == "path"
26
- end
27
-
28
- def required?
29
- @in == 'path'
30
- end
31
-
32
- def oas_fields
33
- [:name, :in, :description, :required, :schema, :style]
34
- end
35
- end
36
- end
37
- end
@@ -1,33 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class PathItem
4
- include Specable
5
- attr_reader :get, :post, :put, :patch, :delete
6
-
7
- def initialize(specification)
8
- @specification = specification
9
- @get = nil
10
- @post = nil
11
- @put = nil
12
- @patch = nil
13
- @delete = nil
14
- end
15
-
16
- def fill_from(path)
17
- OasRails.config.route_extractor.host_routes_by_path(path).each do |oas_route|
18
- add_operation(oas_route.verb.downcase, Spec::Operation.new(@specification).fill_from(oas_route))
19
- end
20
-
21
- self
22
- end
23
-
24
- def add_operation(http_method, operation)
25
- instance_variable_set("@#{http_method}", operation)
26
- end
27
-
28
- def oas_fields
29
- OasRails.config.http_verbs
30
- end
31
- end
32
- end
33
- end
@@ -1,26 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Paths
4
- include Specable
5
-
6
- attr_accessor :path_items
7
-
8
- def initialize(specification)
9
- @specification = specification
10
- @path_items = {}
11
- end
12
-
13
- def add_path(path)
14
- @path_items[path] = Builders::PathItemBuilder.new(@specification).from_path(path).build
15
- end
16
-
17
- def to_spec
18
- paths_hash = {}
19
- @path_items.each do |path, path_object|
20
- paths_hash[path] = path_object.to_spec
21
- end
22
- paths_hash
23
- end
24
- end
25
- end
26
- end
@@ -1,16 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Reference
4
- include Specable
5
- attr_reader :ref
6
-
7
- def initialize(ref)
8
- @ref = ref
9
- end
10
-
11
- def to_spec
12
- { '$ref' => @ref }
13
- end
14
- end
15
- end
16
- end
@@ -1,21 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class RequestBody
4
- include Specable
5
- include Hashable
6
-
7
- attr_accessor :description, :content, :required
8
-
9
- def initialize(specification)
10
- @specification = specification
11
- @description = ""
12
- @content = {} # a hash with media type objects
13
- @required = false
14
- end
15
-
16
- def oas_fields
17
- [:description, :content, :required]
18
- end
19
- end
20
- end
21
- end
@@ -1,20 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Response
4
- include Specable
5
- include Hashable
6
-
7
- attr_accessor :code, :description, :content
8
-
9
- def initialize(specification)
10
- @specification = specification
11
- @description = ""
12
- @content = {} # Hash with {content: MediaType}
13
- end
14
-
15
- def oas_fields
16
- [:description, :content]
17
- end
18
- end
19
- end
20
- end
@@ -1,25 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Responses
4
- include Specable
5
- attr_accessor :responses
6
-
7
- def initialize(specification)
8
- @specification = specification
9
- @responses = {}
10
- end
11
-
12
- def add_response(response)
13
- @responses[response.code] = @specification.components.add_response(response)
14
- end
15
-
16
- def to_spec
17
- spec = {}
18
- @responses.each do |key, value|
19
- spec[key] = value.to_spec
20
- end
21
- spec
22
- end
23
- end
24
- end
25
- end
@@ -1,17 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Server
4
- include Specable
5
- attr_accessor :url, :description
6
-
7
- def initialize(url:, description:)
8
- @url = url
9
- @description = description
10
- end
11
-
12
- def oas_fields
13
- [:url, :description]
14
- end
15
- end
16
- end
17
- end
@@ -1,54 +0,0 @@
1
- module OasRails
2
- module Spec
3
- module Specable
4
- def oas_fields
5
- []
6
- end
7
-
8
- def to_spec
9
- hash = {}
10
- oas_fields.each do |var|
11
- key = var.to_s
12
-
13
- camel_case_key = key.camelize(:lower).to_sym
14
- value = send(var)
15
-
16
- processed_value = if value.respond_to?(:to_spec)
17
- value.to_spec
18
- elsif value.is_a?(Array) && value.all? { |elem| elem.respond_to?(:to_spec) }
19
- value.map(&:to_spec)
20
- # elsif value.is_a?(Hash)
21
- # hash = {}
22
- # value.each do |key, object|
23
- # hash[key] = object.to_spec
24
- # end
25
- # hash
26
- else
27
- value
28
- end
29
-
30
- hash[camel_case_key] = processed_value unless valid_processed_value?(processed_value)
31
- end
32
- hash
33
- end
34
-
35
- # rubocop:disable Lint/UnusedMethodArgument
36
- def as_json(options = nil)
37
- to_spec
38
- end
39
- # rubocop:enable Lint/UnusedMethodArgument
40
-
41
- private
42
-
43
- def valid_processed_value?(processed_value)
44
- ((processed_value.is_a?(Hash) || processed_value.is_a?(Array)) && processed_value.empty?) || processed_value.nil?
45
- end
46
-
47
- def snake_to_camel(snake_str)
48
- words = snake_str.to_s.split('_')
49
- words[1..].map!(&:capitalize)
50
- (words[0] + words[1..].join).to_sym
51
- end
52
- end
53
- end
54
- end
@@ -1,50 +0,0 @@
1
- require 'json'
2
-
3
- module OasRails
4
- module Spec
5
- class Specification
6
- include Specable
7
- attr_accessor :components, :info, :openapi, :servers, :tags, :external_docs, :paths
8
-
9
- # Initializes a new Specification object.
10
- # Clears the cache if running in the development environment.
11
- def initialize
12
- clear_cache unless Rails.env.production?
13
-
14
- @components = Components.new(self)
15
- @info = OasRails.config.info
16
- @openapi = '3.1.0'
17
- @servers = OasRails.config.servers
18
- @tags = OasRails.config.tags
19
- @external_docs = {}
20
- @paths = Spec::Paths.new(self)
21
- end
22
-
23
- def build
24
- OasRails.config.route_extractor.host_paths.each do |path|
25
- @paths.add_path(path)
26
- end
27
- end
28
-
29
- # Clears the cache for MethodSource and RouteExtractor.
30
- #
31
- # @return [void]
32
- def clear_cache
33
- MethodSource.clear_cache
34
- OasRails.config.route_extractor.clear_cache
35
- end
36
-
37
- def oas_fields
38
- [:openapi, :info, :servers, :paths, :components, :security, :tags, :external_docs]
39
- end
40
-
41
- # Create the Security Requirement Object.
42
- # @see https://spec.openapis.org/oas/latest.html#security-requirement-object
43
- def security
44
- return [] unless OasRails.config.authenticate_all_routes_by_default
45
-
46
- OasRails.config.security_schemas.map { |key, _| { key => [] } }
47
- end
48
- end
49
- end
50
- end
@@ -1,18 +0,0 @@
1
- module OasRails
2
- module Spec
3
- class Tag
4
- include Specable
5
-
6
- attr_accessor :name, :description
7
-
8
- def initialize(name:, description:)
9
- @name = name.titleize
10
- @description = description
11
- end
12
-
13
- def oas_fields
14
- [:name, :description]
15
- end
16
- end
17
- end
18
- end
@@ -1,12 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class ExampleTag < ::YARD::Tags::Tag
4
- attr_accessor :content
5
-
6
- def initialize(tag_name, text, content: {})
7
- super(tag_name, text, nil, nil)
8
- @content = content
9
- end
10
- end
11
- end
12
- end
@@ -1,169 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class OasRailsFactory < ::YARD::Tags::DefaultFactory
4
- # Parses a tag that represents a request body.
5
- # @param tag_name [String] The name of the tag.
6
- # @param text [String] The tag text to parse.
7
- # @return [RequestBodyTag] The parsed request body tag object.
8
- def parse_tag_with_request_body(tag_name, text)
9
- description, klass, schema, required, content_type = extract_description_and_schema(text.squish)
10
- RequestBodyTag.new(tag_name, description, klass, schema:, required:, content_type:)
11
- end
12
-
13
- # Parses a tag that represents a request body example.
14
- # @param tag_name [String] The name of the tag.
15
- # @param text [String] The tag text to parse.
16
- # @return [RequestBodyExampleTag] The parsed request body example tag object.
17
- def parse_tag_with_request_body_example(tag_name, text)
18
- description, _, hash = extract_description_type_and_content(text.squish, process_content: true, expresion: /^(.*?)\[([^\]]*)\](.*)$/m)
19
- RequestBodyExampleTag.new(tag_name, description, content: hash)
20
- end
21
-
22
- # Parses a tag that represents a parameter.
23
- # @param tag_name [String] The name of the tag.
24
- # @param text [String] The tag text to parse.
25
- # @return [ParameterTag] The parsed parameter tag object.
26
- def parse_tag_with_parameter(tag_name, text)
27
- name, location, schema, required, description = extract_name_location_schema_and_description(text.squish)
28
- name = "#{name}[]" if location == "query" && schema[:type] == "array"
29
- ParameterTag.new(tag_name, name, description, schema, location, required:)
30
- end
31
-
32
- # Parses a tag that represents a response.
33
- # @param tag_name [String] The name of the tag.
34
- # @param text [String] The tag text to parse.
35
- # @return [ResponseTag] The parsed response tag object.
36
- def parse_tag_with_response(tag_name, text)
37
- name, code, schema = extract_name_code_and_schema(text.squish)
38
- ResponseTag.new(tag_name, code, name, schema)
39
- end
40
-
41
- # Parses a tag that represents a response example.
42
- # @param tag_name [String] The name of the tag.
43
- # @param text [String] The tag text to parse.
44
- # @return [ResponseExampleTag] The parsed response example tag object.
45
- def parse_tag_with_response_example(tag_name, text)
46
- description, code, hash = extract_name_code_and_hash(text.squish)
47
- ResponseExampleTag.new(tag_name, description, content: hash, code:)
48
- end
49
-
50
- private
51
-
52
- # Reusable method for extracting description, type, and content with an option to process content.
53
- # @param text [String] The text to parse.
54
- # @param process_content [Boolean] Whether to evaluate the content as a hash.
55
- # @return [Array] An array containing the description, type, and content or remaining text.
56
- def extract_description_type_and_content(text, process_content: false, expresion: /^(.*?)\s*\[(.*)\]\s*(.*)$/)
57
- match = text.match(expresion)
58
- raise ArgumentError, "Invalid tag format: #{text}" if match.nil?
59
-
60
- description = match[1].strip
61
- type = match[2].strip
62
- content = process_content ? eval_content(match[3].strip) : match[3].strip
63
-
64
- [description, type, content]
65
- end
66
-
67
- # Specific method to extract description and schema for request body tags.
68
- # @param text [String] The text to parse.
69
- # @return [Array] An array containing the description, class, schema, required flag and content type.
70
- def extract_description_and_schema(text)
71
- description, type, = extract_description_type_and_content(text)
72
- description, content_type = extract_text_and_parentheses_content(description)
73
- klass, schema, required = type_text_to_schema(type)
74
-
75
- [description, klass, schema, required, content_type]
76
- end
77
-
78
- # Specific method to extract name, location, and schema for parameters.
79
- # @param text [String] The text to parse.
80
- # @return [Array] An array containing the name, location, schema, and required flag.
81
- def extract_name_location_schema_and_description(text)
82
- match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
83
- name, location = extract_text_and_parentheses_content(match[1].strip)
84
- schema, required = type_text_to_schema(match[2].strip)[1..]
85
- description = match[3].strip
86
- [name, location, schema, required, description]
87
- end
88
-
89
- # Specific method to extract name, code, and schema for responses.
90
- # @param text [String] The text to parse.
91
- # @return [Array] An array containing the name, code, and schema.
92
- def extract_name_code_and_schema(text)
93
- name, code = extract_text_and_parentheses_content(text)
94
- _, type, = extract_description_type_and_content(text)
95
- schema = type_text_to_schema(type)[1]
96
- [name, code, schema]
97
- end
98
-
99
- # Specific method to extract name, code, and hash for responses examples.
100
- # @param text [String] The text to parse.
101
- # @return [Array] An array containing the name, code, and schema.
102
- def extract_name_code_and_hash(text)
103
- name, code = extract_text_and_parentheses_content(text)
104
- _, _, content = extract_description_type_and_content(text, expresion: /^(.*?)\[([^\]]*)\](.*)$/m)
105
- hash = eval_content(content)
106
- [name, code, hash]
107
- end
108
-
109
- # Evaluates a string as a hash, handling errors gracefully.
110
- # @param content [String] The content string to evaluate.
111
- # @return [Hash] The evaluated hash, or an empty hash if an error occurs.
112
- # rubocop:disable Security/Eval
113
- def eval_content(content)
114
- eval(content)
115
- rescue StandardError
116
- {}
117
- end
118
- # rubocop:enable Security/Eval
119
-
120
- # Parses the position name and location from input text.
121
- # @param input [String] The input text to parse.
122
- # @return [Array] An array containing the name and location.
123
- def extract_text_and_parentheses_content(input)
124
- return input unless input =~ /^(.+?)\(([^)]+)\)/
125
-
126
- text = ::Regexp.last_match(1).strip
127
- parenthesis_content = ::Regexp.last_match(2).strip
128
- [text, parenthesis_content]
129
- end
130
-
131
- # Extracts the text and whether it's required.
132
- # @param text [String] The text to parse.
133
- # @return [Array] An array containing the text and a required flag.
134
- def text_and_required(text)
135
- if text.start_with?('!')
136
- [text.sub(/^!/, ''), true]
137
- else
138
- [text, false]
139
- end
140
- end
141
-
142
- # Matches and validates a description and type from text.
143
- # @param text [String] The text to parse.
144
- # @return [MatchData] The match data from the regex.
145
- def description_and_type(text)
146
- match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
147
- raise ArgumentError, "The request body tag is not valid: #{text}" if match.nil?
148
-
149
- match
150
- end
151
-
152
- # Converts type text to a schema, checking if it's an ActiveRecord class.
153
- # @param text [String] The type text to convert.
154
- # @return [Array] An array containing the class, schema, and required flag.
155
- def type_text_to_schema(text)
156
- type_text, required = text_and_required(text)
157
-
158
- if Utils.active_record_class?(type_text)
159
- klass = type_text.constantize
160
- schema = Builders::EsquemaBuilder.build_outgoing_schema(klass:)
161
- else
162
- schema = JsonSchemaGenerator.process_string(type_text)[:json_schema]
163
- klass = Object
164
- end
165
- [klass, schema, required]
166
- end
167
- end
168
- end
169
- end
@@ -1,14 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class ParameterTag < ::YARD::Tags::Tag
4
- attr_accessor :schema, :required, :location
5
-
6
- def initialize(tag_name, name, text, schema, location, required: false)
7
- super(tag_name, text, nil, name)
8
- @schema = schema
9
- @required = required
10
- @location = location
11
- end
12
- end
13
- end
14
- end
@@ -1,11 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class RequestBodyExampleTag < ExampleTag
4
- attr_accessor :content
5
-
6
- def initialize(tag_name, text, content: {})
7
- super
8
- end
9
- end
10
- end
11
- end
@@ -1,16 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class RequestBodyTag < ::YARD::Tags::Tag
4
- attr_accessor :klass, :schema, :required, :content_type
5
-
6
- def initialize(tag_name, text, klass, schema: {}, required: false, content_type: 'application/json')
7
- # initialize(tag_name, text, types = nil, name = nil)
8
- super(tag_name, text, nil, nil)
9
- @klass = klass
10
- @schema = schema
11
- @required = required
12
- @content_type = content_type
13
- end
14
- end
15
- end
16
- end
@@ -1,12 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class ResponseExampleTag < ExampleTag
4
- attr_accessor :code
5
-
6
- def initialize(tag_name, text, content: {}, code: 200)
7
- super(tag_name, text, content:)
8
- @code = code
9
- end
10
- end
11
- end
12
- end
@@ -1,13 +0,0 @@
1
- module OasRails
2
- module YARD
3
- class ResponseTag < ::YARD::Tags::Tag
4
- attr_accessor :schema
5
-
6
- # TODO: name == code. The name MUST be changed to code for better understanding
7
- def initialize(tag_name, name, text, schema)
8
- super(tag_name, text, nil, name)
9
- @schema = schema
10
- end
11
- end
12
- end
13
- end