oas_core 0.5.3 → 1.0.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: 8396df67f2123727d598bd1860f44b8595776b8564ad39b8c788bcfad839a522
4
- data.tar.gz: 4e6525b5259d2b105edfaa094e04e4ee778453d5500208f692849c165a340596
3
+ metadata.gz: ffaaef1c0e6abfd2a9833a5c8a452a0ce2c45053cf118b750a1b8c82ca13ac71
4
+ data.tar.gz: 4af71f8ab33bd1953a8fa737fe8a83646b5d51641b681ad56f6f5ca4ed06a2c2
5
5
  SHA512:
6
- metadata.gz: fab7a5e69cc18c3745d9780ccfee5e9ab0ada24515efde300c7dbdce04f19f68331d394506da5fb71f1b6299ed74f6ce19913c513440c0ab5c1909cd45f20f8b
7
- data.tar.gz: 2e7ccf626a4c5aaeba13f9930d0853605bca4060c69977a50488df36c92dbd79059751c1fc63682cbeadbc7fc819f3537ab3442462b385dc4ecbfdc70657be08
6
+ metadata.gz: de76cece6e0557f2b822ca6922dd31d5b1472ed31ee94d20a5f2d6ea25d582599982e344033adcbf862df45b13570ed05b2b3828378326261426d7465ee47f0f
7
+ data.tar.gz: 717b2203b91a6b4e7976418bea40f47ff4b002f7cdc23239c408abbf273e9dcdd9a1293d727dea26c8da8db7785f0ca22957d49c0eae1704acb218c30a087c44
data/README.md CHANGED
@@ -10,7 +10,11 @@ OasCore is a Ruby gem designed to generate Open API Specification (OAS) 3.1 docu
10
10
 
11
11
  ## Framework adapters
12
12
 
