sorbet_view 0.17.2 → 0.19.0
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/sorbet_view/compiler/template_context.rb +123 -9
- data/lib/sorbet_view/version.rb +1 -1
- data/lib/tapioca/dsl/compilers/sorbet_view.rb +131 -32
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4a6dcbfa73d97af80ddfc038337a094df6adcfe3be2cdb95997e2eb6304993e
|
|
4
|
+
data.tar.gz: 5d96c0eabf3534f72abebb91738f22546f157266368d7f92cf6faf6ac38ce44e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b6781e594f6fbcb73e9b7a62c9931c97c5f8cbf5375530dff6a6e4a35c7fce73e2333d586aa81e079c32d92b19c59b5b493abe27b0e638ca24e6bc9654d7987d
|
|
7
|
+
data.tar.gz: 7a026a08b7eb1ebddbb586799095a9de5c39159e26886bc3b0ad2c11ce8382b28bc57c79ef7768c0b929cd3c4083c49663fa11a0b7d81aeb8777b0ce3c3ebd4a
|
|
@@ -51,6 +51,8 @@ module SorbetView
|
|
|
51
51
|
class << self
|
|
52
52
|
extend T::Sig
|
|
53
53
|
|
|
54
|
+
@all_concrete_controllers = T.let(nil, T.nilable(T::Array[T.untyped]))
|
|
55
|
+
|
|
54
56
|
private
|
|
55
57
|
|
|
56
58
|
# Strip the matching input_dir prefix from a template path
|
|
@@ -93,15 +95,23 @@ module SorbetView
|
|
|
93
95
|
sig { params(path: String, config: Configuration).returns(String) }
|
|
94
96
|
def path_to_class_name(path, config)
|
|
95
97
|
relative = strip_input_dir(path, config)
|
|
96
|
-
|
|
98
|
+
filename = File.basename(relative)
|
|
99
|
+
filename_parts = filename.split('.')
|
|
100
|
+
|
|
101
|
+
basename = T.must(filename_parts.first)
|
|
97
102
|
basename = basename.delete_prefix('_') # strip partial prefix
|
|
103
|
+
|
|
104
|
+
# With 3+ parts (e.g. show.html.erb → [show, html, erb]), the last is the
|
|
105
|
+
# template handler and the middle parts are format/variant extensions.
|
|
106
|
+
# Nest them as sub-classes so show.html.erb and show.turbo_stream.erb
|
|
107
|
+
# don't collide as the same class name.
|
|
108
|
+
format_parts = filename_parts.length >= 3 ? T.must(filename_parts[1..-2]) : []
|
|
109
|
+
|
|
98
110
|
dir = File.dirname(relative)
|
|
99
111
|
|
|
100
|
-
parts =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
dir.split('/') + [basename]
|
|
104
|
-
end
|
|
112
|
+
parts = dir == '.' ? [basename] : dir.split('/') + [basename]
|
|
113
|
+
|
|
114
|
+
parts += format_parts
|
|
105
115
|
|
|
106
116
|
parts.map { |p| camelize(p) }.join('::')
|
|
107
117
|
end
|
|
@@ -113,11 +123,12 @@ module SorbetView
|
|
|
113
123
|
|
|
114
124
|
sig { params(path: String, ruby_path: String, config: Configuration).returns(TemplateContext) }
|
|
115
125
|
def resolve_controller_view(path, ruby_path, config)
|
|
126
|
+
helper_includes = resolve_runtime_helper_includes(path, config)
|
|
116
127
|
new(
|
|
117
128
|
class_name: "SorbetView::Generated::#{path_to_class_name(path, config)}",
|
|
118
129
|
superclass: '::ActionView::Base',
|
|
119
130
|
includes: [
|
|
120
|
-
|
|
131
|
+
*helper_includes,
|
|
121
132
|
*config.extra_includes
|
|
122
133
|
],
|
|
123
134
|
template_path: path,
|
|
@@ -141,11 +152,12 @@ module SorbetView
|
|
|
141
152
|
|
|
142
153
|
sig { params(path: String, ruby_path: String, config: Configuration).returns(TemplateContext) }
|
|
143
154
|
def resolve_layout(path, ruby_path, config)
|
|
155
|
+
helper_includes = resolve_runtime_helper_includes(path, config)
|
|
144
156
|
new(
|
|
145
157
|
class_name: "SorbetView::Generated::#{path_to_class_name(path, config)}",
|
|
146
158
|
superclass: '::ActionView::Base',
|
|
147
159
|
includes: [
|
|
148
|
-
|
|
160
|
+
*helper_includes,
|
|
149
161
|
*config.extra_includes
|
|
150
162
|
],
|
|
151
163
|
template_path: path,
|
|
@@ -155,11 +167,12 @@ module SorbetView
|
|
|
155
167
|
|
|
156
168
|
sig { params(path: String, ruby_path: String, config: Configuration).returns(TemplateContext) }
|
|
157
169
|
def resolve_partial(path, ruby_path, config)
|
|
170
|
+
helper_includes = resolve_runtime_helper_includes(path, config)
|
|
158
171
|
new(
|
|
159
172
|
class_name: "SorbetView::Generated::#{path_to_class_name(path, config)}",
|
|
160
173
|
superclass: '::ActionView::Base',
|
|
161
174
|
includes: [
|
|
162
|
-
|
|
175
|
+
*helper_includes,
|
|
163
176
|
*config.extra_includes
|
|
164
177
|
],
|
|
165
178
|
template_path: path,
|
|
@@ -177,6 +190,107 @@ module SorbetView
|
|
|
177
190
|
ruby_path: ruby_path
|
|
178
191
|
)
|
|
179
192
|
end
|
|
193
|
+
|
|
194
|
+
sig { params(path: String, config: Configuration).returns(T::Array[String]) }
|
|
195
|
+
def resolve_runtime_helper_includes(path, config)
|
|
196
|
+
return ['::ApplicationController::HelperMethods'] unless defined?(::ActionController::Base)
|
|
197
|
+
|
|
198
|
+
relative = strip_input_dir(path, config)
|
|
199
|
+
names = []
|
|
200
|
+
if relative.start_with?('layouts/')
|
|
201
|
+
names = helper_modules_for_layout(relative)
|
|
202
|
+
elsif File.basename(relative).start_with?('_')
|
|
203
|
+
names = helper_modules_for_partial(relative)
|
|
204
|
+
else
|
|
205
|
+
names = helper_modules_for_controller_view(relative)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
return ['::ApplicationController::HelperMethods'] if names.empty?
|
|
209
|
+
|
|
210
|
+
names.map { |name| "::#{name}" }
|
|
211
|
+
rescue StandardError => e
|
|
212
|
+
$stderr.puts "[SorbetView] resolve_runtime_helper_includes failed: #{e.class}: #{e.message}"
|
|
213
|
+
['::ApplicationController::HelperMethods']
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
sig { params(relative: String).returns(T::Array[String]) }
|
|
217
|
+
def helper_modules_for_controller_view(relative)
|
|
218
|
+
controller_path = File.dirname(relative)
|
|
219
|
+
return [] if controller_path.nil? || controller_path.empty? || controller_path == '.'
|
|
220
|
+
|
|
221
|
+
controller = find_controller_by_path(controller_path)
|
|
222
|
+
helper_module_names_for(controller)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
sig { params(relative: String).returns(T::Array[String]) }
|
|
226
|
+
def helper_modules_for_layout(relative)
|
|
227
|
+
filename = File.basename(relative)
|
|
228
|
+
layout_name = T.must(filename.split('.').first).to_s
|
|
229
|
+
return [] if layout_name.empty?
|
|
230
|
+
|
|
231
|
+
all_concrete_controllers.flat_map do |controller|
|
|
232
|
+
next [] unless controller.respond_to?(:_layout)
|
|
233
|
+
next [] unless controller._layout == layout_name
|
|
234
|
+
|
|
235
|
+
helper_module_names_for(controller)
|
|
236
|
+
end.uniq.sort
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
sig { params(relative: String).returns(T::Array[String]) }
|
|
240
|
+
def helper_modules_for_partial(relative)
|
|
241
|
+
dir = File.dirname(relative)
|
|
242
|
+
return [] if dir.nil? || dir.empty? || dir == '.'
|
|
243
|
+
|
|
244
|
+
# Partials can live under subdirectories that share a parent controller
|
|
245
|
+
# (e.g. users/forms/_field.html.erb should use UsersController helpers).
|
|
246
|
+
parts = dir.split('/')
|
|
247
|
+
candidates = parts.length.downto(1).map { |i| parts.first(i).join('/') }
|
|
248
|
+
|
|
249
|
+
candidates.each do |controller_path|
|
|
250
|
+
controller = find_controller_by_path(controller_path)
|
|
251
|
+
names = helper_module_names_for(controller)
|
|
252
|
+
return names unless names.empty?
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
[]
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
sig { params(controller_path: String).returns(T.untyped) }
|
|
259
|
+
def find_controller_by_path(controller_path)
|
|
260
|
+
all_concrete_controllers.find do |controller|
|
|
261
|
+
controller.respond_to?(:controller_path) && controller.controller_path == controller_path
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
sig { returns(T::Array[T.untyped]) }
|
|
266
|
+
def all_concrete_controllers
|
|
267
|
+
controllers = @all_concrete_controllers
|
|
268
|
+
return controllers if controllers
|
|
269
|
+
|
|
270
|
+
controllers = ObjectSpace.each_object(Class).select do |klass|
|
|
271
|
+
klass < ::ActionController::Base && !klass.abstract?
|
|
272
|
+
rescue StandardError
|
|
273
|
+
false
|
|
274
|
+
end.sort_by { |klass| (klass.name || "AnonymousController:#{klass.object_id}").to_s }
|
|
275
|
+
|
|
276
|
+
@all_concrete_controllers = controllers
|
|
277
|
+
controllers
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
sig { params(controller: T.untyped).returns(T::Array[String]) }
|
|
281
|
+
def helper_module_names_for(controller)
|
|
282
|
+
return [] unless controller
|
|
283
|
+
return [] unless controller.respond_to?(:_helpers)
|
|
284
|
+
|
|
285
|
+
# Use the actual runtime helper chain for this controller.
|
|
286
|
+
controller._helpers.ancestors.filter_map do |mod|
|
|
287
|
+
next unless mod.is_a?(Module) && !mod.is_a?(Class)
|
|
288
|
+
name = mod.name
|
|
289
|
+
next if name.nil? || name.empty?
|
|
290
|
+
next if name.start_with?('ActionController::') || name.start_with?('AbstractController::')
|
|
291
|
+
name
|
|
292
|
+
end.uniq
|
|
293
|
+
end
|
|
180
294
|
end
|
|
181
295
|
end
|
|
182
296
|
end
|
data/lib/sorbet_view/version.rb
CHANGED
|
@@ -26,6 +26,7 @@ module Tapioca
|
|
|
26
26
|
sig { override.void }
|
|
27
27
|
def decorate
|
|
28
28
|
@module_cache = T.let({}, T::Hash[String, RBI::Scope])
|
|
29
|
+
@layout_contributions = T.let({}, T::Hash[String, T::Array[T::Hash[Symbol, T.untyped]]])
|
|
29
30
|
@project = T.let(SrbLens::Project.load_or_index(Dir.pwd), T.untyped)
|
|
30
31
|
|
|
31
32
|
# Ensure all controllers are loaded
|
|
@@ -37,10 +38,17 @@ module Tapioca
|
|
|
37
38
|
false
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
# Deterministic order: ObjectSpace has no order guarantee, and "last controller wins" for
|
|
42
|
+
# shared layout classes made RBI diffs unstable. Sort by constant name; anonymous classes
|
|
43
|
+
# fall back to object_id.
|
|
44
|
+
controllers = controllers.sort_by { |c| controller_sort_key(c) }
|
|
45
|
+
|
|
40
46
|
controllers.each do |controller|
|
|
41
47
|
process_controller(controller)
|
|
42
48
|
end
|
|
43
49
|
|
|
50
|
+
emit_merged_layout_includes
|
|
51
|
+
|
|
44
52
|
generate_ivar_mapping(controllers)
|
|
45
53
|
process_components
|
|
46
54
|
compile_all_templates
|
|
@@ -61,7 +69,7 @@ module Tapioca
|
|
|
61
69
|
controllers.each do |controller|
|
|
62
70
|
path = controller.controller_path
|
|
63
71
|
|
|
64
|
-
controller.action_methods.each do |action_name|
|
|
72
|
+
controller.action_methods.to_a.sort.each do |action_name|
|
|
65
73
|
next unless action_name.match?(/\A[a-zA-Z_][a-zA-Z0-9_]*\z/)
|
|
66
74
|
|
|
67
75
|
action_ivars = extract_ivars_from_srb_lens(controller, action_name.to_s)
|
|
@@ -120,11 +128,7 @@ module Tapioca
|
|
|
120
128
|
common_keys.each do |key|
|
|
121
129
|
type_a = T.must(a[key])
|
|
122
130
|
type_b = T.must(b[key])
|
|
123
|
-
result[key] =
|
|
124
|
-
type_a
|
|
125
|
-
else
|
|
126
|
-
"T.all(#{type_a}, #{type_b})"
|
|
127
|
-
end
|
|
131
|
+
result[key] = type_a == type_b ? type_a : "T.all(#{type_a}, #{type_b})"
|
|
128
132
|
end
|
|
129
133
|
result
|
|
130
134
|
end
|
|
@@ -213,7 +217,8 @@ module Tapioca
|
|
|
213
217
|
|
|
214
218
|
sig { params(klass: T.untyped, class_name: String).void }
|
|
215
219
|
def generate_component_rbi(klass, class_name)
|
|
216
|
-
|
|
220
|
+
# Instance method order is not guaranteed; sort for stable RBI.
|
|
221
|
+
methods = klass.instance_methods(false).sort
|
|
217
222
|
|
|
218
223
|
method_sigs = methods.filter_map do |method_name|
|
|
219
224
|
method_name_s = method_name.to_s
|
|
@@ -258,19 +263,126 @@ module Tapioca
|
|
|
258
263
|
end
|
|
259
264
|
end
|
|
260
265
|
|
|
261
|
-
# 2) Each action's template class includes the helper module and controller-specific helper modules
|
|
262
|
-
|
|
266
|
+
# 2) Each action's template class includes the helper module and controller-specific helper modules.
|
|
267
|
+
# Templates compile to format-nested classes (e.g. Users::Show::Html, Users::Show::TurboStream),
|
|
268
|
+
# so emit RBI for each format variant found on disk. Falls back to the action-base class
|
|
269
|
+
# when no format-suffixed templates exist.
|
|
270
|
+
actions = controller.action_methods.to_a.sort
|
|
263
271
|
actions.each do |action_name|
|
|
264
272
|
next unless action_name.match?(/\A[a-zA-Z_][a-zA-Z0-9_]*\z/)
|
|
265
273
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
274
|
+
base_class_name = "SorbetView::Generated::#{parts.join('::')}::#{camelize(action_name)}"
|
|
275
|
+
format_suffixes = find_template_format_suffixes(path, action_name.to_s)
|
|
276
|
+
class_names = format_suffixes.empty? ? [base_class_name] : format_suffixes.map { |fs| "#{base_class_name}::#{fs}" }
|
|
277
|
+
|
|
278
|
+
class_names.each do |class_name|
|
|
279
|
+
create_class_from_path(class_name) do |klass|
|
|
280
|
+
apply_includes_for_action!(klass, helper_module_name, controller_helper_modules)
|
|
271
281
|
end
|
|
272
282
|
end
|
|
273
283
|
end
|
|
284
|
+
|
|
285
|
+
layout_name = controller._layout if controller.respond_to?(:_layout)
|
|
286
|
+
if layout_name.is_a?(String) && !layout_name.empty?
|
|
287
|
+
base_class_name = "SorbetView::Generated::Layouts::#{camelize(layout_name)}"
|
|
288
|
+
format_suffixes = find_template_format_suffixes('layouts', layout_name)
|
|
289
|
+
class_names = format_suffixes.empty? ? [base_class_name] : format_suffixes.map { |fs| "#{base_class_name}::#{fs}" }
|
|
290
|
+
|
|
291
|
+
class_names.each do |class_name|
|
|
292
|
+
contribs = T.must(@layout_contributions)[class_name] ||= []
|
|
293
|
+
contribs << {
|
|
294
|
+
controller_name: controller_sort_key(controller),
|
|
295
|
+
helper_module_name: helper_module_name,
|
|
296
|
+
controller_helper_modules: controller_helper_modules
|
|
297
|
+
}
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Stable sort key for controllers (anonymous classes have no #name).
|
|
303
|
+
sig { params(controller: T.untyped).returns(String) }
|
|
304
|
+
def controller_sort_key(controller)
|
|
305
|
+
(controller.name || "AnonymousController:#{controller.object_id}").to_s
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Apply include lines for a single controller's view class. Order: SorbetView::Helpers
|
|
309
|
+
# module first (if any), then controller-specific helper modules in Rails MRO order
|
|
310
|
+
# (see extract_controller_helper_modules).
|
|
311
|
+
sig do
|
|
312
|
+
params(
|
|
313
|
+
klass: T.untyped,
|
|
314
|
+
helper_module_name: T.nilable(String),
|
|
315
|
+
controller_helper_modules: T::Array[String]
|
|
316
|
+
).void
|
|
317
|
+
end
|
|
318
|
+
def apply_includes_for_action!(klass, helper_module_name, controller_helper_modules)
|
|
319
|
+
klass.create_include(helper_module_name) if helper_module_name
|
|
320
|
+
controller_helper_modules.each do |mod_name|
|
|
321
|
+
klass.create_include("::#{mod_name}")
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Layout template classes are shared by every controller that uses the same layout.
|
|
326
|
+
# Record per-controller includes, then merge once with deterministic rules:
|
|
327
|
+
# - contributors sorted by controller_sort_key
|
|
328
|
+
# - within each contributor: helper module first, then MRO order (unchanged)
|
|
329
|
+
# - same constant name only included once: first contribution in the sorted list wins
|
|
330
|
+
# (preserves a single stable precedence for duplicate modules).
|
|
331
|
+
sig { void }
|
|
332
|
+
def emit_merged_layout_includes
|
|
333
|
+
T.must(@layout_contributions).sort_by(&:first).each do |class_name, contribs|
|
|
334
|
+
lines = merge_layout_include_lines(contribs)
|
|
335
|
+
next if lines.empty?
|
|
336
|
+
|
|
337
|
+
create_class_from_path(class_name) do |klass_rbi|
|
|
338
|
+
lines.each { |line| klass_rbi.create_include(line) }
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Returns the exact `create_include` string for each line (unprefixed SorbetView path or ::Foo).
|
|
344
|
+
sig { params(contributions: T::Array[T::Hash[Symbol, T.untyped]]).returns(T::Array[String]) }
|
|
345
|
+
def merge_layout_include_lines(contributions)
|
|
346
|
+
seen = T.let({}, T::Hash[String, T::Boolean])
|
|
347
|
+
out = T.let([], T::Array[String])
|
|
348
|
+
|
|
349
|
+
contributions.sort_by { |c| c[:controller_name].to_s }.each do |c|
|
|
350
|
+
h = c[:helper_module_name]
|
|
351
|
+
if h && !seen[h]
|
|
352
|
+
seen[h] = true
|
|
353
|
+
out << h
|
|
354
|
+
end
|
|
355
|
+
c[:controller_helper_modules].to_a.each do |mod_name|
|
|
356
|
+
next if seen[mod_name]
|
|
357
|
+
|
|
358
|
+
seen[mod_name] = true
|
|
359
|
+
out << "::#{mod_name}"
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
out
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Scan view dirs for format suffixes of a given action's templates.
|
|
367
|
+
# e.g. ["Html", "TurboStream"] for show.html.erb + show.turbo_stream.erb.
|
|
368
|
+
# Templates with no format (e.g. show.erb) contribute no suffix — for those,
|
|
369
|
+
# callers fall back to the action-base class name.
|
|
370
|
+
sig { params(controller_path: String, action_name: String).returns(T::Array[String]) }
|
|
371
|
+
def find_template_format_suffixes(controller_path, action_name)
|
|
372
|
+
suffixes = T.let([], T::Array[String])
|
|
373
|
+
|
|
374
|
+
resolve_view_dirs.each do |vd|
|
|
375
|
+
Dir.glob(File.join(vd, controller_path, "#{action_name}.*")).each do |file|
|
|
376
|
+
fname_parts = File.basename(file).split('.')
|
|
377
|
+
next unless fname_parts.first == action_name
|
|
378
|
+
next unless fname_parts.length >= 3
|
|
379
|
+
|
|
380
|
+
middle_parts = T.must(fname_parts[1..-2])
|
|
381
|
+
suffixes << middle_parts.map { |p| camelize(p) }.join('::')
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
suffixes.uniq.sort
|
|
274
386
|
end
|
|
275
387
|
|
|
276
388
|
# Returns [[method_name, params, return_type], ...]
|
|
@@ -278,42 +390,29 @@ module Tapioca
|
|
|
278
390
|
def extract_helper_methods(controller)
|
|
279
391
|
return [] unless controller.respond_to?(:_helper_methods)
|
|
280
392
|
|
|
281
|
-
controller._helper_methods.filter_map do |name|
|
|
393
|
+
rows = controller._helper_methods.filter_map do |name|
|
|
282
394
|
name_s = name.to_s
|
|
283
395
|
next unless name_s.match?(/\A[a-zA-Z_][a-zA-Z0-9_]*[?!]?\z/)
|
|
284
396
|
|
|
285
397
|
method_info = find_method_info(controller, name_s)
|
|
286
|
-
params =
|
|
287
|
-
build_params_from_srb_lens(method_info)
|
|
288
|
-
else
|
|
289
|
-
build_params_from_reflection(controller, name_s)
|
|
290
|
-
end
|
|
398
|
+
params = method_info ? build_params_from_srb_lens(method_info) : build_params_from_reflection(controller, name_s)
|
|
291
399
|
return_type = method_info&.return_type || 'T.untyped'
|
|
292
400
|
return_type = 'T.untyped' if return_type.empty?
|
|
293
401
|
|
|
294
402
|
[name_s, params, return_type]
|
|
295
403
|
end
|
|
404
|
+
rows.sort_by { |row| T.must(row).first }
|
|
296
405
|
end
|
|
297
406
|
|
|
298
|
-
# Returns
|
|
407
|
+
# Returns runtime helper module names for the controller.
|
|
299
408
|
sig { params(controller: T.untyped).returns(T::Array[String]) }
|
|
300
409
|
def extract_controller_helper_modules(controller)
|
|
301
410
|
return [] unless controller.respond_to?(:_helpers)
|
|
302
411
|
|
|
303
|
-
|
|
304
|
-
::ApplicationController._helpers.ancestors
|
|
305
|
-
else
|
|
306
|
-
[]
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
controller_helpers = controller._helpers.ancestors
|
|
310
|
-
specific_modules = controller_helpers - base_helpers
|
|
311
|
-
|
|
312
|
-
specific_modules.filter_map do |mod|
|
|
412
|
+
controller._helpers.ancestors.filter_map do |mod|
|
|
313
413
|
next unless mod.is_a?(Module) && !mod.is_a?(Class)
|
|
314
414
|
name = mod.name
|
|
315
415
|
next if name.nil? || name.empty?
|
|
316
|
-
next if name.include?('HelperMethods')
|
|
317
416
|
next if name.start_with?('ActionController::') || name.start_with?('AbstractController::')
|
|
318
417
|
name
|
|
319
418
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sorbet_view
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kazuma
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-05-14 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: herb
|