Skip to content

Commit 4d0e655

Browse files
authored
Merge pull request rubocop#1438 from ydakuka/912-add-more-delegation-targets-to-rails-delegate
[Fix rubocop#912] Enhance `Rails/Delegate` by adding delegation detection for `self.class`, constants, instance variables, and class variables
2 parents f9adec0 + 23e2098 commit 4d0e655

File tree

3 files changed

+120
-17
lines changed

3 files changed

+120
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#912](https://github.com/rubocop/rubocop-rails/issues/912): Enhance `Rails/Delegate` by adding delegation detection for `self.class`, constants, class variables, global variables, and instance variables. ([@ydakuka][])

lib/rubocop/cop/rails/delegate.rb

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class Delegate < Base
6868

6969
def_node_matcher :delegate?, <<~PATTERN
7070
(def _method_name _args
71-
(send {(send nil? _) (self)} _ ...))
71+
(send {(send nil? _) (self) (send (self) :class) ({cvar gvar ivar} _) (const _ _)} _ ...))
7272
PATTERN
7373

7474
def on_def(node)
@@ -82,17 +82,39 @@ def on_def(node)
8282

8383
def register_offense(node)
8484
add_offense(node.loc.keyword) do |corrector|
85-
body = node.body
85+
receiver = determine_register_offense_receiver(node.body.receiver)
86+
delegation = build_delegation(node, receiver)
8687

87-
receiver = body.receiver.self_type? ? 'self' : ":#{body.receiver.method_name}"
88-
89-
delegation = ["delegate :#{body.method_name}", "to: #{receiver}"]
90-
delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
88+
corrector.replace(node, delegation)
89+
end
90+
end
9191

92-
corrector.replace(node, delegation.join(', '))
92+
def determine_register_offense_receiver(receiver)
93+
case receiver.type
94+
when :self
95+
'self'
96+
when :const
97+
full_name = full_const_name(receiver)
98+
full_name.include?('::') ? ":'#{full_name}'" : ":#{full_name}"
99+
when :cvar, :gvar, :ivar
100+
":#{receiver.source}"
101+
else
102+
":#{receiver.method_name}"
93103
end
94104
end
95105

106+
def build_delegation(node, receiver)
107+
delegation = ["delegate :#{node.body.method_name}", "to: #{receiver}"]
108+
delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
109+
delegation.join(', ')
110+
end
111+
112+
def full_const_name(node)
113+
return node.source unless node.namespace
114+
115+
"#{full_const_name(node.namespace)}::#{node.children.last}"
116+
end
117+
96118
def trivial_delegate?(def_node)
97119
delegate?(def_node) &&
98120
method_name_matches?(def_node.method_name, def_node.body) &&
@@ -120,7 +142,18 @@ def include_prefix_case?
120142
def prefixed_method_name(body)
121143
return '' if body.receiver.self_type?
122144

123-
[body.receiver.method_name, body.method_name].join('_').to_sym
145+
[determine_prefixed_method_receiver_name(body.receiver), body.method_name].join('_').to_sym
146+
end
147+
148+
def determine_prefixed_method_receiver_name(receiver)
149+
case receiver.type
150+
when :cvar, :gvar, :ivar
151+
receiver.source
152+
when :const
153+
full_const_name(receiver)
154+
else
155+
receiver.method_name.to_s
156+
end
124157
end
125158

126159
def private_or_protected_delegation(node)

spec/rubocop/cop/rails/delegate_spec.rb

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,6 @@ def new
211211
RUBY
212212
end
213213

214-
it 'ignores delegation to constant' do
215-
expect_no_offenses(<<~RUBY)
216-
FOO = []
217-
def size
218-
FOO.size
219-
end
220-
RUBY
221-
end
222-
223214
it 'ignores code with no receiver' do
224215
expect_no_offenses(<<~RUBY)
225216
def change
@@ -249,4 +240,82 @@ def foo
249240
end
250241
RUBY
251242
end
243+
244+
it 'detects delegation to `self.class`' do
245+
expect_offense(<<~RUBY)
246+
def foo
247+
^^^ Use `delegate` to define delegations.
248+
self.class.foo
249+
end
250+
RUBY
251+
252+
expect_correction(<<~RUBY)
253+
delegate :foo, to: :class
254+
RUBY
255+
end
256+
257+
it 'detects delegation to a constant' do
258+
expect_offense(<<~RUBY)
259+
def foo
260+
^^^ Use `delegate` to define delegations.
261+
CONST.foo
262+
end
263+
RUBY
264+
265+
expect_correction(<<~RUBY)
266+
delegate :foo, to: :CONST
267+
RUBY
268+
end
269+
270+
it 'detects delegation to a namespaced constant' do
271+
expect_offense(<<~RUBY)
272+
def foo
273+
^^^ Use `delegate` to define delegations.
274+
SomeModule::CONST.foo
275+
end
276+
RUBY
277+
278+
expect_correction(<<~RUBY)
279+
delegate :foo, to: :'SomeModule::CONST'
280+
RUBY
281+
end
282+
283+
it 'detects delegation to an instance variable' do
284+
expect_offense(<<~RUBY)
285+
def foo
286+
^^^ Use `delegate` to define delegations.
287+
@instance_variable.foo
288+
end
289+
RUBY
290+
291+
expect_correction(<<~RUBY)
292+
delegate :foo, to: :@instance_variable
293+
RUBY
294+
end
295+
296+
it 'detects delegation to a class variable' do
297+
expect_offense(<<~RUBY)
298+
def foo
299+
^^^ Use `delegate` to define delegations.
300+
@@class_variable.foo
301+
end
302+
RUBY
303+
304+
expect_correction(<<~RUBY)
305+
delegate :foo, to: :@@class_variable
306+
RUBY
307+
end
308+
309+
it 'detects delegation to a global variable' do
310+
expect_offense(<<~RUBY)
311+
def foo
312+
^^^ Use `delegate` to define delegations.
313+
$global_variable.foo
314+
end
315+
RUBY
316+
317+
expect_correction(<<~RUBY)
318+
delegate :foo, to: :$global_variable
319+
RUBY
320+
end
252321
end

0 commit comments

Comments
 (0)