13
- - **[OasRails](https://github.com/a-chacon/oas_rails)**
13
+ - **[OasRails](https://github.com/a-chacon/oas_rails)**: For Ruby on Rails framework.
14
+
15
+ - **[OasHanami](https://github.com/a-chacon/oas_hanami)**: For [Hanami](https://github.com/hanami/hanami) framework.
16
+
17
+ - **[OasRage](https://github.com/a-chacon/oas_rage)**: For [Rage](https://github.com/rage-rb/rage) framework.
14
18
 
15
19
  ## Documentation
16
20
 
@@ -3,15 +3,18 @@
3
3
  module OasCore
4
4
  module Builders
5
5
  class ContentBuilder
6
- def initialize(specification, context)
7
- @context = context || :incoming
6
+ def initialize(specification)
8
7
  @specification = specification
9
8
  @media_type = Spec::MediaType.new(specification)
10
9
  @content_type = 'application/json'
11
10
  end
12
11
 
13
12
  def with_schema(schema)
14
- @media_type.schema = @specification.components.add_schema(schema)
13
+ @media_type.schema = if schema.is_a? OasCore::Spec::Reference
14
+ schema
15
+ else
16
+ @specification.components.add_schema(schema)
17
+ end
15
18
 
16
19
  self
17
20
  end
@@ -31,11 +34,15 @@ module OasCore
31
34
  def with_examples_from_tags(tags)
32
35
  @media_type.examples = @media_type.examples.merge(tags.each_with_object({}).with_index(1) do |(example, result), _index|
33
36
  key = example.text.downcase.gsub(' ', '_')
34
- value = {
35
- 'summary' => example.text,
36
- 'value' => example.content
37
- }
38
- result[key] = @specification.components.add_example(value)
37
+ if example.content.is_a? OasCore::Spec::Reference
38
+ result[key] = example.content
39
+ else
40
+ value = {
41
+ 'summary' => example.text,
42
+ 'value' => example.content
43
+ }
44
+ result[key] = @specification.components.add_example(value)
45
+ end
39
46
  end)
40
47
 
41
48
  self
@@ -15,7 +15,7 @@ module OasCore
15
15
  @operation.tags = extract_tags(oas_route:)
16
16
  @operation.security = extract_security(oas_route:)
17
17
  @operation.parameters = ParametersBuilder.new(@specification).from_oas_route(oas_route).build
18
- @operation.request_body = RequestBodyBuilder.new(@specification).from_oas_route(oas_route).reference
18
+ @operation.request_body = extract_request_body(oas_route)
19
19
  @operation.responses = ResponsesBuilder.new(@specification)
20
20
  .from_oas_route(oas_route)
21
21
  .add_default_responses(oas_route, !@operation.security.empty?).build
@@ -82,6 +82,14 @@ module OasCore
82
82
  destroy: 'Delete'
83
83
  }.fetch(method.to_sym)
84
84
  end
85
+
86
+ def extract_request_body(oas_route)
87
+ if (ref_tag = oas_route.tags(:request_body_ref).first)
88
+ ref_tag.reference
89
+ else
90
+ RequestBodyBuilder.new(@specification).from_oas_route(oas_route).reference
91
+ end
92
+ end
85
93
  end
86
94
  end
87
95
  end
@@ -15,6 +15,10 @@ module OasCore
15
15
  @parameters << ParameterBuilder.new(@specification).from_path(oas_route.path, p).build unless @parameters.any? { |param| param.name.to_s == p.to_s }
16
16
  end
17
17
 
18
+ oas_route.tags(:parameter_ref)&.each do |ref_tag|
19
+ @parameters << ref_tag.reference
20
+ end
21
+
18
22
  self
19
23
  end
20
24
 
@@ -34,7 +38,11 @@ module OasCore
34
38
 
35
39
  def build
36
40
  @parameters.map do |p|
37
- @specification.components.add_parameter(p)
41
+ if p.is_a? OasCore::Spec::Reference
42
+ p
43
+ else
44
+ @specification.components.add_parameter(p)
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -19,7 +19,7 @@ module OasCore
19
19
 
20
20
  def from_tags(tag:, examples_tags: [])
21
21
  @request_body.description = tag.text
22
- @request_body.content = ContentBuilder.new(@specification, :incoming).with_schema(tag.schema).with_examples_from_tags(examples_tags).with_content_type(tag.content_type).build
22
+ @request_body.content = ContentBuilder.new(@specification).with_schema(tag.content).with_examples_from_tags(examples_tags).with_content_type(tag.content_type).build
23
23
  @request_body.required = tag.required
24
24
 
25
25
  self
@@ -27,9 +27,9 @@ module OasCore
27
27
  end
28
28
 
29
29
  def from_tag(tag)
30
- @response.code = tag.name.to_i
30
+ @response.code = tag.code.to_i
31
31
  @response.description = tag.text
32
- @response.content = ContentBuilder.new(@specification, :outgoing).with_schema(tag.schema).build
32
+ @response.content = ContentBuilder.new(@specification).with_schema(tag.content).build
33
33
 
34
34
  self
35
35
  end
@@ -15,15 +15,18 @@ module OasCore
15
15
 
16
16
  def from_oas_route(oas_route)
17
17
  oas_route.tags(:response).each do |tag|
18
- content = @content_builder.new(@specification,
19
- :outgoing).with_schema(tag.schema).with_examples_from_tags(oas_route.tags(:response_example).filter do |re|
20
- re.code == tag.name
21
- end).build
22
- response = @response_builder.new(@specification).with_code(tag.name.to_i).with_description(tag.text).with_content(content).build
18
+ content = @content_builder.new(@specification).with_schema(tag.content).with_examples_from_tags(oas_route.tags(:response_example).filter do |re|
19
+ re.code == tag.code
20
+ end).build
21
+ response = @response_builder.new(@specification).with_code(tag.code.to_i).with_description(tag.text).with_content(content).build
23
22
 
24
23
  @responses.add_response(response)
25
24
  end
26
25
 
26
+ oas_route.tags(:response_ref)&.each do |ref_tag|
27
+ @responses.add_reference_response(ref_tag.code, ref_tag.reference)
28
+ end
29
+
27
30
  self
28
31
  end
29
32
 
@@ -63,8 +66,7 @@ module OasCore
63
66
  def add_responses_for_errors(errors)
64
67
  errors.each do |error|
65
68
  response_body = resolve_response_body(error)
66
- content = @content_builder.new(@specification,
67
- :outgoing).with_schema(@json_schema_generator.process_string(response_body)[:json_schema]).build
69
+ content = @content_builder.new(@specification).with_schema(@json_schema_generator.process_string(response_body)[:json_schema]).build
68
70
  code = @utils.status_to_integer(error)
69
71
  response = @response_builder.new(@specification).with_code(code).with_description(@utils.get_definition(code)).with_content(content).build
70
72
 
@@ -9,8 +9,7 @@ module OasCore
9
9
  :authenticate_all_routes_by_default,
10
10
  :set_default_responses,
11
11
  :possible_default_responses,
12
- :http_verbs,
13
- :use_model_names
12
+ :http_verbs
14
13
 
15
14
  attr_reader :servers, :tags, :security_schema, :response_body_of_default
16
15
 
@@ -29,8 +28,6 @@ module OasCore
29
28
  unprocessable_entity]
30
29
  @http_verbs = %i[get post put patch delete]
31
30
  @response_body_of_default = 'Hash{ status: !Integer, error: String }'
32
- # TODO: What does this config??
33
- @use_model_names = false
34
31
 
35
32
  @possible_default_responses.each do |response|
36
33
  method_name = "response_body_of_#{response}="
@@ -52,6 +52,7 @@ module OasCore
52
52
  end
53
53
  end
54
54
 
55
+ # rubocop:disable Metrics
55
56
  # Parses the properties of an object type from a string.
56
57
  #
57
58
  # @param str [String] The string representation of the object's properties.
@@ -97,6 +98,7 @@ module OasCore
97
98
 
98
99
  properties
99
100
  end
101
+ # rubocop:enable Metrics
100
102
 
101
103
  # Converts a parsed data type into a JSON schema format.
102
104
  #
@@ -128,11 +130,11 @@ module OasCore
128
130
  end
129
131
  end
130
132
 
133
+ # rubocop:disable Metrics
131
134
  # Converts a Ruby data type into its corresponding JSON schema type.
132
135
  #
133
136
  # @param type [Symbol, String] The Ruby data type.
134
137
  # @return [Hash, String] The JSON schema type or a hash with additional format information.
135
- # rubocop:disable Metrics/CyclomaticComplexity
136
138
  def self.ruby_type_to_json_schema_type(type)
137
139
  case type.to_s.downcase
138
140
  when 'string' then { type: 'string' }
@@ -148,6 +150,6 @@ module OasCore
148
150
  else type.to_s.downcase
149
151
  end
150
152
  end
151
- # rubocop:enable Metrics/CyclomaticComplexity
153
+ # rubocop:enable Metrics
152
154
  end
153
155
  end
@@ -21,8 +21,18 @@ module OasCore
21
21
  @callbacks = {}
22
22
  end
23
23
 
24
- def oas_fields
25
- %i[request_bodies examples responses schemas parameters security_schemes]
24
+ def to_spec
25
+ {
26
+ schemas: @schemas,
27
+ responses: @responses.transform_values(&:to_spec),
28
+ parameters: @parameters.transform_values(&:to_spec),
29
+ requestBodies: @request_bodies.transform_values(&:to_spec),
30
+ securitySchemes: @security_schemes,
31
+ headers: @headers.transform_values(&:to_spec),
32
+ examples: @examples,
33
+ links: @links.transform_values(&:to_spec),
34
+ callbacks: @callbacks.transform_values(&:to_spec)
35
+ }.compact
26
36
  end
27
37
 
28
38
  def add_response(response)
@@ -47,20 +57,9 @@ module OasCore
47
57
  end
48
58
 
49
59
  def add_schema(schema)
50
- key = nil
51
- if OasCore.config.use_model_names
52
- if schema[:type] == 'array'
53
- arr_schema = schema[:items]
54
- arr_key = arr_schema['title']
55
- key = "#{arr_key}List" unless arr_key.nil?
56
- else
57
- key = schema['title']
58
- end
59
- end
60
-
61
- key = Hashable.generate_hash(schema) if key.nil?
62
-
60
+ key = Hashable.generate_hash(schema)
63
61
  @schemas[key] = schema if @schemas[key].nil?
62
+
64
63
  schema_reference(key)
65
64
  end
66
65
 
@@ -10,7 +10,6 @@ module OasCore
10
10
  # Initializes a new MediaType object.
11
11
  #
12
12
  # @param schema [Hash] the schema of the media type.
13
- # @param kwargs [Hash] additional keyword arguments.
14
13
  def initialize(specification)
15
14
  @specification = specification
16
15
  @schema = {}
@@ -12,6 +12,7 @@ module OasCore
12
12
  @specification = specification
13
13
  @description = ''
14
14
  @content = {} # Hash with {content: MediaType}
15
+ @code = 200
15
16
  end
16
17
 
17
18
  def oas_fields
@@ -15,12 +15,12 @@ module OasCore
15
15
  @responses[response.code] = @specification.components.add_response(response)
16
16
  end
17
17
 
18
+ def add_reference_response(code, reference)
19
+ @responses[code] = reference
20
+ end
21
+
18
22
  def to_spec
19
- spec = {}
20
- @responses.each do |key, value|
21
- spec[key] = value.to_spec
22
- end
23
- spec
23
+ @responses.transform_values(&:to_spec)
24
24
  end
25
25
  end
26
26
  end
@@ -8,30 +8,12 @@ module OasCore
8
8
  end
9
9
 
10
10
  def to_spec
11
- hash = {}
12
- oas_fields.each do |var|
13
- key = var.to_s
14
-
15
- camel_case_key = key.camelize(:lower).to_sym
11
+ oas_fields.each_with_object({}) do |var, hash|
12
+ key = var.to_s.camelize(:lower).to_sym
16
13
  value = send(var)
17
-
18
- processed_value = if value.respond_to?(:to_spec)
19
- value.to_spec
20
- elsif value.is_a?(Array) && value.all? { |elem| elem.respond_to?(:to_spec) }
21
- value.map(&:to_spec)
22
- # elsif value.is_a?(Hash)
23
- # hash = {}
24
- # value.each do |key, object|
25
- # hash[key] = object.to_spec
26
- # end
27
- # hash
28
- else
29
- value
30
- end
31
-
32
- hash[camel_case_key] = processed_value unless valid_processed_value?(processed_value)
14
+ processed_value = process_value(value)
15
+ hash[key] = processed_value unless valid_processed_value?(processed_value)
33
16
  end
34
- hash
35
17
  end
36
18
 
37
19
  # rubocop:disable Lint/UnusedMethodArgument
@@ -42,6 +24,26 @@ module OasCore
42
24
 
43
25
  private
44
26
 
27
+ def process_value(value)
28
+ if value.respond_to?(:to_spec)
29
+ value.to_spec
30
+ elsif value.is_a?(Array)
31
+ process_array(value)
32
+ elsif value.is_a?(Hash)
33
+ process_hash(value)
34
+ else
35
+ value
36
+ end
37
+ end
38
+
39
+ def process_array(array)
40
+ array.all? { |elem| elem.respond_to?(:to_spec) } ? array.map(&:to_spec) : array
41
+ end
42
+
43
+ def process_hash(hash)
44
+ hash.transform_values { |val| val.respond_to?(:to_spec) ? val.to_spec : val }
45
+ end
46
+
45
47
  def valid_processed_value?(processed_value)
46
48
  ((processed_value.is_a?(Hash) || processed_value.is_a?(Array)) && processed_value.empty?) || processed_value.nil?
47
49
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OasCore
4
- VERSION = '0.5.3'
4
+ VERSION = '1.0.1'
5
5
  end
@@ -2,14 +2,22 @@
2
2
 
3
3
  module OasCore
4
4
  module YARD
5
+ class TagParsingError < StandardError; end
6
+
5
7
  class OasCoreFactory < ::YARD::Tags::DefaultFactory
6
8
  # Parses a tag that represents a request body.
7
9
  # @param tag_name [String] The name of the tag.
8
10
  # @param text [String] The tag text to parse.
9
11
  # @return [RequestBodyTag] The parsed request body tag object.
10
12
  def parse_tag_with_request_body(tag_name, text)
11
- description, klass, schema, required, content_type = extract_description_and_schema(text.squish)
12
- RequestBodyTag.new(tag_name, description, klass, schema:, required:, content_type:)
13
+ description, raw_type = split_description_and_type(text.squish)
14
+ description, content_type = text_and_last_parenthesis_content(description)
15
+ raw_type, required = text_and_required(raw_type)
16
+ content = raw_type_to_content(raw_type)
17
+
18
+ RequestBodyTag.new(tag_name, description, content:, required:, content_type:)
19
+ rescue StandardError => e
20
+ raise TagParsingError, "Failed to parse request body tag: #{e.message}"
13
21
  end
14
22
 
15
23
  # Parses a tag that represents a request body example.
@@ -17,9 +25,13 @@ module OasCore
17
25
  # @param text [String] The tag text to parse.
18
26
  # @return [RequestBodyExampleTag] The parsed request body example tag object.
19
27
  def parse_tag_with_request_body_example(tag_name, text)
20
- description, _, hash = extract_description_type_and_content(text.squish, process_content: true,
21
- expresion: /^(.*?)\[([^\]]*)\](.*)$/m)
22
- RequestBodyExampleTag.new(tag_name, description, content: hash)
28
+ description, raw_type = split_description_and_type(text.squish)
29
+ raw_type, = text_and_required(raw_type)
30
+ content = raw_type_to_content(raw_type)
31
+
32
+ RequestBodyExampleTag.new(tag_name, description, content: content)
33
+ rescue StandardError => e
34
+ raise TagParsingError, "Failed to parse request body example tag: #{e.message}"
23
35
  end
24
36
 
25
37
  # Parses a tag that represents a parameter.
@@ -27,9 +39,20 @@ module OasCore
27
39
  # @param text [String] The tag text to parse.
28
40
  # @return [ParameterTag] The parsed parameter tag object.
29
41
  def parse_tag_with_parameter(tag_name, text)
30
- name, location, schema, required, description = extract_name_location_schema_and_description(text.squish)
42
+ match = text.squish.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
43
+ if match
44
+ first = match[1]
45
+ second = match[2]
46
+ description = match[3]
47
+ end
48
+ name, location = text_and_last_parenthesis_content(first)
49
+ raw_type, required = text_and_required(second)
50
+ schema = raw_type_to_content(raw_type)
31
51
  name = "#{name}[]" if location == 'query' && schema[:type] == 'array'
32
- ParameterTag.new(tag_name, name, description, schema, location, required:)
52
+
53
+ ParameterTag.new(tag_name, name, description.strip, schema, location, required:)
54
+ rescue StandardError => e
55
+ raise TagParsingError, "Failed to parse parameter tag: #{e.message}"
33
56
  end
34
57
 
35
58
  # Parses a tag that represents a response.
@@ -37,8 +60,13 @@ module OasCore
37
60
  # @param text [String] The tag text to parse.
38
61
  # @return [ResponseTag] The parsed response tag object.
39
62
  def parse_tag_with_response(tag_name, text)
40
- name, code, schema = extract_name_code_and_schema(text.squish)
41
- ResponseTag.new(tag_name, code, name, schema)
63
+ description, raw_type = split_description_and_type(text.squish)
64
+ description, code = text_and_last_parenthesis_content(description)
65
+ content = raw_type_to_content(raw_type)
66
+
67
+ ResponseTag.new(tag_name, description, code, content)
68
+ rescue StandardError => e
69
+ raise TagParsingError, "Failed to parse response tag: #{e.message}"
42
70
  end
43
71
 
44
72
  # Parses a tag that represents a response example.
@@ -46,79 +74,67 @@ module OasCore
46
74
  # @param text [String] The tag text to parse.
47
75
  # @return [ResponseExampleTag] The parsed response example tag object.
48
76
  def parse_tag_with_response_example(tag_name, text)
49
- description, code, hash = extract_name_code_and_hash(text.squish)
50
- ResponseExampleTag.new(tag_name, description, content: hash, code:)
51
- end
52
-
53
- private
77
+ description, raw_type = split_description_and_type(text.squish)
78
+ description, code = text_and_last_parenthesis_content(description)
79
+ content = raw_type_to_content(raw_type)
54
80
 
55
- # Reusable method for extracting description, type, and content with an option to process content.
56
- # @param text [String] The text to parse.
57
- # @param process_content [Boolean] Whether to evaluate the content as a hash.
58
- # @return [Array] An array containing the description, type, and content or remaining text.
59
- def extract_description_type_and_content(text, process_content: false, expresion: /^(.*?)\s*\[(.*)\]\s*(.*)$/)
60
- match = text.match(expresion)
61
- raise ArgumentError, "Invalid tag format: #{text}" if match.nil?
62
-
63
- description = match[1].strip
64
- type = match[2].strip
65
- content = process_content ? eval_content(match[3].strip) : match[3].strip
66
-
67
- [description, type, content]
81
+ ResponseExampleTag.new(tag_name, description, content: content, code:)
82
+ rescue StandardError => e
83
+ raise TagParsingError, "Failed to parse response example tag: #{e.message}"
68
84
  end
69
85
 
70
- # Specific method to extract description and schema for request body tags.
71
- # @param text [String] The text to parse.
72
- # @return [Array] An array containing the description, class, schema, required flag and content type.
73
- def extract_description_and_schema(text)
74
- description, type, = extract_description_type_and_content(text)
75
- description, content_type = extract_text_and_parentheses_content(description)
76
-
77
- klass, schema, required = type_text_to_schema(type)
78
- [description, klass, schema, required, content_type]
86
+ # Parses a tag that represents a parameter reference.
87
+ # @param tag_name [String] The name of the tag.
88
+ # @param text [String] The tag text to parse.
89
+ # @return [ParameterReferenceTag] The parsed parameter reference tag object.
90
+ def parse_tag_with_parameter_reference(tag_name, text)
91
+ ref = text.strip
92
+ reference = OasCore::Spec::Reference.new(ref)
93
+ ParameterReferenceTag.new(tag_name, reference)
94
+ rescue StandardError => e
95
+ raise TagParsingError, "Failed to parse parameter reference tag: #{e.message}"
79
96
  end
80
97
 
81
- # Specific method to extract name, location, and schema for parameters.
82
- # @param text [String] The text to parse.
83
- # @return [Array] An array containing the name, location, schema, and required flag.
84
- def extract_name_location_schema_and_description(text)
85
- match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
86
- name, location = extract_text_and_parentheses_content(match[1].strip)
87
- schema, required = type_text_to_schema(match[2].strip)[1..]
88
- description = match[3].strip
89
- [name, location, schema, required, description]
98
+ # Parses a tag that represents a request body reference.
99
+ # @param tag_name [String] The name of the tag.
100
+ # @param text [String] The tag text to parse.
101
+ # @return [RequestBodyReferenceTag] The parsed request body reference tag object.
102
+ def parse_tag_with_request_body_reference(tag_name, text)
103
+ ref = text.strip
104
+ reference = OasCore::Spec::Reference.new(ref)
105
+ RequestBodyReferenceTag.new(tag_name, reference)
106
+ rescue StandardError => e
107
+ raise TagParsingError, "Failed to parse request body reference tag: #{e.message}"
90
108
  end
91
109
 
92
- # Specific method to extract name, code, and schema for responses.
93
- # @param text [String] The text to parse.
94
- # @return [Array] An array containing the name, code, and schema.
95
- def extract_name_code_and_schema(text)
96
- name, code = extract_text_and_parentheses_content(text)
97
- _, type, = extract_description_type_and_content(text)
98
- schema = type_text_to_schema(type)[1]
99
- [name, code, schema]
110
+ # Parses a tag that represents a response reference.
111
+ # @param tag_name [String] The name of the tag.
112
+ # @param text [String] The tag text to parse.
113
+ # @return [ResponseReferenceTag] The parsed response reference tag object.
114
+ def parse_tag_with_response_reference(tag_name, text)
115
+ reference_str, code_text = text_and_first_parenthesis_content(text.strip)
116
+ reference = OasCore::Spec::Reference.new(reference_str)
117
+ ResponseReferenceTag.new(tag_name, reference, code: code_text.to_i)
118
+ rescue StandardError => e
119
+ raise TagParsingError, "Failed to parse response reference tag: #{e.message}"
100
120
  end
101
121
 
102
- # Specific method to extract name, code, and hash for responses examples.
103
- # @param text [String] The text to parse.
104
- # @return [Array] An array containing the name, code, and schema.
105
- def extract_name_code_and_hash(text)
106
- name, code = extract_text_and_parentheses_content(text)
107
- _, _, content = extract_description_type_and_content(text, expresion: /^(.*?)\[([^\]]*)\](.*)$/m)
108
- hash = eval_content(content)
109
- [name, code, hash]
110
- end
122
+ private
111
123
 
