At the beginning of 2023, the problem is still relevant. Because the rubocop documentation is not the place to post information about the intricacies of OOP in ruby.
The dislike of using class variables comes from unexpected behavior when we use class inheritance. But we love to watch the code, not read the description, and the documentation clearly says:
You have to be careful when setting a value for a class variable; if a class has been inherited, changing the value of a class variable also affects the inheriting classes. This means that it's almost always better to use a class instance variable instead.
I would like to supplement Alexey Matyushkin's answer and show the behavior of class variables with simple examples. And also explain what this can lead to.
I confirm that the code from the rubocop documentation is some kind of nonsense:
# good
class A
@test = 10
end
class A
def test
@@test # you can access class variable without offense
end
end
class A
def self.test(name)
class_variable_get("@@#{name}") # you can access without offense
end
end
begin
puts A.new.test
rescue => e
puts e.message
end
begin
puts A.test 'test'
rescue => e
puts e.message
end
puts "RUBY_VERSION: #{RUBY_VERSION}"
=>>>
uninitialized class variable @@test in A
Did you mean? @test
uninitialized class variable @@test in A
Did you mean? @test
RUBY_VERSION: 2.5.3
What rubocop really wanted to tell us.
puts 'When we use "classic" class variables:'
class A
@@var = 10
cattr_accessor :var
end
class Aa < A
end
puts Aa.var, '- the child class has inherited the methods and the value of the variable.'
Aa.var = 20
puts A.var, '- but the variable of the parent class was implicitly changed (bad)!'
puts 'When we use class instance variables:'
class B
@test = 10
class << self
attr_accessor :test
end
end
class Bb < B
end
puts Bb.test, '- the child class has inherited the methods, but not the value of the variable (this is also bad)!'
Bb.test = 20
puts B.test, '- a change in the child class does not lead to a change in the parent.'
=>>>
When we use "classic" class variables:
10
- the child class has inherited the methods and the value of the variable.
20
- but the variable of the parent class was implicitly changed (bad)!
When we use class instance variables:
- the child class has inherited the methods, but not the value of the variable (this is also bad)!
10
- a change in the child class does not lead to a change in the parent.
And what's the big deal? How can this harm?
One way to modify BIG programs is to inherit the class and make your own changes to it. Often the project is complex, there are many implicit dependencies (let's be honest =)), and if you make changes directly to the class, the project will crash. Therefore, we use inheritance, the child class is used in a new service with its own settings, or the child class changes the behavior of one part of the program. And if, during inheritance, the child class suddenly changes the base class, then inheritance loses its meaning! Flexibility is lost.
But any question needs to be viewed in context. If you are writing a miniature project alone, then there is nothing wrong with @@ var. You just need understanding.