Class Methods and Variables in Ruby

Class methods are methods that apply to a particular class in Ruby. Rather than acting on a particular instance of a class, they act on the class blueprint itself, i.e. the instance of the Class object.

Class methods can’t access instance variables. But instance methods can access class variables.

The most common way to create a class method in Ruby is to use the self keyword. This refers back to the class object itself.

class IceCream
  @@total_scoops = 0

  def self.increment_scoops
    @@total_scoops +=1
  end

  def self.scoops_served
    @@total_scoops
  end
end

In this example, we use the class variable @@scoops to keep track of total ice cream scoops served. If you aren’t careful, class variables can cause you problems in Ruby. Consider the following example.

class FrozenYogurt < IceCream
  @@total_scoops = 0
end

What would you expect the output to be when you ran the following code?

puts "Serving ice cream."
IceCream.increment_scoops
puts "#{IceCream.scoops_served} scoops of ice cream served."

puts "Serving froyo."
FrozenYogurt.increment_scoops
puts "#{FrozenYogurt.scoops_served} scoops of froyo served."

The answer is…

Serving ice cream.
1 scoops of ice cream served.
Serving froyo.
2 scoops of froyo served.

In Ruby, classes share class variables with their ancestors. Both of these classes are operating on the same @@scoops variable. This is often not the desired behavior. When you don’t want this, you should use what are sometimes called “class instance variables”. These are really just instance variables, but they’re applied to the instance of the Class class, the blueprint object. You create one by defining an instance variable right in the class body. You can only refer to these variable within class methods. Let’s try it.

class IceCream
  @total_scoops = 0

  def self.increment_scoops
    @total_scoops +=1
  end

  def self.scoops_served
    @total_scoops
  end
end
class FrozenYogurt < IceCream
  @total_scoops = 0
end

puts "Serving ice cream."
IceCream.increment_scoops
puts "#{IceCream.scoops_served} scoops of ice cream served."

puts "Serving froyo."
FrozenYogurt.increment_scoops
puts "#{FrozenYogurt.scoops_served} scoops of froyo served."

This code outputs:

Serving ice cream.
1 scoops of ice cream served.
Serving froyo.
1 scoops of froyo served.

Now both of our class instances, IceCream and Froyo, each have their own @scoops variable. Remember, classes in Ruby are objects too, specifically instances of the Class class. It’s a little confusing but all we are doing here is adding instance variables to our class instance. This avoids the issue with class variables being inherited by child classes.