Previous post: How Ruby Objects are Implemented

Ruby classes are represented by a RClass structure. It’s a fairly big structure, so I’ve broken it down according to what a Ruby class actually does and what it contains.

RClass

A Ruby class is a Ruby object that also contains method definitions, attribute names, a superclass pointer, and a constants table.
- Pat Shaughnessy

class Fruit
  attr_accessor :colour
  attr_accessor :taste
end

A Ruby class contains method definitions.

Method definitions are stored in a m_tbl method table. In Fruit, this will be a table that contains the keys colour, colour=, taste, and taste=, and whose values are pointers to the actual method definitions and their YARV instructions.

A Ruby class contains instance-level attribute names.

As mentioned in the previous post, a RObject contains a pointer iv_index_tbl which is a hash table that maps attribute names to their positions in the ivtpr array. In Fruit, this will be a hash table that contains the keys colour and taste, and whose values are their indices in the ivptr array.

A Ruby class is also a Ruby object.

p Fruit.class           # Class
p Fruit.singleton_class # #<Class:Fruit>

If you print out Fruit’s class, it shows Fruit as being an instance of the Class class. A Ruby class is also a Ruby object.

As a Ruby object, a Ruby class will also have:

  • A klass class pointer that references the RClass of its class (and is stored, like any other RObject, in a RBasic structure)
  • Its own methods (called class methods - those that you define with def self.some_method). These methods are not stored in the class itself, but in its metaclass, or sometimes called the singleton class. This singleton class is what klass is set to, not Class.
  • A table of its own class-level instance variables called iv_tbl

A Ruby class has a pointer to a superclass, which allows it implement inheritance.

class Pear < Fruit
end

p Pear.class      # Class
p Pear.superclass # Fruit

A Ruby class will have a super pointer that references its superclass. In this case, Pear will have a klass pointer referencing Class, and a super pointer referencing Fruit. There is a dinstinction between the two - klass refers to the class from which the Pear class object is instantiated, whereas super refers to Pear’s logical superclass. The Pear class object is not instantiated from Fruit, but Class.

A Ruby class contains constants.

class Pear < Fruit
  SIZE = 6 # units in cm
end

A Ruby class can contain constants. Constants are stored in a const_tbl constants table.

Ruby classes are also objects. When a class is created, Ruby actually creates two objects - the class object itself, as well as a metaclass object.

Class Variables

In Ruby, there are class variables, which are dintinct from class-level instance variables, as mentioned above. Class-level instance variables are defined with a single @ prepended to the variable name and are scoped to each individual class object. Class variables are defined with a double @@ prepended to the variable name and is visible globally across all instances of the class. For example:

class Fruit
  @smell = nil
  @@edible = false
  def self.smell
    @smell
  end
  def self.edible
    @@edible
  end
end

class Pear < Fruit
  @smell = "Fragrant"
  @@edible = true
end

p Fruit.smell    # nil
p Pear.smell     # "Fragrant"
p Fruit.edible   # true
p Pear.edible    # true

Internally, both types of variables are stored together in the same iv_tbl tables of both Fruit and Pear. When setting a class-level instance variable, @smell is set to nil in Fruit’s iv_tbl and "Fragrant in Pear’s. However, when setting a class variable, Ruby checks the iv_tbl of that class and all its superclasses for the presence of the variable, and sets them all to the same value. When Pear redefines @@edible to true, that change is also reflected in Fruit’s iv_tbl.