Michael Cordell's Blog

Ruby: Classes, Modules, and Mixins

27 Jul 2013

Below is a short review of classes and modules. This post is written so I can cement these concepts for myself. The source material is Metaprogramming Ruby and is mainly a rephrasing of concepts from it. If you would like to understand more, I recommend the book.

Objects

Starting for the bottom, the most basic unit is, of course, the object. In essence, an object has two things: instance variables and a link to its class. Using this link (and further inheritance), an object can have methods called on it.

Object Rules

Classes

Classes are objects themselves, and subclasses of Modules. They can be thought of as containers of methods (Modules, by extension, can be thought of the same way). An object’s methods are that object’s class’ instance_methods. This is best illustrated by the following code (copied from Metaprogramming Ruby)

String.instance_methods() == "astring".methods()

Which evaluates to true in irb.

Class names are constants, hence they are CamelCased.

Class Instance Variables

Classes are themselves objects, and therefore can contain instance variables. These instance variables are only accessible to the class object itself. Below is an illustrative example of how class instance variables work and how they differantiate from the instance variables of objects instantiated from them. Pictured code is found here, but obviously the return stuff is from irb (lines >19).

Module heirarchy

Class variables

Class variables are different from the above class instance variables. Class variables are prepended with @@ and defined thusly:

@@var = 'apple'

Class variables are accessible to subclasses. However they are often not used because they can lead to scope confusion if used incorrectly.

Modules

Modules can be used to avoid name collisions on methods because they partition constants in a heirarchy. If you defined a class named Todo, you might very well run into a name collision with a core class or another project. Placing Todo class inside the Module TodoList effectively changes the name to TodoList::Todo (a much less likely candidate for collision).

Including a module inserts the module and methods directly above the includer class in the inheritance heirarchy. Subsequently included modules will be inserted into this position and present modules will move up this heirarchy. W I made this to illustrate multiple module inclusion:

Module heirarchy

Eigen classes or Singleton Classes or Meta classes

Eigen classes are the last wrinkle in the heirarchy of Ruby objects/classes. They do not have an official name, and can be called singleton classes, eigen classes, and “meta classes”. It is better to just define them as the place where singleton methods reside. What are singleton methods, you ask? They are methods that are defined on a single object (could be a Class object). Example:

class Employee
end

bob = Employee.new

def bob.say_hi
  "Hi I am bob"
end

bob.say_hi #=> "Hi I am bob"

Employee.instance_methods.grep("say_hi") #=> []

If say_hi is not stored in Employee, where is it? The answer is the singleton/eigen class. These classes are “shadow” or hidden classes on objects for storing these methods. They will not be accessible by .class() or superclass().

bob.class() #=> Employee

To get in the scope of these hidden classes, use the following syntax:

class << bob

    def say_bye
        "Adios"
    end
end

bob.say_bye #=> "Adios"
Employee.instance_methods.grep("say_bye") #=> []

You can actually get a hold of the class by returning self in the scope, e.g:

bobs_eigen = class << bob; self; end;

The final point with Eigen classes revolves around what happens with Class objects’ eigen classes. Extended code/irb with diagram to follow:

class Person; end;

class Employee < Person; end;

bob = Employee.new
bobs_eigen = class << bob; self; end; #=> #<Class:#<Employee:0x007fcd5d17af70>>
employee_class = bob.class() #=> Employee
person_class = bob.class().superclass() #=> Person
employee_class_eigen = class << employee_class; self; end; #=> #<Class:Employee>

bobs_eigen.superclass() #=> Employee
employee_class_eigen.superclass() #=> #<Class:Person>

person_class_eigen = class << person_class; self; end; #=> #<Class:Person>

Module heirarchy

The key points here are:

With these points, it becomes clear how singleton methods exist. Further, how class methods exist and how they are inherited.