I see the issue with using class variables with Ruby; however, it seems RuboCop's documentation for how to fix the issue is not sufficient.
Now, I could just ignore it. Given my project, it doesn't matter. But, I just want to know what Rubocop is trying to tell me to do, because it doesn't make sense.
Executing the provided code in irb 0.9.6
with Ruby 2.5.1
gives:
class A
@test = 10
end
#=> 10
class A
def test
@@test # you can access class variable without offense
end
end
#=> :test
A.new.test
Traceback (most recent call last):
3: from /Users/Ricky/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
2: from (irb):12
1: from (irb):9:in `test'
NameError (uninitialized class variable @@test in A)
Did you mean? @test
So, no. We obviously cannot access class variable without offense. irb was very offended. But, ruby suggests using @test
. Maybe it was just a typo? Let's try it:
class A
@test = 10
def test
@test # you can access class variable without offense
end
end
#=> :test
A.new.test
#=> nil
So, the instance variable was never defined. What is RuboCop trying to say here?
You are missing the difference between the scopes of variables.
class A
@test = 42
end
The above declares an instance variable in the class scope. It is accessible as
A.instance_variable_get(:@test)
#⇒ 42
You can define an accessor for this variable:
class A
@test = 42
def self.test
@test
end
end
A.test #⇒ 42
It is shared between instances and to access it from instances you should refer to the class:
# ⇓⇓⇓⇓⇓ HERE
A.new.class.test #⇒ 42
The following code declares an instance variable on the class instances:
class A
def initialize
@test = 42
end
end
It can be accessed from the instances of A
:
A.new.instance_variable_get(:@test)
#⇒ 42
Class variables have some drawbacks when used within the class hierarchy, that is [probably] why Rubocop suggests not to use class variables (or whatever it suggests—I honestly never used it since it brings more harm than help IMSO.)
In your first snippet you have missed the @
. The correct code would be:
class A
# ⇓⇓ HERE
@@test = 10
end
class A
def test
@@test # you can access class variable without offense
end
end