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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '084f6ab46fcab4522ee4a647d510183b5c7d1dc5ddd4a78bdd808208cbde3fc0'
4
- data.tar.gz: ea44520a949dd1644ab77a85e0d9c03cb359f486ec13e0911cd97a6c027e4a4f
3
+ metadata.gz: '09529237dfb6748e3a3f1d61722d8ac67643bc8d7b98c24ba4a78d20a9c44dee'
4
+ data.tar.gz: 5a81b05b2b1bfd73739a88edbbbc7173e1b16d2e34a5a011c6b3d7e201b270ee
5
5
  SHA512:
6
- metadata.gz: 490456ca0185bb3c7024ec098f9f036ee2aa5f59f5ae3e39c05e12644dd21eb1cc7a751894e8bbc215e95bdab3ba24a82179ba6220ca31fa6de57c37b6b3583a
7
- data.tar.gz: 952a4d1eecca40e356e72306bf7c6a7184d266364472866274f5660eed0169c23b1ea82993a861a0f95acb6651f5fcf956afb1ca5cdbd6807530fc5a4d66295a
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
- comment_lines = controller_class.constantize.instance_method(method).comment.lines
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
- method_comment = controller_class.constantize.instance_method(method).comment
64
- class_comment = controller_class.constantize.instance_method(method).class_comment
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
@@ -1,3 +1,3 @@
1
1
  module OasRails
2
- VERSION = "1.3.4"
2
+ VERSION = "1.3.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oas_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.4
4
+ version: 1.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - a-chacon