rails-openapi-gen 0.0.2 → 0.0.3

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/CLAUDE.md +17 -5
  4. data/README.md +25 -0
  5. data/lib/rails-openapi-gen/ast_nodes/array_node.rb +101 -0
  6. data/lib/rails-openapi-gen/ast_nodes/base_node.rb +139 -0
  7. data/lib/rails-openapi-gen/ast_nodes/comment_data.rb +180 -0
  8. data/lib/rails-openapi-gen/ast_nodes/node_factory.rb +206 -0
  9. data/lib/rails-openapi-gen/ast_nodes/object_node.rb +129 -0
  10. data/lib/rails-openapi-gen/ast_nodes/partial_node.rb +111 -0
  11. data/lib/rails-openapi-gen/ast_nodes/property_node.rb +74 -0
  12. data/lib/rails-openapi-gen/ast_nodes.rb +129 -0
  13. data/lib/rails-openapi-gen/configuration.rb +154 -22
  14. data/lib/rails-openapi-gen/debug_helpers.rb +185 -0
  15. data/lib/rails-openapi-gen/engine.rb +1 -1
  16. data/lib/rails-openapi-gen/generators/yaml_generator.rb +242 -27
  17. data/lib/rails-openapi-gen/generators.rb +5 -0
  18. data/lib/rails-openapi-gen/importer.rb +164 -145
  19. data/lib/rails-openapi-gen/parsers/comment_parser.rb +1 -1
  20. data/lib/rails-openapi-gen/parsers/comment_parsers/attribute_parser.rb +7 -7
  21. data/lib/rails-openapi-gen/parsers/comment_parsers/base_attribute_parser.rb +5 -9
  22. data/lib/rails-openapi-gen/parsers/comment_parsers/body_parser.rb +6 -6
  23. data/lib/rails-openapi-gen/parsers/comment_parsers/conditional_parser.rb +1 -1
  24. data/lib/rails-openapi-gen/parsers/comment_parsers/operation_parser.rb +5 -5
  25. data/lib/rails-openapi-gen/parsers/comment_parsers/param_parser.rb +6 -6
  26. data/lib/rails-openapi-gen/parsers/comment_parsers/query_parser.rb +6 -6
  27. data/lib/rails-openapi-gen/parsers/controller_parser.rb +64 -20
  28. data/lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb +914 -0
  29. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/array_call_detector.rb +103 -0
  30. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/base_detector.rb +107 -0
  31. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/cache_call_detector.rb +112 -0
  32. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/json_call_detector.rb +91 -0
  33. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/key_format_detector.rb +27 -0
  34. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/null_handling_detector.rb +27 -0
  35. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/object_manipulation_detector.rb +27 -0
  36. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/partial_call_detector.rb +125 -0
  37. data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors.rb +95 -0
  38. data/lib/rails-openapi-gen/parsers/jbuilder/jbuilder_parser.rb +39 -0
  39. data/lib/rails-openapi-gen/parsers/jbuilder/operation_comment_parser.rb +26 -0
  40. data/lib/rails-openapi-gen/parsers/jbuilder/processors/array_processor.rb +266 -0
  41. data/lib/rails-openapi-gen/parsers/jbuilder/processors/base_processor.rb +235 -0
  42. data/lib/rails-openapi-gen/parsers/jbuilder/processors/composite_processor.rb +97 -0
  43. data/lib/rails-openapi-gen/parsers/jbuilder/processors/object_processor.rb +176 -0
  44. data/lib/rails-openapi-gen/parsers/jbuilder/processors/partial_processor.rb +69 -0
  45. data/lib/rails-openapi-gen/parsers/jbuilder/processors/property_processor.rb +68 -0
  46. data/lib/rails-openapi-gen/parsers/jbuilder/processors.rb +10 -0
  47. data/lib/rails-openapi-gen/parsers/jbuilder/property_comment_parser.rb +26 -0
  48. data/lib/rails-openapi-gen/parsers/jbuilder.rb +10 -0
  49. data/lib/rails-openapi-gen/parsers/routes_parser.rb +83 -9
  50. data/lib/rails-openapi-gen/parsers/template_processors/jbuilder_template_processor.rb +125 -131
  51. data/lib/rails-openapi-gen/parsers/template_processors/response_template_processor.rb +8 -12
  52. data/lib/rails-openapi-gen/parsers/template_processors.rb +6 -0
  53. data/lib/rails-openapi-gen/parsers.rb +9 -0
  54. data/lib/rails-openapi-gen/processors/ast_to_schema_processor.rb +226 -0
  55. data/lib/rails-openapi-gen/processors/base_processor.rb +124 -0
  56. data/lib/rails-openapi-gen/processors/component_schema_processor.rb +35 -0
  57. data/lib/rails-openapi-gen/processors/openapi_schema_processor.rb +218 -0
  58. data/lib/rails-openapi-gen/processors.rb +7 -0
  59. data/lib/rails-openapi-gen/railtie.rb +1 -1
  60. data/lib/rails-openapi-gen/tasks/openapi.rake +4 -4
  61. data/lib/rails-openapi-gen/version.rb +1 -1
  62. data/lib/rails-openapi-gen.rb +169 -196
  63. data/lib/tasks/openapi_import.rake +35 -36
  64. data/rails-openapi-gen.gemspec +6 -5
  65. metadata +62 -23
  66. data/lib/rails-openapi-gen/parsers/jbuilder_parser.rb +0 -529
