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.
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.
- Has instance variables
- Has a refrence or class designations that is used to for method calls on it
- Has “methods” as opposed to a Class’ instance_methods
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)
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).
Class variables are different from the above class instance variables. Class variables are prepended with @@ and defined thusly:
Class variables are accessible to subclasses. However they are often not used because they can lead to scope confusion if used incorrectly.
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:
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:
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().
To get in the scope of these hidden classes, use the following syntax:
You can actually get a hold of the class by returning self in the scope, e.g:
The final point with Eigen classes revolves around what happens with Class objects’ eigen classes. Extended code/irb with diagram to follow:
The key points here are:
- The position of the bob’s (the object) eigen class. Below the Employee class.
- The eigen class of Employee has a Super class of the eigen class of Person.
With these points, it becomes clear how singleton methods exist. Further, how class methods exist and how they are inherited.