112
- # Evaluates a string as a hash, handling errors gracefully.
113
- # @param content [String] The content string to evaluate.
114
- # @return [Hash] The evaluated hash, or an empty hash if an error occurs.
115
- # rubocop:disable Security/Eval
116
- def eval_content(content)
117
- eval(content)
118
- rescue StandardError
119
- {}
124
+ # Converts raw_type to content based on its format.
125
+ # @param raw_type [String] The raw type string to process.
126
+ # @return [Hash] The processed content.
127
+ def raw_type_to_content(raw_type)
128
+ if raw_type.start_with?('JSON')
129
+ json_string = raw_type.sub(/^JSON/, '').gsub(/'/, '"')
130
+ JSON.parse(json_string)
131
+ elsif raw_type.start_with?('Reference:')
132
+ ref = raw_type.sub(/^Reference:/, '').strip
133
+ OasCore::Spec::Reference.new(ref)
134
+ else
135
+ JsonSchemaGenerator.process_string(raw_type)[:json_schema]
136
+ end
120
137
  end
121
- # rubocop:enable Security/Eval
122
138
 
123
139
  # Parses the position name and location from input text.
124
140
  # @param input [String] The input text to parse.
@@ -142,26 +158,48 @@ module OasCore
142
158
  end
143
159
  end
144
160
 
145
- # Matches and validates a description and type from text.
161
+ # Splits the text into description with detail and type parts.
146
162
  # @param text [String] The text to parse.
147
- # @return [MatchData] The match data from the regex.
148
- def description_and_type(text)
149
- match = text.match(/^(.*?)\s*\[(.*?)\]\s*(.*)$/)
150
- raise ArgumentError, "The request body tag is not valid: #{text}" if match.nil?
163
+ # @return [Array] An array containing the description with detail and the type.
164
+ def split_description_and_type(text)
165
+ match = text.match(/^(.*?)\s*\[(.*?)\]$/)
166
+ raise TagParsingError, "Invalid format: #{text}" if match.nil?
151
167
 
152
- match
168
+ [match[1].strip, match[2].strip]
153
169
  end
154
170
 
155
- # Converts type text to a schema, checking if it's an ActiveRecord class.
156
- # @param text [String] The type text to convert.
157
- # @return [Array] An array containing the class, schema, and required flag.
158
- def type_text_to_schema(text)
159
- type_text, required = text_and_required(text)
160
-
161
- schema = JsonSchemaGenerator.process_string(type_text)[:json_schema]
162
- klass = Object
171
+ # Extracts the description and the detail (content of the last parentheses) from the text.
172
+ # @param text [String] The text to parse.
173
+ # @return [Array] An array containing the description and the detail.
174
+ def text_and_last_parenthesis_content(text)
175
+ # Find the last occurrence of parentheses
176
+ last_open = text.rindex('(')
177
+ last_close = text.rindex(')')
178
+
179
+ if last_open && last_close && last_open < last_close
180
+ description = text[0...last_open].strip
181
+ detail = text[(last_open + 1)...last_close].strip
182
+ [description, detail]
183
+ else
184
+ [text.strip, nil]
185
+ end
186
+ end
163
187
 
164
- [klass, schema, required]
188
+ # Extracts the first occurrence of content inside parentheses and the rest of the text.
189
+ # @param text [String] The text to parse.
190
+ # @return [Array] An array containing the rest of the text and the first detail in parentheses.
191
+ def text_and_first_parenthesis_content(text)
192
+ # Find the first occurrence of parentheses
193
+ first_open = text.index('(')
194
+ first_close = text.index(')')
195
+
196
+ if first_open && first_close && first_open < first_close
197
+ detail = text[(first_open + 1)...first_close].strip
198
+ rest = (text[0...first_open] + text[(first_close + 1)..]).strip
199
+ [rest, detail]
200
+ else
201
+ [text.strip, nil]
202
+ end
165
203
  end
166
204
  end
167
205
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module YARD
5
+ class ParameterReferenceTag < ReferenceTag
6
+ end
7
+ end
8
+ end
@@ -5,11 +5,11 @@ module OasCore
5
5
  class ParameterTag < ::YARD::Tags::Tag
6
6
  attr_accessor :schema, :required, :location
7
7
 
8
- def initialize(tag_name, name, text, schema, location, required: false)
8
+ def initialize(tag_name, name, text, *args, **kwargs)
9
9
  super(tag_name, text, nil, name)
10
- @schema = schema
11
- @required = required
12
- @location = location
10
+ @schema = args[0] || kwargs.fetch(:schema, nil)
11
+ @location = args[1] || kwargs.fetch(:location, nil)
12
+ @required = kwargs.fetch(:required, false)
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module YARD
5
+ class ReferenceTag < ::YARD::Tags::Tag
6
+ attr_accessor :reference
7
+
8
+ def initialize(tag_name, reference)
9
+ super(tag_name, nil, nil, nil)
10
+ @reference = reference
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,11 +3,6 @@
3
3
  module OasCore
4
4
  module YARD
5
5
  class RequestBodyExampleTag < ExampleTag
6
- attr_accessor :content
7
-
8
- def initialize(tag_name, text, content: {})
9
- super
10
- end
11
6
  end
12
7
  end
13
8
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module YARD
5
+ class RequestBodyReferenceTag < ReferenceTag
6
+ end
7
+ end
8
+ end
@@ -3,13 +3,12 @@
3
3
  module OasCore
4
4
  module YARD
5
5
  class RequestBodyTag < ::YARD::Tags::Tag
6
- attr_accessor :klass, :schema, :required, :content_type
6
+ attr_accessor :content, :required, :content_type
7
7
 
8
- def initialize(tag_name, text, klass, schema: {}, required: false, content_type: 'application/json')
8
+ def initialize(tag_name, text, content: nil, required: false, content_type: 'application/json')
9
9
  # initialize(tag_name, text, types = nil, name = nil)
10
10
  super(tag_name, text, nil, nil)
11
- @klass = klass
12
- @schema = schema
11
+ @content = content
13
12
  @required = required
14
13
  @content_type = content_type
15
14
  end
@@ -5,7 +5,7 @@ module OasCore
5
5
  class ResponseExampleTag < ExampleTag
6
6
  attr_accessor :code
7
7
 
8
- def initialize(tag_name, text, content: {}, code: 200)
8
+ def initialize(tag_name, text, content: nil, code: 200)
9
9
  super(tag_name, text, content:)
10
10
  @code = code
11
11
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OasCore
4
+ module YARD
5
+ class ResponseReferenceTag < ReferenceTag
6
+ attr_accessor :code
7
+
8
+ def initialize(tag_name, reference, code: 200)
9
+ super(tag_name, reference)
10
+ @code = code
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,12 +3,13 @@
3
3
  module OasCore
4
4
  module YARD
5
5
  class ResponseTag < ::YARD::Tags::Tag
6
- attr_accessor :schema
6
+ attr_accessor :content, :code
7
7
 
8
8
  # TODO: name == code. The name MUST be changed to code for better understanding
9
- def initialize(tag_name, name, text, schema)
10
- super(tag_name, text, nil, name)
11
- @schema = schema
9
+ def initialize(tag_name, description, code, content)
10
+ super(tag_name, description, nil, nil)
11
+ @code = code
12
+ @content = content
12
13
  end
13
14
  end
14
15
  end
data/lib/oas_core.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'yard'
4
4
  require 'method_source'
5
5
  require 'active_support/all'
6
+ require 'deep_merge/rails_compat'
6
7
 
7
8
  module OasCore
8
9
  require 'oas_core/version'
@@ -49,11 +50,16 @@ module OasCore
49
50
  module YARD
50
51
  autoload :RequestBodyTag, 'oas_core/yard/request_body_tag'
51
52
  autoload :ExampleTag, 'oas_core/yard/example_tag'
53
+ autoload :ReferenceTag, 'oas_core/yard/reference_tag'
52
54
  autoload :RequestBodyExampleTag, 'oas_core/yard/request_body_example_tag'
53
55
  autoload :ParameterTag, 'oas_core/yard/parameter_tag'
54
56
  autoload :ResponseTag, 'oas_core/yard/response_tag'
55
57
  autoload :ResponseExampleTag, 'oas_core/yard/response_example_tag'
56
58
  autoload :OasCoreFactory, 'oas_core/yard/oas_core_factory'
59
+
60
+ autoload :ParameterReferenceTag, 'oas_core/yard/parameter_reference_tag'
61
+ autoload :RequestBodyReferenceTag, 'oas_core/yard/request_body_reference_tag'
62
+ autoload :ResponseReferenceTag, 'oas_core/yard/response_reference_tag'
57
63
  end
58
64
 
59
65
  class << self
@@ -71,9 +77,12 @@ module OasCore
71
77
  ::YARD::Tags::Library.default_factory = YARD::OasCoreFactory
72
78
  yard_tags = {
73
79
  'Request body' => %i[request_body with_request_body],
80
+ 'Request body Reference' => %i[request_body_ref with_request_body_reference],
74
81
  'Request body Example' => %i[request_body_example with_request_body_example],
75
82
  'Parameter' => %i[parameter with_parameter],
83
+ 'Parameter Reference' => %i[parameter_ref with_parameter_reference],
76
84
  'Response' => %i[response with_response],
85
+ 'Response Reference' => %i[response_ref with_response_reference],
77
86
  'Response Example' => %i[response_example with_response_example],
78
87
  'Endpoint Tags' => [:tags],
79
88
  'Summary' => [:summary],
@@ -85,5 +94,11 @@ module OasCore
85
94
  ::YARD::Tags::Library.define_tag(tag_name, method_name, handler)
86
95
  end
87
96
  end
97
+
98
+ def build(oas_routes, oas_source: {})
99
+ oas = Builders::SpecificationBuilder.new.with_oas_routes(oas_routes).build.to_spec
100
+
101
+ oas_source.deeper_merge(oas, merge_hash_arrays: true, extend_existing_arrays: true)
102
+ end
88
103
  end
89
104
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oas_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - a-chacon
@@ -23,6 +23,26 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: deep_merge
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.2'
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.2.2
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.2'
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.2
26
46
  - !ruby/object:Gem::Dependency
27
47
  name: method_source
28
48
  requirement: !ruby/object:Gem::Requirement
@@ -98,16 +118,22 @@ files:
98
118
  - lib/oas_core/version.rb
99
119
  - lib/oas_core/yard/example_tag.rb
100
120
  - lib/oas_core/yard/oas_core_factory.rb
121
+ - lib/oas_core/yard/parameter_reference_tag.rb
101
122
  - lib/oas_core/yard/parameter_tag.rb
123
+ - lib/oas_core/yard/reference_tag.rb
102
124
  - lib/oas_core/yard/request_body_example_tag.rb
125
+ - lib/oas_core/yard/request_body_reference_tag.rb
103
126
  - lib/oas_core/yard/request_body_tag.rb
104
127
  - lib/oas_core/yard/response_example_tag.rb
128
+ - lib/oas_core/yard/response_reference_tag.rb
105
129
  - lib/oas_core/yard/response_tag.rb
106
130
  homepage: https://github.com/a-chacon/oas_core
107
131
  licenses:
108
132
  - GPL-3.0-only
109
133
  metadata:
110
134
  homepage_uri: https://github.com/a-chacon/oas_core
135
+ source_code_uri: https://github.com/a-chacon/oas_core
136
+ changelog_uri: https://github.com/a-chacon/oas_core/blob/main/CHANGELOG.md
111
137
  rubygems_mfa_required: 'true'
112
138
  rdoc_options: []
113
139
  require_paths: