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
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_detector'
|
4
|
+
|
5
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
6
|
+
# Detects array calls (json.array!)
|
7
|
+
# Handles array generation in Jbuilder templates
|
8
|
+
class ArrayCallDetector < BaseDetector
|
9
|
+
class << self
|
10
|
+
# Check if this detector handles the method call
|
11
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver node
|
12
|
+
# @param method_name [Symbol] Method name
|
13
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
14
|
+
# @return [Boolean] True if this is an array call
|
15
|
+
def handles?(receiver, method_name, _args = [])
|
16
|
+
result = array_call?(receiver, method_name)
|
17
|
+
if ENV['RAILS_OPENAPI_DEBUG']
|
18
|
+
puts "🔍 DEBUG: ArrayCallDetector.handles? for #{method_name}"
|
19
|
+
puts " receiver: #{receiver.inspect}"
|
20
|
+
puts " method_name: #{method_name.inspect}"
|
21
|
+
puts " method_name == :array!: #{method_name == :array!}"
|
22
|
+
puts " json_receiver?: #{json_receiver?(receiver)}"
|
23
|
+
puts " result: #{result}"
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
# High priority for array calls
|
29
|
+
# @return [Integer] Priority value
|
30
|
+
def priority
|
31
|
+
20
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Symbol] Detector category
|
35
|
+
def category
|
36
|
+
:array
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] Description
|
40
|
+
def description
|
41
|
+
"Array calls (json.array!)"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if this is an array call
|
45
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver
|
46
|
+
# @param method_name [Symbol] Method name
|
47
|
+
# @return [Boolean] True if this is an array call
|
48
|
+
def array_call?(receiver, method_name)
|
49
|
+
method_name == :array! && json_receiver?(receiver)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check if arguments contain partial key in hash
|
53
|
+
# @param hash_node [Parser::AST::Node] Hash node to check
|
54
|
+
# @return [Boolean] True if hash contains partial key
|
55
|
+
def has_partial_key?(hash_node)
|
56
|
+
return false unless hash_node.type == :hash
|
57
|
+
|
58
|
+
hash_node.children.any? do |pair|
|
59
|
+
next false unless pair.type == :pair
|
60
|
+
|
61
|
+
key, _value = pair.children
|
62
|
+
key.type == :sym && key.children.first == :partial
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if array call has collection argument
|
67
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
68
|
+
# @return [Boolean] True if array has collection
|
69
|
+
def has_collection?(args)
|
70
|
+
args.any? && !args.first.type == :hash
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check if array call has options hash
|
74
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
75
|
+
# @return [Boolean] True if array has options
|
76
|
+
def has_options?(args)
|
77
|
+
args.any? { |arg| arg.type == :hash }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Extract collection from array arguments
|
81
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
82
|
+
# @return [Parser::AST::Node, nil] Collection node
|
83
|
+
def extract_collection(args)
|
84
|
+
args.find { |arg| arg.type != :hash }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Extract options hash from array arguments
|
88
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
89
|
+
# @return [Parser::AST::Node, nil] Options hash node
|
90
|
+
def extract_options(args)
|
91
|
+
args.find { |arg| arg.type == :hash }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Check if array call uses partial rendering
|
95
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
96
|
+
# @return [Boolean] True if array uses partial
|
97
|
+
def uses_partial?(args)
|
98
|
+
options = extract_options(args)
|
99
|
+
options && has_partial_key?(options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
4
|
+
# Base class for all call detectors
|
5
|
+
# Provides common interface and utility methods for detecting Jbuilder method calls
|
6
|
+
class BaseDetector
|
7
|
+
class << self
|
8
|
+
# Check if a method call matches this detector's patterns
|
9
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver node
|
10
|
+
# @param method_name [Symbol] Method name
|
11
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
12
|
+
# @return [Boolean] True if this detector handles the method call
|
13
|
+
def handles?(_receiver, _method_name, _args = [])
|
14
|
+
raise NotImplementedError, "#{self} must implement #handles?"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Get the detection priority for this detector
|
18
|
+
# Higher priority detectors are checked first
|
19
|
+
# @return [Integer] Priority value (higher = more priority)
|
20
|
+
def priority
|
21
|
+
0
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the category of this detector for organization
|
25
|
+
# @return [Symbol] Detector category
|
26
|
+
def category
|
27
|
+
:general
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get human-readable description of what this detector handles
|
31
|
+
# @return [String] Description
|
32
|
+
def description
|
33
|
+
"Base detector"
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
# Check if receiver is json object
|
39
|
+
# @param receiver [Parser::AST::Node, nil] Receiver node
|
40
|
+
# @return [Boolean] True if receiver is json
|
41
|
+
def json_receiver?(receiver)
|
42
|
+
# Handle implicit json calls (receiver is nil in top-level context)
|
43
|
+
return true if receiver.nil?
|
44
|
+
|
45
|
+
# Handle explicit json calls (json.property_name)
|
46
|
+
return true if receiver.type == :send && receiver.children[0].nil? && receiver.children[1] == :json
|
47
|
+
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
# Check if method name matches any of the given patterns
|
52
|
+
# @param method_name [Symbol, String] Method name to check
|
53
|
+
# @param patterns [Array<Symbol, String, Regexp>] Patterns to match against
|
54
|
+
# @return [Boolean] True if method name matches any pattern
|
55
|
+
def method_matches?(method_name, patterns)
|
56
|
+
patterns.any? do |pattern|
|
57
|
+
case pattern
|
58
|
+
when Symbol, String
|
59
|
+
method_name.to_sym == pattern.to_sym
|
60
|
+
when Regexp
|
61
|
+
pattern.match?(method_name.to_s)
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Check if arguments contain a hash with specific keys
|
69
|
+
# @param args [Array<Parser::AST::Node>] Arguments to check
|
70
|
+
# @param keys [Array<Symbol>] Keys to look for
|
71
|
+
# @return [Boolean] True if hash argument contains any of the keys
|
72
|
+
def args_contain_hash_with_keys?(args, keys)
|
73
|
+
args.any? do |arg|
|
74
|
+
next false unless arg.type == :hash
|
75
|
+
|
76
|
+
hash_keys = arg.children.map do |pair|
|
77
|
+
key_node = pair.children.first
|
78
|
+
key_node.type == :sym ? key_node.children.first : nil
|
79
|
+
end.compact
|
80
|
+
|
81
|
+
(keys & hash_keys).any?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Extract string value from a node
|
86
|
+
# @param node [Parser::AST::Node] Node to extract value from
|
87
|
+
# @return [String, nil] String value or nil
|
88
|
+
def extract_string_value(node)
|
89
|
+
return nil unless node
|
90
|
+
|
91
|
+
case node.type
|
92
|
+
when :str
|
93
|
+
node.children.first
|
94
|
+
when :sym
|
95
|
+
node.children.first.to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Check if node represents a literal value
|
100
|
+
# @param node [Parser::AST::Node] Node to check
|
101
|
+
# @return [Boolean] True if node is a literal
|
102
|
+
def literal_node?(node)
|
103
|
+
%i[str int float true false nil sym].include?(node.type)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_detector'
|
4
|
+
|
5
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
6
|
+
# Detects cache calls (json.cache!, json.cache_if!)
|
7
|
+
# Handles caching directives in Jbuilder templates
|
8
|
+
class CacheCallDetector < BaseDetector
|
9
|
+
# Cache methods that this detector handles
|
10
|
+
CACHE_METHODS = %w[cache! cache_if!].freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Check if this detector handles the method call
|
14
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver node
|
15
|
+
# @param method_name [Symbol] Method name
|
16
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
17
|
+
# @return [Boolean] True if this is a cache call
|
18
|
+
def handles?(receiver, method_name, _args = [])
|
19
|
+
cache_call?(receiver, method_name) || cache_if_call?(receiver, method_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Medium priority - caching doesn't affect schema structure
|
23
|
+
# @return [Integer] Priority value
|
24
|
+
def priority
|
25
|
+
5
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Symbol] Detector category
|
29
|
+
def category
|
30
|
+
:meta
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String] Description
|
34
|
+
def description
|
35
|
+
"Cache calls (json.cache!, json.cache_if!)"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Check if this is a cache call
|
39
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver
|
40
|
+
# @param method_name [Symbol] Method name
|
41
|
+
# @return [Boolean] True if this is a cache call
|
42
|
+
def cache_call?(receiver, method_name)
|
43
|
+
method_name == :cache! && json_receiver?(receiver)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if this is a conditional cache call
|
47
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver
|
48
|
+
# @param method_name [Symbol] Method name
|
49
|
+
# @return [Boolean] True if this is a cache_if call
|
50
|
+
def cache_if_call?(receiver, method_name)
|
51
|
+
method_name == :cache_if! && json_receiver?(receiver)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check if method is any cache-related method
|
55
|
+
# @param method_name [Symbol] Method name
|
56
|
+
# @return [Boolean] True if method is cache-related
|
57
|
+
def cache_method?(method_name)
|
58
|
+
CACHE_METHODS.include?(method_name.to_s)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Extract cache key from arguments
|
62
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
63
|
+
# @return [String, nil] Cache key
|
64
|
+
def extract_cache_key(args)
|
65
|
+
return nil if args.empty?
|
66
|
+
|
67
|
+
first_arg = args.first
|
68
|
+
extract_string_value(first_arg)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Extract cache condition from cache_if! arguments
|
72
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
73
|
+
# @return [Parser::AST::Node, nil] Condition node
|
74
|
+
def extract_cache_condition(args)
|
75
|
+
return nil if args.length < 2
|
76
|
+
|
77
|
+
args.first # First argument is the condition
|
78
|
+
end
|
79
|
+
|
80
|
+
# Check if cache call has block
|
81
|
+
# @param node [Parser::AST::Node] The cache call node
|
82
|
+
# @return [Boolean] True if cache has block
|
83
|
+
def has_block?(node)
|
84
|
+
node.type == :block
|
85
|
+
end
|
86
|
+
|
87
|
+
# Extract cache options from arguments
|
88
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
89
|
+
# @return [Hash] Cache options
|
90
|
+
def extract_cache_options(args)
|
91
|
+
options = {}
|
92
|
+
|
93
|
+
args.each do |arg|
|
94
|
+
next unless arg.type == :hash
|
95
|
+
|
96
|
+
arg.children.each do |pair|
|
97
|
+
next unless pair.type == :pair
|
98
|
+
|
99
|
+
key_node, value_node = pair.children
|
100
|
+
next unless key_node.type == :sym
|
101
|
+
|
102
|
+
key = key_node.children.first
|
103
|
+
value = extract_string_value(value_node) || value_node
|
104
|
+
options[key] = value
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
options
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_detector'
|
4
|
+
|
5
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
6
|
+
# Detects JSON property calls (json.property_name)
|
7
|
+
# Handles the most common Jbuilder pattern for setting properties
|
8
|
+
class JsonCallDetector < BaseDetector
|
9
|
+
# JSON methods that should not be treated as properties
|
10
|
+
SPECIAL_METHODS = %w[array! partial! cache! cache_if! key_format! null! ignore_nil!].freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Check if this detector handles the method call
|
14
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver node
|
15
|
+
# @param method_name [Symbol] Method name
|
16
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
17
|
+
# @return [Boolean] True if this is a JSON property call
|
18
|
+
def handles?(receiver, method_name, _args = [])
|
19
|
+
json_property?(receiver, method_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Higher priority since this is the most common case
|
23
|
+
# @return [Integer] Priority value
|
24
|
+
def priority
|
25
|
+
10
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Symbol] Detector category
|
29
|
+
def category
|
30
|
+
:property
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String] Description
|
34
|
+
def description
|
35
|
+
"JSON property calls (json.property_name)"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Checks if this is a json property call (json.property_name)
|
39
|
+
# @param receiver [Parser::AST::Node, nil] The receiver of the method call
|
40
|
+
# @param method_name [Symbol] The method name being called
|
41
|
+
# @return [Boolean] True if this is a json property call
|
42
|
+
def json_property?(receiver, method_name)
|
43
|
+
return false if method_name.nil?
|
44
|
+
return false if SPECIAL_METHODS.include?(method_name.to_s)
|
45
|
+
|
46
|
+
# Don't handle bare "json" calls - these are receivers, not properties
|
47
|
+
return false if receiver.nil? && method_name == :json
|
48
|
+
|
49
|
+
# Must be called on json (implicit or explicit)
|
50
|
+
json_receiver?(receiver)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks if this is an array-related call (json.array!)
|
54
|
+
# @param receiver [Parser::AST::Node, nil] The receiver of the method call
|
55
|
+
# @param method_name [Symbol] The method name being called
|
56
|
+
# @return [Boolean] True if this is an array call
|
57
|
+
def array_call?(receiver, method_name)
|
58
|
+
method_name == :array! && json_receiver?(receiver)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks if this is a partial call (json.partial!)
|
62
|
+
# @param receiver [Parser::AST::Node, nil] The receiver of the method call
|
63
|
+
# @param method_name [Symbol] The method name being called
|
64
|
+
# @return [Boolean] True if this is a partial call
|
65
|
+
def partial_call?(receiver, method_name)
|
66
|
+
method_name == :partial! && json_receiver?(receiver)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if method call represents a simple property assignment
|
70
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver
|
71
|
+
# @param method_name [Symbol] Method name
|
72
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
73
|
+
# @return [Boolean] True if this is a simple property assignment
|
74
|
+
def simple_property?(receiver, method_name, args)
|
75
|
+
return false unless json_property?(receiver, method_name)
|
76
|
+
|
77
|
+
# Simple property has exactly one argument and no block
|
78
|
+
args.length == 1
|
79
|
+
end
|
80
|
+
|
81
|
+
# Check if method call represents a property with block (object)
|
82
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver
|
83
|
+
# @param method_name [Symbol] Method name
|
84
|
+
# @param has_block [Boolean] Whether the call has a block
|
85
|
+
# @return [Boolean] True if this is a property with block
|
86
|
+
def property_with_block?(receiver, method_name, has_block)
|
87
|
+
json_property?(receiver, method_name) && has_block
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
4
|
+
class KeyFormatDetector
|
5
|
+
class << self
|
6
|
+
# Detects if node represents a key formatting method
|
7
|
+
# @param receiver [Parser::AST::Node] Receiver node
|
8
|
+
# @param method_name [Symbol] Method name
|
9
|
+
# @return [Boolean] True if key format method
|
10
|
+
def key_format?(receiver, method_name)
|
11
|
+
return false unless json_receiver?(receiver)
|
12
|
+
|
13
|
+
key_format_methods = %i[key_format! deep_format_keys!]
|
14
|
+
key_format_methods.include?(method_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Checks if receiver is a json object
|
20
|
+
# @param receiver [Parser::AST::Node] Receiver node
|
21
|
+
# @return [Boolean] True if json receiver
|
22
|
+
def json_receiver?(receiver)
|
23
|
+
receiver && receiver.type == :send && receiver.children[1] == :json
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
4
|
+
class NullHandlingDetector
|
5
|
+
class << self
|
6
|
+
# Detects if node represents a null/nil handling method
|
7
|
+
# @param receiver [Parser::AST::Node] Receiver node
|
8
|
+
# @param method_name [Symbol] Method name
|
9
|
+
# @return [Boolean] True if null handling method
|
10
|
+
def null_handling?(receiver, method_name)
|
11
|
+
return false unless json_receiver?(receiver)
|
12
|
+
|
13
|
+
null_handling_methods = %i[ignore_nil! nil! null!]
|
14
|
+
null_handling_methods.include?(method_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Checks if receiver is a json object
|
20
|
+
# @param receiver [Parser::AST::Node] Receiver node
|
21
|
+
# @return [Boolean] True if json receiver
|
22
|
+
def json_receiver?(receiver)
|
23
|
+
receiver && receiver.type == :send && receiver.children[1] == :json
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
4
|
+
class ObjectManipulationDetector
|
5
|
+
class << self
|
6
|
+
# Detects if node represents an object manipulation method
|
7
|
+
# @param receiver [Parser::AST::Node] Receiver node
|
8
|
+
# @param method_name [Symbol] Method name
|
9
|
+
# @return [Boolean] True if object manipulation method
|
10
|
+
def object_manipulation?(receiver, method_name)
|
11
|
+
return false unless json_receiver?(receiver)
|
12
|
+
|
13
|
+
object_manipulation_methods = %i[merge! set! child! cache_root!]
|
14
|
+
object_manipulation_methods.include?(method_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Checks if receiver is a json object
|
20
|
+
# @param receiver [Parser::AST::Node] Receiver node
|
21
|
+
# @return [Boolean] True if json receiver
|
22
|
+
def json_receiver?(receiver)
|
23
|
+
receiver && receiver.type == :send && receiver.children[1] == :json
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_detector'
|
4
|
+
|
5
|
+
module RailsOpenapiGen::Parsers::Jbuilder::CallDetectors
|
6
|
+
# Detects partial calls (json.partial!)
|
7
|
+
# Handles partial template rendering in Jbuilder
|
8
|
+
class PartialCallDetector < BaseDetector
|
9
|
+
class << self
|
10
|
+
# Check if this detector handles the method call
|
11
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver node
|
12
|
+
# @param method_name [Symbol] Method name
|
13
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
14
|
+
# @return [Boolean] True if this is a partial call
|
15
|
+
def handles?(receiver, method_name, _args = [])
|
16
|
+
partial_call?(receiver, method_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
# High priority for partial calls
|
20
|
+
# @return [Integer] Priority value
|
21
|
+
def priority
|
22
|
+
20
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Symbol] Detector category
|
26
|
+
def category
|
27
|
+
:partial
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String] Description
|
31
|
+
def description
|
32
|
+
"Partial calls (json.partial!)"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check if this is a partial call
|
36
|
+
# @param receiver [Parser::AST::Node, nil] Method receiver
|
37
|
+
# @param method_name [Symbol] Method name
|
38
|
+
# @return [Boolean] True if this is a partial call
|
39
|
+
def partial_call?(receiver, method_name)
|
40
|
+
method_name == :partial! && json_receiver?(receiver)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Extract partial path from arguments
|
44
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
45
|
+
# @return [String, nil] Partial path
|
46
|
+
def extract_partial_path(args)
|
47
|
+
return nil if args.empty?
|
48
|
+
|
49
|
+
first_arg = args.first
|
50
|
+
return extract_string_value(first_arg) if literal_node?(first_arg)
|
51
|
+
|
52
|
+
# Check if it's in a hash under :partial key
|
53
|
+
if first_arg.type == :hash
|
54
|
+
partial_pair = first_arg.children.find do |pair|
|
55
|
+
key_node = pair.children.first
|
56
|
+
key_node.type == :sym && key_node.children.first == :partial
|
57
|
+
end
|
58
|
+
|
59
|
+
if partial_pair
|
60
|
+
value_node = partial_pair.children.last
|
61
|
+
return extract_string_value(value_node)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Extract local variables from arguments
|
69
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
70
|
+
# @return [Hash] Local variables hash
|
71
|
+
def extract_locals(args)
|
72
|
+
locals = {}
|
73
|
+
|
74
|
+
args.each do |arg|
|
75
|
+
next unless arg.type == :hash
|
76
|
+
|
77
|
+
arg.children.each do |pair|
|
78
|
+
next unless pair.type == :pair
|
79
|
+
|
80
|
+
key_node, value_node = pair.children
|
81
|
+
next unless key_node.type == :sym
|
82
|
+
|
83
|
+
key = key_node.children.first
|
84
|
+
next if key == :partial # Skip partial path
|
85
|
+
|
86
|
+
# For now, just store the key, value extraction would need evaluation
|
87
|
+
locals[key] = value_node
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
locals
|
92
|
+
end
|
93
|
+
|
94
|
+
# Check if partial has locals
|
95
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
96
|
+
# @return [Boolean] True if partial has locals
|
97
|
+
def has_locals?(args)
|
98
|
+
!extract_locals(args).empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
# Check if partial path is a string literal
|
102
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
103
|
+
# @return [Boolean] True if path is literal
|
104
|
+
def literal_path?(args)
|
105
|
+
return false if args.empty?
|
106
|
+
|
107
|
+
literal_node?(args.first)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Check if partial is called with collection
|
111
|
+
# @param args [Array<Parser::AST::Node>] Method arguments
|
112
|
+
# @return [Boolean] True if partial has collection
|
113
|
+
def has_collection?(args)
|
114
|
+
args.any? do |arg|
|
115
|
+
next false unless arg.type == :hash
|
116
|
+
|
117
|
+
arg.children.any? do |pair|
|
118
|
+
key_node = pair.children.first
|
119
|
+
key_node.type == :sym && key_node.children.first == :collection
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|