@@ -37,4 +37,4 @@ module RailsOpenapiGen
37
37
  end
38
38
  end
39
39
  end
40
- end
40
+ end
@@ -12,7 +12,7 @@ module RailsOpenapiGen
12
12
  def parse(comment_text)
13
13
  # Skip conditional pattern which is handled by ConditionalParser
14
14
  return nil if comment_text.match?(/@openapi\s+conditional:true\s*$/)
15
-
15
+
16
16
  match = comment_text.match(REGEX)
17
17
  return nil unless match
18
18
 
@@ -24,20 +24,20 @@ module RailsOpenapiGen
24
24
 
25
25
  def parse_attributes(content)
26
26
  attributes = {}
27
-
27
+
28
28
  parts = parse_key_value_pairs(content)
29
-
29
+
30
30
  # First part should be field_name:type
31
31
  if parts.any?
32
32
  first_key, first_value = parts.first
33
33
  attributes[:field_name] = first_key
34
34
  attributes[:type] = clean_value(first_value)
35
35
  end
36
-
36
+
37
37
  # Remaining parts are attributes
38
38
  parts[1..-1]&.each do |key, value|
39
39
  cleaned_value = clean_value(value)
40
-
40
+
41
41
  case key
42
42
  when "required"
43
43
  attributes[:required] = cleaned_value
@@ -49,9 +49,9 @@ module RailsOpenapiGen
49
49
  attributes[key.to_sym] = cleaned_value
50
50
  end
51
51
  end
52
-
52
+
53
53
  attributes
54
54
  end
55
55
  end
56
56
  end
57
- end
57
+ end
@@ -7,11 +7,9 @@ module RailsOpenapiGen
7
7
 
8
8
  def clean_value(value)
9
9
  value = value.strip
10
-
10
+
11
11
  if value.start_with?('"') && value.end_with?('"')
12
12
  value[1..-2]
13
- elsif value.start_with?('[') && value.end_with?(']')
14
- value
15
13
  else
16
14
  value
17
15
  end
@@ -19,10 +17,10 @@ module RailsOpenapiGen
19
17
 
20
18
  def parse_enum(value)
21
19
  return value unless value.is_a?(String) && value.start_with?('[') && value.end_with?(']')
22
-
20
+
23
21
  inner = value[1..-2]
24
-
25
- items = inner.split(',').map do |item|
22
+
23
+ inner.split(',').map do |item|
26
24
  item = item.strip
27
25
  if item.start_with?('"') && item.end_with?('"')
28
26
  item[1..-2]
@@ -30,8 +28,6 @@ module RailsOpenapiGen
30
28
  item
31
29
  end
32
30
  end
33
-
34
- items
35
31
  end
36
32
 
37
33
  def parse_key_value_pairs(content)
@@ -39,4 +35,4 @@ module RailsOpenapiGen
39
35
  end
40
36
  end
41
37
  end
42
- end
38
+ end
@@ -21,20 +21,20 @@ module RailsOpenapiGen
21
21
 
22
22
  def parse_parameter_attributes(content)
23
23
  attributes = {}
24
-
24
+
25
25
  parts = parse_key_value_pairs(content)
26
-
26
+
27
27
  # First part should be parameter_name:type
28
28
  if parts.any?
29
29
  first_key, first_value = parts.first
30
30
  attributes[:name] = first_key
31
31
  attributes[:type] = clean_value(first_value)
32
32
  end
33
-
33
+
34
34
  # Remaining parts are attributes
35
35
  parts[1..-1]&.each do |key, value|
36
36
  cleaned_value = clean_value(value)
37
-
37
+
38
38
  case key
39
39
  when "required"
40
40
  attributes[:required] = cleaned_value
@@ -54,9 +54,9 @@ module RailsOpenapiGen
54
54
  attributes[key.to_sym] = cleaned_value
55
55
  end
56
56
  end
57
-
57
+
58
58
  attributes
59
59
  end
60
60
  end
61
61
  end
