Class: RailsOpenapiGen::Parsers::Jbuilder::AstParser

Inherits:
Parser::AST::Processor
  • Object
show all
Defined in:
lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb

Overview

Main AST parser for Jbuilder templates Orchestrates the parsing process using CallDetectors and building AstNodes

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path) ⇒ AstParser

Returns a new instance of AstParser.



15
16
17
18
19
20
21
22
23
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 15

def initialize(file_path)
  super()
  @file_path = file_path
  @root_node = RailsOpenapiGen::AstNodes::ObjectNode.new(property_name: 'root')
  @current_context = [@root_node]
  @comment_parser = RailsOpenapiGen::Parsers::CommentParser.new
  @conditional_stack = []
  @partial_components = {}
end

Instance Attribute Details

#comment_parserObject (readonly)

Returns the value of attribute comment_parser.



13
14
15
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 13

def comment_parser
  @comment_parser
end

#current_contextObject (readonly)

Returns the value of attribute current_context.



13
14
15
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 13

def current_context
  @current_context
end

#file_pathObject (readonly)

Returns the value of attribute file_path.



13
14
15
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 13

def file_path
  @file_path
end

#partial_componentsObject (readonly)

Returns the value of attribute partial_components.



13
14
15
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 13

def partial_components
  @partial_components
end

#root_nodeObject (readonly)

Returns the value of attribute root_node.



13
14
15
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 13

def root_node
  @root_node
end

Instance Method Details

#on_block(node) ⇒ void

This method returns an undefined value.

Process block nodes

Parameters:

  • node (Parser::AST::Node)

    Block node



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 106

def on_block(node)
  send_node, _args_node, body_node = node.children
  receiver, method_name, *args = send_node.children

  puts "🔍 DEBUG: Processing block: #{method_name}, receiver: #{receiver}" if ENV['RAILS_OPENAPI_DEBUG']

  # Find appropriate detector for this method call
  detector = CallDetectors::DetectorRegistry.find_detector(receiver, method_name, args)

  puts "🔍 DEBUG: Detector found: #{detector ? detector.class.name : 'none'}" if ENV['RAILS_OPENAPI_DEBUG']

  if detector
    process_detected_block(node, detector, receiver, method_name, args, body_node)
  else
    # Unknown block, process body only
    puts "🔍 DEBUG: Unknown block, processing body only" if ENV['RAILS_OPENAPI_DEBUG']
    process(body_node) if body_node
  end
end

#on_if(node) ⇒ void Also known as: on_unless

This method returns an undefined value.

Process conditional nodes (if, unless, etc.)

Parameters:

  • node (Parser::AST::Node)

    Conditional node



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 129

def on_if(node)
  _condition, true_branch, false_branch = node.children

  # Mark subsequent nodes as conditional
  @conditional_stack.push(true)

  # Process true branch
  process(true_branch) if true_branch

  # Process false branch (else/elsif)
  process(false_branch) if false_branch

  # Restore conditional state
  @conditional_stack.pop
end

#on_send(node) ⇒ void

This method returns an undefined value.

Process send nodes (method calls)

Parameters:

  • node (Parser::AST::Node)

    Send node



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 76

def on_send(node)
  receiver, method_name, *args = node.children

  puts "🔍 DEBUG: Processing send node: #{method_name}, receiver: #{receiver}" if ENV['RAILS_OPENAPI_DEBUG']

  # Debug partial calls
  if ENV['RAILS_OPENAPI_DEBUG'] && method_name == :partial!
    puts "🔍 Found partial! call: #{method_name}"
    puts "   receiver: #{receiver&.inspect}"
    puts "   args: #{args.inspect}"
  end

  # Find appropriate detector for this method call
  detector = CallDetectors::DetectorRegistry.find_detector(receiver, method_name, args)

  if ENV['RAILS_OPENAPI_DEBUG'] && method_name == :partial!
    puts "   detector found: #{detector&.name}"
  end

  if detector
    process_detected_call(node, detector, receiver, method_name, args)
  else
    # Unknown method call, process only the arguments (not the receiver to avoid duplicates)
    args.each { |arg| process(arg) }
  end
end

#parse(content = nil) ⇒ RailsOpenapiGen::AstNodes::ObjectNode

Parse the Jbuilder template and return the root AST node

Parameters:

  • content (String, nil) (defaults to: nil)

    Template content (will read from file if nil)

Returns:



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/rails-openapi-gen/parsers/jbuilder/ast_parser.rb', line 28

def parse(content = nil)
  content ||= File.read(@file_path)
  
  begin
    # Try multiple parser approaches for better Ruby 3.1 compatibility
    ast = nil
    
    # First try: Parser::CurrentRuby
    begin
      ast = Parser::CurrentRuby.parse(content, @file_path)
    rescue => e1
      # Second try: Ruby31 parser
      begin
        require 'parser/ruby31'
        parser = Parser::Ruby31.new
        buffer = Parser::Source::Buffer.new(@file_path)
        buffer.source = content
        ast = parser.parse(buffer)
      rescue => e2
        # Third try: Manual parsing (fallback)
        puts "⚠️  All parser attempts failed for #{@file_path}" if ENV['RAILS_OPENAPI_DEBUG']
        puts "⚠️  CurrentRuby error: #{e1.message}" if ENV['RAILS_OPENAPI_DEBUG']
        puts "⚠️  Ruby31 error: #{e2.message}" if ENV['RAILS_OPENAPI_DEBUG']
        
        # For testing purposes, try fallback parsing for all cases when the Parser fails
        puts "⚠️  Attempting fallback parsing for #{@file_path}" if ENV['RAILS_OPENAPI_DEBUG']
        return create_fallback_node_from_content(content)
      end
    end
  rescue => e
    puts "⚠️  Unexpected parser error in #{@file_path}: #{e.message}" if ENV['RAILS_OPENAPI_DEBUG']
    return @root_node
  end

  return @root_node unless ast

  # Store content for comment extraction
  @content_lines = content.lines.map(&:chomp)

  # Process the AST
  process(ast)

  @root_node
end