Skip to content

Commit 72c85a9

Browse files
authored
Add support for class_methods do blocks within concerns (#530)
* Temporarily point to branch * Update for changes to enhancements API * Add example of class_methods to dummy app * Add support for `class_methods do` blocks in concerns * Re-add x64-mingw-ucrt * Fix tests * Remove unnecessary initializer * Fix test * Extract @indexable_path to setup
1 parent cba97f8 commit 72c85a9

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

lib/ruby_lsp/ruby_lsp_rails/indexing_enhancement.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ def on_call_node_enter(call_node)
2020
handle_concern_extend(owner, call_node)
2121
when :has_one, :has_many, :belongs_to, :has_and_belongs_to_many
2222
handle_association(owner, call_node)
23+
# for `class_methods do` blocks within concerns
24+
when :class_methods
25+
handle_class_methods(owner, call_node)
26+
end
27+
end
28+
29+
sig do
30+
override.params(
31+
call_node: Prism::CallNode,
32+
).void
33+
end
34+
def on_call_node_leave(call_node)
35+
if call_node.name == :class_methods && call_node.block
36+
@listener.pop_namespace_stack
2337
end
2438
end
2539

@@ -83,6 +97,13 @@ def handle_concern_extend(owner, call_node)
8397
# Do nothing
8498
end
8599
end
100+
101+
sig { params(owner: RubyIndexer::Entry::Namespace, call_node: Prism::CallNode).void }
102+
def handle_class_methods(owner, call_node)
103+
return unless call_node.block
104+
105+
@listener.add_module("ClassMethods", call_node.location, call_node.location)
106+
end
86107
end
87108
end
88109
end

test/dummy/app/models/concerns/verifiable.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@ def all_verified
1313
all.select(&:verified?)
1414
end
1515
end
16+
17+
class_methods do
18+
def all_unverified
19+
all.reject(&:verified?)
20+
end
21+
end
1622
end

test/ruby_lsp_rails/indexing_enhancement_test.rb

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module Rails
88
class IndexingEnhancementTest < ActiveSupport::TestCase
99
class << self
1010
# For these tests, it's convenient to have the index fully populated with Rails information, but we don't have
11-
# to reindex on every single example or that will be too slow
11+
# to re-index on every single example or that will be too slow
1212
def populated_index
1313
@index ||= begin
1414
index = RubyIndexer::Index.new
@@ -20,22 +20,77 @@ def populated_index
2020

2121
def setup
2222
@index = self.class.populated_index
23+
@indexable_path = RubyIndexer::IndexablePath.new(nil, "/fake.rb")
24+
end
25+
26+
def teardown
27+
# Prevent state leaking between tests
28+
@index.delete(@indexable_path)
29+
@index.instance_variable_set(:@ancestors, {})
2330
end
2431

2532
test "ClassMethods module inside concerns are automatically extended" do
26-
@index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY)
27-
class Post < ActiveRecord::Base
33+
@index.index_single(@indexable_path, <<~RUBY)
34+
module Verifiable
35+
extend ActiveSupport::Concern
36+
37+
module ClassMethods
38+
def all_verified; end
39+
end
40+
end
41+
42+
class Post
43+
include Verifiable
2844
end
2945
RUBY
3046

3147
ancestors = @index.linearized_ancestors_of("Post::<Class:Post>")
32-
assert_includes(ancestors, "ActiveRecord::Associations::ClassMethods")
33-
assert_includes(ancestors, "ActiveRecord::Store::ClassMethods")
34-
assert_includes(ancestors, "ActiveRecord::AttributeMethods::ClassMethods")
48+
49+
assert_includes(ancestors, "Verifiable::ClassMethods")
50+
refute_nil(@index.resolve_method("all_verified", "Post::<Class:Post>"))
51+
end
52+
53+
test "class_methods blocks inside concerns are automatically extended via a ClassMethods module" do
54+
@index.index_single(@indexable_path, <<~RUBY)
55+
module Verifiable
56+
extend ActiveSupport::Concern
57+
58+
class_methods do
59+
def all_verified; end
60+
end
61+
end
62+
63+
class Post
64+
include Verifiable
65+
end
66+
RUBY
67+
68+
ancestors = @index.linearized_ancestors_of("Post::<Class:Post>")
69+
70+
assert_includes(ancestors, "Verifiable::ClassMethods")
71+
refute_nil(@index.resolve_method("all_verified", "Post::<Class:Post>"))
72+
end
73+
74+
test "ignores `class_methods` calls without a block" do
75+
@index.index_single(@indexable_path, <<~RUBY)
76+
module Verifiable
77+
extend ActiveSupport::Concern
78+
79+
class_methods
80+
end
81+
82+
class Post
83+
include Verifiable
84+
end
85+
RUBY
86+
87+
ancestors = @index.linearized_ancestors_of("Post::<Class:Post>")
88+
89+
refute_includes(ancestors, "Verifiable::ClassMethods")
3590
end
3691

3792
test "associations" do
38-
@index.index_single(RubyIndexer::IndexablePath.new(nil, "/fake.rb"), <<~RUBY)
93+
@index.index_single(@indexable_path, <<~RUBY)
3994
class Post < ActiveRecord::Base
4095
has_one :content
4196
belongs_to :author

0 commit comments

Comments
 (0)