62
- end
62
+ end
@@ -10,4 +10,4 @@ module RailsOpenapiGen
10
10
  end
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -21,12 +21,12 @@ module RailsOpenapiGen
21
21
 
22
22
  def parse_operation_attributes(content)
23
23
  attributes = {}
24
-
24
+
25
25
  parts = parse_key_value_pairs(content)
26
-
26
+
27
27
  parts.each do |key, value|
28
28
  cleaned_value = clean_value(value)
29
-
29
+
30
30
  case key
31
31
  when "summary"
32
32
  attributes[:summary] = cleaned_value
@@ -42,9 +42,9 @@ module RailsOpenapiGen
42
42
  attributes[key.to_sym] = cleaned_value
43
43
  end
44
44
  end
45
-
45
+
46
46
  attributes
47
47
  end
48
48
  end
49
49
  end
50
- end
50
+ end
@@ -21,20 +21,20 @@ module RailsOpenapiGen
21
21
 
22
22
  def parse_parameter_attributes(content)
23
23
  attributes = {}
24
-
24
+
25
25
  parts = parse_key_value_pairs(content)
26
-
26
+
27
27
  # First part should be parameter_name:type
28
28
  if parts.any?
29
29
  first_key, first_value = parts.first
30
30
  attributes[:name] = first_key
31
31
  attributes[:type] = clean_value(first_value)
32
32
  end
33
-
33
+
34
34
  # Remaining parts are attributes
35
35
  parts[1..-1]&.each do |key, value|
36
36
  cleaned_value = clean_value(value)
37
-
37
+
38
38
  case key
39
39
  when "required"
40
40
  attributes[:required] = cleaned_value
@@ -54,9 +54,9 @@ module RailsOpenapiGen
54
54
  attributes[key.to_sym] = cleaned_value
55
55
  end
56
56
  end
57
-
57
+
58
58
  attributes
59
59
  end
60
60
  end
61
61
  end
62
- end
62
+ end
@@ -21,20 +21,20 @@ module RailsOpenapiGen
21
21
 
22
22
  def parse_parameter_attributes(content)
23
23
  attributes = {}
24
-
24
+
25
25
  parts = parse_key_value_pairs(content)
26
-
26
+
27
27
  # First part should be parameter_name:type
28
28
  if parts.any?
29
29
  first_key, first_value = parts.first
30
30
  attributes[:name] = first_key
31
31
  attributes[:type] = clean_value(first_value)
32
32
  end
33
-
33
+
34
34
  # Remaining parts are attributes
35
35
  parts[1..-1]&.each do |key, value|
36
36
  cleaned_value = clean_value(value)
37
-
37
+
38
38
  case key
39
39
  when "required"
40
40
  attributes[:required] = cleaned_value
@@ -54,9 +54,9 @@ module RailsOpenapiGen
54
54
  attributes[key.to_sym] = cleaned_value
55
55
  end
56
56
  end
57
-
57
+
58
58
  attributes
59
59
  end
60
60
  end
61
61
  end
62
- end
62
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "parser/current"
4
- require_relative "template_processors/jbuilder_template_processor"
4
+ require_relative "template_processors"
5
5
 
6
6
  module RailsOpenapiGen
7
7
  module Parsers
@@ -13,7 +13,9 @@ module RailsOpenapiGen
13
13
  # @param template_processor [ResponseTemplateProcessor] Template processor for extracting template paths
14
14
  def initialize(route, template_processor: nil)
15
15
  @route = route
16
- @template_processor = template_processor || TemplateProcessors::JbuilderTemplateProcessor.new(route[:controller], route[:action])
16
+ @template_processor = template_processor || RailsOpenapiGen::Parsers::TemplateProcessors::JbuilderTemplateProcessor.new(
17
+ route[:controller], route[:action]
18
+ )
17
19
  end
18
20
 
19
21
  # Parses controller to find action method and response template
@@ -24,13 +26,13 @@ module RailsOpenapiGen
24
26
 
25
27
  content = File.read(controller_path)
26
28
  ast = Parser::CurrentRuby.parse(content)
27
-
29
+
28
30
  action_node = find_action_method(ast)
29
31
  return {} unless action_node
30
32
 
31
33
  template_path = extract_template_path(action_node)
32
34
  parameters = extract_parameters_from_comments(content, action_node)
