oas_rails 1.3.4 → 1.3.5
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/lib/oas_rails/builders/oas_route_builder.rb +105 -6
- data/lib/oas_rails/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz: '
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09529237dfb6748e3a3f1d61722d8ac67643bc8d7b98c24ba4a78d20a9c44dee'
|
|
4
|
+
data.tar.gz: 5a81b05b2b1bfd73739a88edbbbc7173e1b16d2e34a5a011c6b3d7e201b270ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c97e67a13e1a8bde1d9d6f9ad08834734ad2211455b9ba100a8eec030561133c77494020705a941bb118fb2578b9d0a739d3d41c80f4d09eb5811c6d79830550
|
|
7
|
+
data.tar.gz: b3bafc5f8160f61e4c5fd427a9571426d5e7b9c5cc251d5984addef3547de1eb79f43bb0081795a25cbfa8ea6e0fbb67f2cdebbe4c48d045b71f58d1e86fa2e8
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
module OasRails
|
|
2
2
|
module Builders
|
|
3
3
|
class OasRouteBuilder
|
|
4
|
+
# Gems/frameworks (like Sorbet) may wrap methods at runtime, causing
|
|
5
|
+
# +instance_method.source_location+ to point to the wrapper rather than
|
|
6
|
+
# the original source file. We detect this and fall back to reading the
|
|
7
|
+
# source file directly so that OasRails annotations remain readable.
|
|
8
|
+
KNOWN_RUNTIME_WRAPPERS = %w[sorbet-runtime].freeze
|
|
9
|
+
|
|
4
10
|
def self.build_from_rails_route(rails_route)
|
|
5
11
|
new(rails_route).build
|
|
6
12
|
end
|
|
@@ -48,7 +54,8 @@ module OasRails
|
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
def docstring
|
|
51
|
-
|
|
57
|
+
comment = method_comment_safe
|
|
58
|
+
comment_lines = comment.lines
|
|
52
59
|
processed_lines = comment_lines.map { |line| line.sub(/^# /, '') }
|
|
53
60
|
|
|
54
61
|
filtered_lines = processed_lines.reject do |line|
|
|
@@ -60,11 +67,8 @@ module OasRails
|
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
def tags
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
method_tags = parse_tags(method_comment)
|
|
67
|
-
class_tags = parse_tags(class_comment)
|
|
70
|
+
method_tags = parse_tags(method_comment_safe)
|
|
71
|
+
class_tags = parse_tags(class_comment_safe)
|
|
68
72
|
|
|
69
73
|
method_tags + class_tags
|
|
70
74
|
end
|
|
@@ -73,6 +77,101 @@ module OasRails
|
|
|
73
77
|
lines = comment.lines.map { |line| line.sub(/^# /, '') }
|
|
74
78
|
::YARD::Docstring.parser.parse(lines.join).tags
|
|
75
79
|
end
|
|
80
|
+
|
|
81
|
+
# Returns the method-level comment, falling back to direct source-file
|
|
82
|
+
# reading when the method has been wrapped by a runtime library
|
|
83
|
+
# (e.g. Sorbet's +sig+ blocks replace the method at runtime, causing
|
|
84
|
+
# +instance_method.source_location+ to point to sorbet-runtime internals
|
|
85
|
+
# rather than the controller file).
|
|
86
|
+
def method_comment_safe
|
|
87
|
+
unbound = controller_class.constantize.instance_method(method)
|
|
88
|
+
return unbound.comment unless wrapped_by_runtime?(unbound)
|
|
89
|
+
|
|
90
|
+
read_comment_from_source(method)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def class_comment_safe
|
|
94
|
+
unbound = controller_class.constantize.instance_method(method)
|
|
95
|
+
return unbound.class_comment unless wrapped_by_runtime?(unbound)
|
|
96
|
+
|
|
97
|
+
read_comment_from_source(:class)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns true when the unbound method's source_location points to a
|
|
101
|
+
# known runtime wrapper gem rather than the application source.
|
|
102
|
+
def wrapped_by_runtime?(unbound_method)
|
|
103
|
+
loc = unbound_method.source_location
|
|
104
|
+
return false unless loc
|
|
105
|
+
|
|
106
|
+
KNOWN_RUNTIME_WRAPPERS.any? { |wrapper| loc.first.include?(wrapper) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Reads the contiguous comment block above a +def+ or +class+ line in
|
|
110
|
+
# the controller source file, skipping over non-comment lines such as
|
|
111
|
+
# +sig { void }+ injected by Sorbet.
|
|
112
|
+
#
|
|
113
|
+
# When +target+ is +:class+, finds the class declaration line.
|
|
114
|
+
# Otherwise, finds +def <target>+.
|
|
115
|
+
def read_comment_from_source(target)
|
|
116
|
+
source_file = find_source_file
|
|
117
|
+
return "" unless source_file
|
|
118
|
+
|
|
119
|
+
lines = File.readlines(source_file)
|
|
120
|
+
target_idx = if target == :class
|
|
121
|
+
lines.index { |l| l.match?(/^\s*class\s+\w+/) }
|
|
122
|
+
else
|
|
123
|
+
lines.index { |l| l.match?(/^\s+def #{Regexp.escape(target.to_s)}\b/) }
|
|
124
|
+
end
|
|
125
|
+
return "" unless target_idx
|
|
126
|
+
|
|
127
|
+
collect_comments_before(lines, target_idx)
|
|
128
|
+
rescue StandardError
|
|
129
|
+
""
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Walk backwards from +target_idx+ collecting contiguous comment lines,
|
|
133
|
+
# skipping blank lines and non-comment lines (like +sig { void }+).
|
|
134
|
+
# Leading whitespace is stripped so output matches method_source format.
|
|
135
|
+
def collect_comments_before(lines, target_idx)
|
|
136
|
+
comment_lines = []
|
|
137
|
+
idx = target_idx - 1
|
|
138
|
+
|
|
139
|
+
while idx >= 0
|
|
140
|
+
stripped = lines[idx].strip
|
|
141
|
+
if stripped.start_with?("#")
|
|
142
|
+
comment_lines.unshift("#{stripped}\n")
|
|
143
|
+
elsif stripped.empty? || stripped.match?(/\Asig\s*[{(]/)
|
|
144
|
+
# skip blank lines and Sorbet sig blocks
|
|
145
|
+
else
|
|
146
|
+
break
|
|
147
|
+
end
|
|
148
|
+
idx -= 1
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
comment_lines.join
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Attempts to locate the controller source file by scanning the Rails
|
|
155
|
+
# autoload paths, since +source_location+ is unavailable when wrapped.
|
|
156
|
+
def find_source_file
|
|
157
|
+
# Try using ancestors with valid source locations first
|
|
158
|
+
klass = controller_class.constantize
|
|
159
|
+
klass.instance_methods(false).each do |m|
|
|
160
|
+
loc = klass.instance_method(m).source_location
|
|
161
|
+
next unless loc && !wrapped_by_runtime?(klass.instance_method(m))
|
|
162
|
+
|
|
163
|
+
return loc.first
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Fall back to searching autoload paths for the expected filename
|
|
167
|
+
expected_path = "#{controller}_controller.rb"
|
|
168
|
+
Rails.autoloaders.main.dirs.each do |dir|
|
|
169
|
+
candidate = File.join(dir, expected_path)
|
|
170
|
+
return candidate if File.exist?(candidate)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
76
175
|
end
|
|
77
176
|
end
|
|
78
177
|
end
|
data/lib/oas_rails/version.rb
CHANGED