Skip to content

Commit 568cd20

Browse files
committed
LeakyConstantDeclaration - allow definitions in explicit namespaces
Constant, classes, and modules that are defined on explicit namespaces do not "leak" into the global namespace, because the author is making their intention clear. This is even true when when explicitly using `::`. While doing this may not be a particularly good practice, it should ideally not result in an offense. The practice is described here: https://makandracards.com/makandra/47189-rspec-how-to-define-classes-for-specs#section-1-defining-the-constant-on-the-example-class
1 parent dc0ce74 commit 568cd20

File tree

3 files changed

+81
-5
lines changed

3 files changed

+81
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Master (Unreleased)
44

55
- Mark `RSpec/IncludeExamples` as `SafeAutoCorrect: false`. ([@yujideveloper])
6+
- Fix a false positive for `RSpec/LeakyConstantDeclaration` when defining constants in explicit namespaces. ([@naveg])
67

78
## 3.6.0 (2025-04-18)
89

lib/rubocop/cop/rspec/leaky_constant_declaration.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,21 @@ class LeakyConstantDeclaration < Base
100100

101101
def on_casgn(node)
102102
return unless inside_describe_block?(node)
103+
return if explicit_namespace?(node.namespace)
103104

104105
add_offense(node, message: MSG_CONST)
105106
end
106107

107108
def on_class(node)
108109
return unless inside_describe_block?(node)
110+
return if explicit_namespace?(node.identifier.namespace)
109111

110112
add_offense(node, message: MSG_CLASS)
111113
end
112114

113115
def on_module(node)
114116
return unless inside_describe_block?(node)
117+
return if explicit_namespace?(node.identifier.namespace)
115118

116119
add_offense(node, message: MSG_MODULE)
117120
end
@@ -121,6 +124,10 @@ def on_module(node)
121124
def inside_describe_block?(node)
122125
node.each_ancestor(:block).any? { |ancestor| spec_group?(ancestor) }
123126
end
127+
128+
def explicit_namespace?(namespace)
129+
!namespace.nil?
130+
end
124131
end
125132
end
126133
end

spec/rubocop/cop/rspec/leaky_constant_declaration_spec.rb

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@
3131
RUBY
3232
end
3333

34+
it 'ignores constant defined on the example group' do
35+
expect_no_offenses(<<~RUBY)
36+
describe SomeClass do
37+
self::CONSTANT = "Accessible as self.class::CONSTANT".freeze
38+
end
39+
RUBY
40+
end
41+
42+
it 'ignores constant defined in an explicit namespace' do
43+
expect_no_offenses(<<~RUBY)
44+
describe SomeClass do
45+
Foo::CONSTANT = "Accessible as Foo::CONSTANT".freeze
46+
end
47+
RUBY
48+
end
49+
50+
it 'ignores classes defined explicitly in the global namespace' do
51+
expect_no_offenses(<<~RUBY)
52+
describe SomeClass do
53+
::CONSTANT = "Accessible as ::CONSTANT".freeze
54+
end
55+
RUBY
56+
end
57+
3458
it 'ignores outside of example/shared group' do
3559
expect_no_offenses(<<~RUBY)
3660
factory :some_class do
@@ -60,15 +84,32 @@ def method
6084
end
6185
end
6286
end
63-
end
87+
end
6488
RUBY
6589
end
6690

67-
it 'flags namespaced class' do
68-
expect_offense(<<~RUBY)
91+
it 'ignores classes defined on the example group' do
92+
expect_no_offenses(<<~RUBY)
93+
describe SomeClass do
94+
class self::DummyClass
95+
end
96+
end
97+
RUBY
98+
end
99+
100+
it 'ignores classes defined in an explicit namespace' do
101+
expect_no_offenses(<<~RUBY)
69102
describe SomeClass do
70-
class SomeModule::AnotherModule::DummyClass
71-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Stub class constant instead of declaring explicitly.
103+
class Foo::DummyClass
104+
end
105+
end
106+
RUBY
107+
end
108+
109+
it 'ignores classes defined explicitly in the global namespace' do
110+
expect_no_offenses(<<~RUBY)
111+
describe SomeClass do
112+
class ::DummyClass
72113
end
73114
end
74115
RUBY
@@ -93,6 +134,33 @@ module DummyModule
93134
RUBY
94135
end
95136

137+
it 'ignores modules defined on the example group' do
138+
expect_no_offenses(<<~RUBY)
139+
describe SomeClass do
140+
module self::DummyModule
141+
end
142+
end
143+
RUBY
144+
end
145+
146+
it 'ignores modules defined in an explicit namespace' do
147+
expect_no_offenses(<<~RUBY)
148+
describe SomeClass do
149+
module Foo::DummyModule
150+
end
151+
end
152+
RUBY
153+
end
154+
155+
it 'ignores modules defined explicitly in the global namespace' do
156+
expect_no_offenses(<<~RUBY)
157+
describe SomeClass do
158+
module ::DummyModule
159+
end
160+
end
161+
RUBY
162+
end
163+
96164
it 'ignores outside of example/shared group' do
97165
expect_no_offenses(<<~RUBY)
98166
module Dummymodule

0 commit comments

Comments
 (0)