I would like to amplify on what others have said and, in particular, compare the use of class variables with class instance variables. Let's start with this:
class Holder1
end
class Holder2 < Holder1
@@var = 99
# Create a class setter and a getter for the class variable
def Holder2.var=(val)
@@var = val
end
def Holder2.var
@@var
end
# Create a instance getter for the class variable
def var
@@var
end
end
class Holder3 < Holder2
end
Holder2.var #=> 99
Holder2.var = 1
Holder3.var #=> 1
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder2.new.var #=> 1
Holder3.new.var #=> 1
Holder3.var = 2
Holder3.var #=> 2
Holder2.var #=> 2
Holder3.new.var #=> 2
Holder2.new.var #=> 2
Aside: the first two methods would normally be written like this:
def self.var=(val)
@@var = val
end
def self.var
@@var
end
This works because self
=> Holder2
when the methods are created. By not hardwiring the class name, no change is required if you decide to rename the class.
Now this (the use of a class variable) may be exactly the behavior you want. That is, if you want subclasses to see the variable and be able to change it, a class variable is what you need.
If, however, you want each subclass to have their own variable, which can be neither seen nor changed by subclasses, you need to use a class instance variable, @var
, rather than a class variable, @@var
. (Technically, this is not quite right, because one could employ Holder2.instance_variable_get(:@var)
or Holder2.instance_variable_set(:@var)
anywhere in your program.)
Compare the results of the code that follows with that above. I've included an instance variable with the same name as the class instance variable, @var
, to illustrate that they are as different as are @night
and @day
.
class Holder1
end
class Holder2 < Holder1
# Create an accessor for the instance variable
attr_accessor :var
# Initialize class instance variable
@var = 99
# Create an accessor for the class instance variable
class << self
puts "self in 'class << self': #{self}"
attr_accessor :var
def our_dog
"Diva"
end
end
# Create a class method
def self.our_cat
puts "self in 'self.our_cat())' def: #{self}"
"Teagan"
end
# Create an instance setter and a getter for the class instance variable
def c_ivar=(val)
self.class.var = val
end
def c_ivar
self.class.var
end
end
class Holder3 < Holder2
end
#=> self in 'class << self': #<Class:Holder2>
Holder2.var #=> 99
h2 = Holder2.new
h2.var #=> nil
Holder2.var = 1
Holder2.var #=> 1
h2.c_ivar #=> 1
h2.c_ivar = 2
Holder2.var #=> 2
h2.var #=> nil
h2.var = 3
h2.var #=> 3
Holder2.var #=> 2
Holder3.var #=> nil
Holder1.var #=> NoMethodError: undefined method `var' for Holder1:Class
Holder3.var = 4
Holder3.var #=> 4
Holder2.var #=> 2
h3 = Holder3.new
h3.c_ivar #=> 4
h2.c_ivar #=> 2
Holder3.our_dog #=> "Diva"
Holder3.our_cat #=> "self in 'self.our_cat())' def: Holder3"
#=> "Teagan"
Holder1.var
raises an exception because that class has no access to Holder2
's class instance variable var@
. By contrast, the first use of Holder3.var
returns nil
because the variable exists for Holder3
, by inheritance, but has not been initialized.
The expression class << self
changes self
from Holder2
to Holder2
's singleton class (aka metaclass) until the next end
. Notice that Ruby denotes Holder2
's singleton class as #<Class:Holder2>
. Also, we do not need to prepend my_dog
with self.
because self
is Holder2
's singleton class when the method is created.
Notice that:
Holder2.singleton_methods #=> [:var, :var=, :our_dog, :our_cat]
This shows that our_cat()
is a method of Holder2
's singleton class, even though self
was Holder2
(and not Holder2
's singleton class ', #<Class:Holder2>
) when the method was constructed. For this reason, some say that, technically, "there is no such thing as a class method". This also tells us that we could have moved the definition of my_cat
into the class << self
construct (and dropped self.
).
Another common way to add instance variables and methods to Holder2
's singleton class is to replace class << self ... end
with extend M
and create a module:
module M
attr_accessor :var
def our_dog
"Diva"
end
end
Object#extend mixes these instance variables and methods into Holder2
's singleton class.