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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/CLAUDE.md +17 -5
- data/README.md +25 -0
- data/lib/rails-openapi-gen/ast_nodes/array_node.rb +101 -0
- data/lib/rails-openapi-gen/ast_nodes/base_node.rb +139 -0
- data/lib/rails-openapi-gen/ast_nodes/comment_data.rb +180 -0
- data/lib/rails-openapi-gen/ast_nodes/node_factory.rb +206 -0
- data/lib/rails-openapi-gen/ast_nodes/object_node.rb +129 -0
- data/lib/rails-openapi-gen/ast_nodes/partial_node.rb +111 -0
- data/lib/rails-openapi-gen/ast_nodes/property_node.rb +74 -0
- data/lib/rails-openapi-gen/ast_nodes.rb +129 -0
- data/lib/rails-openapi-gen/configuration.rb +154 -22
- data/lib/rails-openapi-gen/debug_helpers.rb +185 -0
- data/lib/rails-openapi-gen/engine.rb +1 -1
- data/lib/rails-openapi-gen/generators/yaml_generator.rb +242 -27
- data/lib/rails-openapi-gen/generators.rb +5 -0
- data/lib/rails-openapi-gen/importer.rb +164 -145
- data/lib/rails-openapi-gen/parsers/comment_parser.rb +1 -1
- data/lib/rails-openapi-gen/parsers/comment_parsers/attribute_parser.rb +7 -7
- data/lib/rails-openapi-gen/parsers/comment_parsers/base_attribute_parser.rb +5 -9
- data/lib/rails-openapi-gen/parsers/comment_parsers/body_parser.rb +6 -6
- data/lib/rails-openapi-gen/parsers/comment_parsers/conditional_parser.rb +1 -1
- data/lib/rails-openapi-gen/parsers/comment_parsers/operation_parser.rb +5 -5
- data/lib/rails-openapi-gen/parsers/comment_parsers/param_parser.rb +6 -6
- data/lib/rails-openapi-gen/parsers/comment_parsers/query_parser.rb +6 -6
- data/lib/rails-openapi-gen/parsers/controller_parser.rb +64 -20
- data/lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb +914 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/array_call_detector.rb +103 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/base_detector.rb +107 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/cache_call_detector.rb +112 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/json_call_detector.rb +91 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/key_format_detector.rb +27 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/null_handling_detector.rb +27 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/object_manipulation_detector.rb +27 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors/partial_call_detector.rb +125 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/call_detectors.rb +95 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/jbuilder_parser.rb +39 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/operation_comment_parser.rb +26 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/array_processor.rb +266 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/base_processor.rb +235 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/composite_processor.rb +97 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/object_processor.rb +176 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/partial_processor.rb +69 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors/property_processor.rb +68 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/processors.rb +10 -0
- data/lib/rails-openapi-gen/parsers/jbuilder/property_comment_parser.rb +26 -0
- data/lib/rails-openapi-gen/parsers/jbuilder.rb +10 -0
- data/lib/rails-openapi-gen/parsers/routes_parser.rb +83 -9
- data/lib/rails-openapi-gen/parsers/template_processors/jbuilder_template_processor.rb +125 -131
- data/lib/rails-openapi-gen/parsers/template_processors/response_template_processor.rb +8 -12
- data/lib/rails-openapi-gen/parsers/template_processors.rb +6 -0
- data/lib/rails-openapi-gen/parsers.rb +9 -0
- data/lib/rails-openapi-gen/processors/ast_to_schema_processor.rb +226 -0
- data/lib/rails-openapi-gen/processors/base_processor.rb +124 -0
- data/lib/rails-openapi-gen/processors/component_schema_processor.rb +35 -0
- data/lib/rails-openapi-gen/processors/openapi_schema_processor.rb +218 -0
- data/lib/rails-openapi-gen/processors.rb +7 -0
- data/lib/rails-openapi-gen/railtie.rb +1 -1
- data/lib/rails-openapi-gen/tasks/openapi.rake +4 -4
- data/lib/rails-openapi-gen/version.rb +1 -1
- data/lib/rails-openapi-gen.rb +169 -196
- data/lib/tasks/openapi_import.rake +35 -36
- data/rails-openapi-gen.gemspec +6 -5
- metadata +62 -23
- data/lib/rails-openapi-gen/parsers/jbuilder_parser.rb +0 -529
@@ -1,529 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "parser/current"
|
4
|
-
require "ostruct"
|
5
|
-
|
6
|
-
module RailsOpenapiGen
|
7
|
-
module Parsers
|
8
|
-
class JbuilderParser
|
9
|
-
attr_reader :jbuilder_path
|
10
|
-
|
11
|
-
# Initializes Jbuilder parser with template path
|
12
|
-
# @param jbuilder_path [String] Path to Jbuilder template file
|
13
|
-
def initialize(jbuilder_path)
|
14
|
-
@jbuilder_path = jbuilder_path
|
15
|
-
@properties = []
|
16
|
-
@operation_info = nil
|
17
|
-
@parsed_files = Set.new
|
18
|
-
end
|
19
|
-
|
20
|
-
# Parses Jbuilder template to extract properties and operation info
|
21
|
-
# @return [Hash] Hash with properties array and operation info
|
22
|
-
def parse
|
23
|
-
return { properties: @properties, operation: @operation_info } unless File.exist?(jbuilder_path)
|
24
|
-
|
25
|
-
parse_file(jbuilder_path)
|
26
|
-
{ properties: @properties, operation: @operation_info }
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
# Recursively parses a Jbuilder file and its partials
|
32
|
-
# @param file_path [String] Path to file to parse
|
33
|
-
# @return [void]
|
34
|
-
def parse_file(file_path)
|
35
|
-
return if @parsed_files.include?(file_path)
|
36
|
-
@parsed_files << file_path
|
37
|
-
|
38
|
-
content = File.read(file_path)
|
39
|
-
|
40
|
-
# Extract block comments first
|
41
|
-
block_comments = extract_block_comments(content)
|
42
|
-
|
43
|
-
ast, comments = Parser::CurrentRuby.parse_with_comments(content)
|
44
|
-
|
45
|
-
# Combine line comments and block comments
|
46
|
-
all_comments = comments + block_comments
|
47
|
-
|
48
|
-
processor = JbuilderProcessor.new(file_path, all_comments)
|
49
|
-
processor.process(ast)
|
50
|
-
|
51
|
-
@properties.concat(processor.properties)
|
52
|
-
@operation_info ||= processor.operation_info
|
53
|
-
|
54
|
-
processor.partials.each do |partial_path|
|
55
|
-
parse_file(partial_path) if File.exist?(partial_path)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Extracts block comments (=begin/=end) from file content
|
60
|
-
# @param content [String] File content
|
61
|
-
# @return [Array<OpenStruct>] Array of mock comment objects
|
62
|
-
def extract_block_comments(content)
|
63
|
-
block_comments = []
|
64
|
-
lines = content.lines
|
65
|
-
|
66
|
-
i = 0
|
67
|
-
while i < lines.length
|
68
|
-
line = lines[i].strip
|
69
|
-
if line.start_with?('=begin')
|
70
|
-
# Found start of block comment
|
71
|
-
comment_lines = []
|
72
|
-
i += 1
|
73
|
-
|
74
|
-
while i < lines.length && !lines[i].strip.start_with?('=end')
|
75
|
-
comment_lines << lines[i]
|
76
|
-
i += 1
|
77
|
-
end
|
78
|
-
|
79
|
-
# Create a mock comment object
|
80
|
-
comment_text = comment_lines.join
|
81
|
-
if comment_text.include?('@openapi')
|
82
|
-
mock_comment = OpenStruct.new(
|
83
|
-
text: comment_text,
|
84
|
-
location: OpenStruct.new(line: i - comment_lines.length)
|
85
|
-
)
|
86
|
-
block_comments << mock_comment
|
87
|
-
end
|
88
|
-
end
|
89
|
-
i += 1
|
90
|
-
end
|
91
|
-
|
92
|
-
block_comments
|
93
|
-
end
|
94
|
-
|
95
|
-
class JbuilderProcessor < Parser::AST::Processor
|
96
|
-
attr_reader :properties, :partials, :operation_info
|
97
|
-
|
98
|
-
# Initializes AST processor for Jbuilder parsing
|
99
|
-
# @param file_path [String] Path to current file
|
100
|
-
# @param comments [Array] Array of comment objects
|
101
|
-
def initialize(file_path, comments)
|
102
|
-
@file_path = file_path
|
103
|
-
@comments = comments
|
104
|
-
@operation_info = nil
|
105
|
-
@properties = []
|
106
|
-
@partials = []
|
107
|
-
@comment_parser = CommentParser.new
|
108
|
-
@block_stack = []
|
109
|
-
@current_object_properties = []
|
110
|
-
@nested_objects = {}
|
111
|
-
@conditional_stack = []
|
112
|
-
end
|
113
|
-
|
114
|
-
# Processes method call nodes to extract JSON properties
|
115
|
-
# @param node [Parser::AST::Node] Method call node
|
116
|
-
# @return [void]
|
117
|
-
def on_send(node)
|
118
|
-
receiver, method_name, *args = node.children
|
119
|
-
|
120
|
-
if cache_call?(receiver, method_name) || cache_if_call?(receiver, method_name) || jbuilder_helper?(receiver, method_name)
|
121
|
-
# Skip Jbuilder helper methods - they are not JSON properties
|
122
|
-
super
|
123
|
-
elsif array_call?(receiver, method_name)
|
124
|
-
# Check if this is an array with partial
|
125
|
-
if args.any? && args.any? { |arg| arg.type == :hash && has_partial_key?(arg) }
|
126
|
-
process_array_with_partial(node, args)
|
127
|
-
else
|
128
|
-
process_array_property(node)
|
129
|
-
end
|
130
|
-
elsif partial_call?(receiver, method_name)
|
131
|
-
process_partial(args)
|
132
|
-
elsif json_property?(receiver, method_name)
|
133
|
-
process_json_property(node, method_name.to_s, args)
|
134
|
-
end
|
135
|
-
|
136
|
-
super
|
137
|
-
end
|
138
|
-
|
139
|
-
# Processes block nodes for nested objects and array iterations
|
140
|
-
# @param node [Parser::AST::Node] Block node
|
141
|
-
# @return [void]
|
142
|
-
def on_block(node)
|
143
|
-
send_node, args_node, body = node.children
|
144
|
-
receiver, method_name, *send_args = send_node.children
|
145
|
-
|
146
|
-
if cache_call?(receiver, method_name) || cache_if_call?(receiver, method_name)
|
147
|
-
# This is json.cache! or json.cache_if! block - just process the block contents
|
148
|
-
process(body) if body
|
149
|
-
elsif json_property?(receiver, method_name) && method_name != :array!
|
150
|
-
# Check if this is an array iteration block (has block arguments)
|
151
|
-
if args_node && args_node.type == :args && args_node.children.any?
|
152
|
-
# This is an array iteration block like json.tags @tags do |tag|
|
153
|
-
process_array_iteration_block(node, method_name.to_s)
|
154
|
-
else
|
155
|
-
# This is a nested object block like json.profile do
|
156
|
-
process_nested_object_block(node, method_name.to_s)
|
157
|
-
end
|
158
|
-
elsif array_call?(receiver, method_name)
|
159
|
-
# This is json.array! block
|
160
|
-
@block_stack.push(:array)
|
161
|
-
super
|
162
|
-
@block_stack.pop
|
163
|
-
else
|
164
|
-
super
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# Processes if statements to track conditional properties
|
169
|
-
# @param node [Parser::AST::Node] If statement node
|
170
|
-
# @return [void]
|
171
|
-
def on_if(node)
|
172
|
-
# Check if this if statement has a conditional comment
|
173
|
-
comment_data = find_comment_for_node(node)
|
174
|
-
|
175
|
-
if comment_data && comment_data[:conditional]
|
176
|
-
@conditional_stack.push(true)
|
177
|
-
super
|
178
|
-
@conditional_stack.pop
|
179
|
-
else
|
180
|
-
super
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
private
|
185
|
-
|
186
|
-
# Checks if node represents a json property call
|
187
|
-
# @param receiver [Parser::AST::Node] Receiver node
|
188
|
-
# @param method_name [Symbol] Method name
|
189
|
-
# @return [Boolean] True if json property call
|
190
|
-
def json_property?(receiver, method_name)
|
191
|
-
receiver && receiver.type == :send && receiver.children[1] == :json
|
192
|
-
end
|
193
|
-
|
194
|
-
# Checks if node represents a partial render call
|
195
|
-
# @param receiver [Parser::AST::Node] Receiver node
|
196
|
-
# @param method_name [Symbol] Method name
|
197
|
-
# @return [Boolean] True if partial call
|
198
|
-
def partial_call?(receiver, method_name)
|
199
|
-
method_name == :partial! && (!receiver || receiver.type == :send && receiver.children[1] == :json)
|
200
|
-
end
|
201
|
-
|
202
|
-
# Checks if node represents a json.array! call
|
203
|
-
# @param receiver [Parser::AST::Node] Receiver node
|
204
|
-
# @param method_name [Symbol] Method name
|
205
|
-
# @return [Boolean] True if array call
|
206
|
-
def array_call?(receiver, method_name)
|
207
|
-
method_name == :array! && receiver && receiver.type == :send && receiver.children[1] == :json
|
208
|
-
end
|
209
|
-
|
210
|
-
# Checks if node represents a json.cache! call
|
211
|
-
# @param receiver [Parser::AST::Node] Receiver node
|
212
|
-
# @param method_name [Symbol] Method name
|
213
|
-
# @return [Boolean] True if cache call
|
214
|
-
def cache_call?(receiver, method_name)
|
215
|
-
method_name == :cache! && receiver && receiver.type == :send && receiver.children[1] == :json
|
216
|
-
end
|
217
|
-
|
218
|
-
# Checks if node represents a json.cache_if! call
|
219
|
-
# @param receiver [Parser::AST::Node] Receiver node
|
220
|
-
# @param method_name [Symbol] Method name
|
221
|
-
# @return [Boolean] True if cache_if call
|
222
|
-
def cache_if_call?(receiver, method_name)
|
223
|
-
method_name == :cache_if! && receiver && receiver.type == :send && receiver.children[1] == :json
|
224
|
-
end
|
225
|
-
|
226
|
-
# Checks if node represents a Jbuilder helper method that should be ignored
|
227
|
-
# @param receiver [Parser::AST::Node] Receiver node
|
228
|
-
# @param method_name [Symbol] Method name
|
229
|
-
# @return [Boolean] True if helper method
|
230
|
-
def jbuilder_helper?(receiver, method_name)
|
231
|
-
helper_methods = [:key_format!, :ignore_nil!, :merge!, :deep_format_keys!, :set!, :child!, :nil!, :null!, :cache_root!]
|
232
|
-
helper_methods.include?(method_name) && receiver && receiver.type == :send && receiver.children[1] == :json
|
233
|
-
end
|
234
|
-
|
235
|
-
# Processes a simple JSON property assignment
|
236
|
-
# @param node [Parser::AST::Node] Property node
|
237
|
-
# @param property_name [String] Name of the property
|
238
|
-
# @param args [Array] Method arguments
|
239
|
-
# @return [void]
|
240
|
-
def process_json_property(node, property_name, args)
|
241
|
-
comment_data = find_comment_for_node(node)
|
242
|
-
|
243
|
-
# Check if we're inside an array block
|
244
|
-
if @block_stack.last == :array
|
245
|
-
process_simple_property(node, property_name, comment_data)
|
246
|
-
else
|
247
|
-
process_simple_property(node, property_name, comment_data)
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
# Processes json.array! calls to create array schema
|
252
|
-
# @param node [Parser::AST::Node] Array call node
|
253
|
-
# @return [void]
|
254
|
-
def process_array_property(node)
|
255
|
-
comment_data = find_comment_for_node(node)
|
256
|
-
|
257
|
-
# Mark this as an array root
|
258
|
-
property_info = {
|
259
|
-
property: "items", # Special property to indicate array items
|
260
|
-
comment_data: comment_data || { type: "array", items: { type: "object" } },
|
261
|
-
is_array_root: true
|
262
|
-
}
|
263
|
-
|
264
|
-
@properties << property_info
|
265
|
-
end
|
266
|
-
|
267
|
-
# Processes json.array! with partial rendering
|
268
|
-
# @param node [Parser::AST::Node] Array call node
|
269
|
-
# @param args [Array] Array call arguments
|
270
|
-
# @return [void]
|
271
|
-
def process_array_with_partial(node, args)
|
272
|
-
# Extract partial path from the hash arguments
|
273
|
-
partial_path = nil
|
274
|
-
args.each do |arg|
|
275
|
-
if arg.type == :hash
|
276
|
-
arg.children.each do |pair|
|
277
|
-
if pair.type == :pair
|
278
|
-
key, value = pair.children
|
279
|
-
if key.type == :sym && key.children.first == :partial && value.type == :str
|
280
|
-
partial_path = value.children.first
|
281
|
-
break
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
if partial_path
|
289
|
-
# Resolve the partial path and parse it
|
290
|
-
resolved_path = resolve_partial_path(partial_path)
|
291
|
-
if resolved_path && File.exist?(resolved_path)
|
292
|
-
# Parse the partial to get its properties
|
293
|
-
partial_parser = JbuilderParser.new(resolved_path)
|
294
|
-
partial_result = partial_parser.parse
|
295
|
-
|
296
|
-
# Create array schema with items from the partial
|
297
|
-
property_info = {
|
298
|
-
property: "items",
|
299
|
-
comment_data: { type: "array" },
|
300
|
-
is_array_root: true,
|
301
|
-
array_item_properties: partial_result[:properties]
|
302
|
-
}
|
303
|
-
|
304
|
-
@properties << property_info
|
305
|
-
else
|
306
|
-
# Fallback to regular array processing
|
307
|
-
process_array_property(node)
|
308
|
-
end
|
309
|
-
else
|
310
|
-
# Fallback to regular array processing
|
311
|
-
process_array_property(node)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
# Checks if hash node contains a :partial key
|
316
|
-
# @param hash_node [Parser::AST::Node] Hash node to check
|
317
|
-
# @return [Boolean] True if partial key exists
|
318
|
-
def has_partial_key?(hash_node)
|
319
|
-
hash_node.children.any? do |pair|
|
320
|
-
if pair.type == :pair
|
321
|
-
key, _value = pair.children
|
322
|
-
key.type == :sym && key.children.first == :partial
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
# Processes array iteration blocks (e.g., json.tags @tags do |tag|)
|
328
|
-
# @param node [Parser::AST::Node] Block node
|
329
|
-
# @param property_name [String] Array property name
|
330
|
-
# @return [void]
|
331
|
-
def process_array_iteration_block(node, property_name)
|
332
|
-
comment_data = find_comment_for_node(node)
|
333
|
-
|
334
|
-
# Save current context
|
335
|
-
previous_properties = @properties.dup
|
336
|
-
previous_partials = @partials.dup
|
337
|
-
|
338
|
-
# Create a temporary properties array for array items
|
339
|
-
@properties = []
|
340
|
-
@partials = []
|
341
|
-
@block_stack.push(:array)
|
342
|
-
|
343
|
-
# Process the block contents
|
344
|
-
send_node, _args, body = node.children
|
345
|
-
process(body) if body
|
346
|
-
|
347
|
-
# Collect item properties
|
348
|
-
item_properties = @properties.dup
|
349
|
-
|
350
|
-
# Process any partials found in this block
|
351
|
-
@partials.each do |partial_path|
|
352
|
-
if File.exist?(partial_path)
|
353
|
-
partial_properties = parse_partial_for_nested_object(partial_path)
|
354
|
-
item_properties.concat(partial_properties)
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
# Restore context
|
359
|
-
@properties = previous_properties
|
360
|
-
@partials = previous_partials
|
361
|
-
@block_stack.pop
|
362
|
-
|
363
|
-
# Build array schema with items
|
364
|
-
property_info = {
|
365
|
-
property: property_name,
|
366
|
-
comment_data: comment_data || { type: "array" },
|
367
|
-
is_array: true,
|
368
|
-
array_item_properties: item_properties
|
369
|
-
}
|
370
|
-
|
371
|
-
@properties << property_info
|
372
|
-
end
|
373
|
-
|
374
|
-
# Processes nested object blocks (e.g., json.profile do)
|
375
|
-
# @param node [Parser::AST::Node] Block node
|
376
|
-
# @param property_name [String] Object property name
|
377
|
-
# @return [void]
|
378
|
-
def process_nested_object_block(node, property_name)
|
379
|
-
comment_data = find_comment_for_node(node)
|
380
|
-
|
381
|
-
# Save current context
|
382
|
-
previous_nested_objects = @nested_objects.dup
|
383
|
-
previous_properties = @properties.dup
|
384
|
-
previous_partials = @partials.dup
|
385
|
-
|
386
|
-
# Create a temporary properties array for this nested object
|
387
|
-
@properties = []
|
388
|
-
@partials = []
|
389
|
-
@block_stack.push(:object)
|
390
|
-
|
391
|
-
# Process the block contents
|
392
|
-
send_node, _args, body = node.children
|
393
|
-
process(body) if body
|
394
|
-
|
395
|
-
# Collect nested properties
|
396
|
-
nested_properties = @properties.dup
|
397
|
-
|
398
|
-
# Process any partials found in this block
|
399
|
-
@partials.each do |partial_path|
|
400
|
-
if File.exist?(partial_path)
|
401
|
-
partial_properties = parse_partial_for_nested_object(partial_path)
|
402
|
-
nested_properties.concat(partial_properties)
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
# Restore context
|
407
|
-
@properties = previous_properties
|
408
|
-
@partials = previous_partials # Don't add nested partials to main partials
|
409
|
-
@nested_objects = previous_nested_objects
|
410
|
-
@block_stack.pop
|
411
|
-
|
412
|
-
# Store nested object info
|
413
|
-
@nested_objects[property_name] = nested_properties
|
414
|
-
|
415
|
-
# Add the parent property
|
416
|
-
property_info = {
|
417
|
-
property: property_name,
|
418
|
-
comment_data: comment_data || { type: "object" },
|
419
|
-
is_object: true,
|
420
|
-
nested_properties: nested_properties
|
421
|
-
}
|
422
|
-
|
423
|
-
# Mark as optional if inside a conditional block
|
424
|
-
if @conditional_stack.any?
|
425
|
-
property_info[:is_conditional] = true
|
426
|
-
end
|
427
|
-
|
428
|
-
@properties << property_info
|
429
|
-
end
|
430
|
-
|
431
|
-
# Processes a simple property assignment
|
432
|
-
# @param node [Parser::AST::Node] Property node
|
433
|
-
# @param property_name [String] Name of the property
|
434
|
-
# @param comment_data [Hash, nil] Parsed comment data
|
435
|
-
# @return [void]
|
436
|
-
def process_simple_property(node, property_name, comment_data)
|
437
|
-
property_info = {
|
438
|
-
property: property_name,
|
439
|
-
comment_data: comment_data
|
440
|
-
}
|
441
|
-
|
442
|
-
unless comment_data && !comment_data.empty?
|
443
|
-
property_info[:comment_data] = { type: "TODO: MISSING COMMENT" }
|
444
|
-
end
|
445
|
-
|
446
|
-
# Mark as optional if inside a conditional block
|
447
|
-
if @conditional_stack.any?
|
448
|
-
property_info[:is_conditional] = true
|
449
|
-
end
|
450
|
-
|
451
|
-
@properties << property_info
|
452
|
-
end
|
453
|
-
|
454
|
-
|
455
|
-
# Processes partial render calls to track dependencies
|
456
|
-
# @param args [Array] Partial call arguments
|
457
|
-
# @return [void]
|
458
|
-
def process_partial(args)
|
459
|
-
return if args.empty?
|
460
|
-
|
461
|
-
partial_arg = args.first
|
462
|
-
if partial_arg.type == :str
|
463
|
-
partial_name = partial_arg.children.first
|
464
|
-
partial_path = resolve_partial_path(partial_name)
|
465
|
-
@partials << partial_path if partial_path
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
# Finds OpenAPI comment for a given AST node
|
470
|
-
# @param node [Parser::AST::Node] Node to find comment for
|
471
|
-
# @return [Hash, nil] Parsed comment data or nil
|
472
|
-
def find_comment_for_node(node)
|
473
|
-
line_number = node.location.line
|
474
|
-
|
475
|
-
# First check all comments for operation info
|
476
|
-
@comments.each do |comment|
|
477
|
-
parsed = @comment_parser.parse(comment.text)
|
478
|
-
if parsed&.dig(:operation) && @operation_info.nil?
|
479
|
-
@operation_info = parsed[:operation]
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
# Then find comment for the specific node
|
484
|
-
@comments.reverse.find do |comment|
|
485
|
-
comment_line = comment.location.line
|
486
|
-
comment_line == line_number - 1 || comment_line == line_number
|
487
|
-
end&.then do |comment|
|
488
|
-
@comment_parser.parse(comment.text)
|
489
|
-
end
|
490
|
-
end
|
491
|
-
|
492
|
-
# Resolves partial name to full file path
|
493
|
-
# @param partial_name [String] Partial name (e.g., "users/user")
|
494
|
-
# @return [String, nil] Full path to partial file or nil
|
495
|
-
def resolve_partial_path(partial_name)
|
496
|
-
dir = File.dirname(@file_path)
|
497
|
-
|
498
|
-
if partial_name.include?("/")
|
499
|
-
# Find the app/views directory from the current file path
|
500
|
-
path_parts = @file_path.split('/')
|
501
|
-
views_index = path_parts.rindex('views')
|
502
|
-
if views_index
|
503
|
-
views_path = path_parts[0..views_index].join('/')
|
504
|
-
# For paths like 'users/user', convert to 'users/_user.json.jbuilder'
|
505
|
-
parts = partial_name.split('/')
|
506
|
-
dir_part = parts[0..-2].join('/')
|
507
|
-
file_part = "_#{parts[-1]}"
|
508
|
-
File.join(views_path, dir_part, "#{file_part}.json.jbuilder")
|
509
|
-
else
|
510
|
-
File.join(dir, "#{partial_name}.json.jbuilder")
|
511
|
-
end
|
512
|
-
else
|
513
|
-
File.join(dir, "_#{partial_name}.json.jbuilder")
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
# Parses a partial file to extract properties for nested objects
|
518
|
-
# @param partial_path [String] Path to partial file
|
519
|
-
# @return [Array<Hash>] Array of property definitions
|
520
|
-
def parse_partial_for_nested_object(partial_path)
|
521
|
-
# Create a new parser to parse the partial independently
|
522
|
-
partial_parser = JbuilderParser.new(partial_path)
|
523
|
-
result = partial_parser.parse
|
524
|
-
result[:properties]
|
525
|
-
end
|
526
|
-
end
|
527
|
-
end
|
528
|
-
end
|
529
|
-
end
|