33
-
35
+
34
36
  {
35
37
  controller_path: controller_path,
36
38
  jbuilder_path: template_path, # Keep existing key name for backward compatibility
@@ -48,15 +50,16 @@ module RailsOpenapiGen
48
50
  possible_paths = [
49
51
  Rails.root.join("app", "controllers", "#{controller_name}.rb")
50
52
  ]
51
-
53
+
52
54
  # Handle nested controllers
53
55
  if route[:controller].include?("/")
54
56
  parts = route[:controller].split("/")
55
57
  nested_path = Rails.root.join("app", "controllers", *parts[0..-2], "#{parts.last}_controller.rb")
56
58
  possible_paths << nested_path
57
59
  end
58
-
59
- possible_paths.find { |path| File.exist?(path) }
60
+
61
+ found_path = possible_paths.find { |path| File.exist?(path.to_s) }
62
+ found_path&.to_s
60
63
  end
61
64
 
62
65
  # Finds action method node in the AST
@@ -78,10 +81,10 @@ module RailsOpenapiGen
78
81
 
79
82
  # Try to extract explicit template path from action
80
83
  template_path = template_processor.extract_template_path(action_node, route)
81
-
84
+
82
85
  # If no explicit template found, check for default template
83
86
  template_path ||= template_processor.find_default_template(route)
84
-
87
+
85
88
  template_path
86
89
  end
87
90
 
@@ -94,29 +97,70 @@ module RailsOpenapiGen
94
97
 
95
98
  lines = content.lines
96
99
  action_line = action_node.location.line - 1 # Convert to 0-based index
97
-
100
+
98
101
  # Look for comments before the action method
99
102
  parameters = {
100
103
  path_parameters: [],
101
104
  query_parameters: [],
102
105
  body_parameters: []
103
106
  }
104
-
107
+
105
108
  comment_parser = RailsOpenapiGen::Parsers::CommentParser.new
106
-
109
+
107
110
  # Scan backwards from the action line to find comments
108
111
  (action_line - 1).downto(0) do |line_index|
109
112
  line = lines[line_index].strip
110
-
113
+
111
114
  # Stop if we encounter a non-comment line that's not empty
112
115
  break if !line.empty? && !line.start_with?('#')
113
-
116
+
114
117
  next if line.empty? || !line.include?('@openapi')
115
-
118
+
116
119
  parsed = comment_parser.parse(line)
117
120
  next unless parsed
118
-
119
- if parsed[:parameter]
121
+
122
+ # Handle different comment formats
123
+ if line.include?('path_parameter')
124
+ # Extract the parameter info from parsed attributes
125
+ param_info = {
126
+ name: parsed[:field_name],
127
+ type: parsed[:type],
128
+ description: parsed[:description],
129
+ required: parsed[:required] || 'true',
130
+ enum: parsed[:enum],
131
+ format: parsed[:format],
132
+ minimum: parsed[:minimum],
133
+ maximum: parsed[:maximum],
134
+ example: parsed[:example]
135
+ }.compact
136
+ parameters[:path_parameters] << param_info
137
+ elsif line.include?('query_parameter')
138
+ param_info = {
139
+ name: parsed[:field_name],
140
+ type: parsed[:type],
141
+ description: parsed[:description],
142
+ required: parsed[:required] || 'false',
143
+ enum: parsed[:enum],
144
+ format: parsed[:format],
145
+ minimum: parsed[:minimum],
146
+ maximum: parsed[:maximum],
147
+ example: parsed[:example]
148
+ }.compact
149
+ parameters[:query_parameters] << param_info
150
+ elsif line.include?('body_parameter')
151
+ param_info = {
152
+ name: parsed[:field_name],
153
+ type: parsed[:type],
154
+ description: parsed[:description],
155
+ required: parsed[:required] || 'true',
156
+ enum: parsed[:enum],
157
+ format: parsed[:format],
158
+ minimum: parsed[:minimum],
159
+ maximum: parsed[:maximum],
160
+ example: parsed[:example]
161
+ }.compact
162
+ parameters[:body_parameters] << param_info
163
+ elsif parsed[:parameter]
120
164
  parameters[:path_parameters] << parsed[:parameter]
121
165
  elsif parsed[:query_parameter]
122
166
  parameters[:query_parameters] << parsed[:query_parameter]
@@ -124,7 +168,7 @@ module RailsOpenapiGen
124
168
  parameters[:body_parameters] << parsed[:body_parameter]
125
169
  end
126
170
  end
127
-
171
+
128
172
  parameters
129
173
  end
130
174
 
@@ -134,6 +178,7 @@ module RailsOpenapiGen
134
178
  # Initializes processor to find specific action method
135
179
  # @param action_name [String, Symbol] Name of action to find
136
180
  def initialize(action_name)
181
+ super()
137
182
  @action_name = action_name.to_sym
138
183
  @action_node = nil
139
184
  end
@@ -147,7 +192,6 @@ module RailsOpenapiGen
147
192
  super
148
193
  end
149
194
  end
150
-
151
195
  end
152
196
  end
153
- end
197
+ end