- Develop a mental model for how Ruby manages instances, classes, superclasses, and modules
- Understand Ruby’s Lookup Chain for methods
- Object Model
- Look Up Chain
- Available here
- What is class inheritance and how is it implemented?
- What is a module and how is it implemented?
- How do you know what variables, methods and classes you have available at any given time?
The Ruby Object Model
These three methods can help you investigate the relationships between classes and modules. All methods are run on the class (i.e.
.ancestors: lists all classes along the inheritance chain, and any modules included by those classes. See docs.
.included_modules: returns a list of all modules included by any class along the inheritance chain. See docs.
.superclass: returns the superclass of the class. See docs.
Mapping Ruby’s Object Model
We are going to create a
Dog class. That
Dog class is going to inherit from another class called
Animal class is going to include a module called
# dog.rb require './animal' class Dog < Animal end dog = Dog.new require 'pry'; binding.pry
# animal.rb require './animal_behavior' class Animal include AnimalBehavior end
# animal_behavior.rb module AnimalBehavior end
Notice that we haven’t actually included any methods in these classes/modules. We don’t need them to map Ruby’s Object Model.
When we run the
dog.rb file, we create a new instance of
Dog and then hit a pry. Using the investigative methods we defined above, we can learn about that dog instance’s ancestors:
pry(main)> dog # => #<Dog:0x007ff414932eb0> pry(main)> dog.class # => Dog pry(main)> Dog.ancestors # => [Dog, Animal, AnimalBehavior, Object, PP::ObjectMixin, Kernel, BasicObject] pry(main)> Dog.superclass # => Animal pry(main)> Animal.superclass # => Object pry(main)> Object.superclass # => BasicObject pry(main)> BasicObject.superclass # => nil
.class on the Dog object leads us to the
Dog class. Calling
.superclass on the
Dog class leads us to
Object, and calling
Object leads us to
BasicObject has no superclass, so the inheritance chain ends there. We can summarize this information in a diagram:
Notice how we have included two instances of Dog in this diagram. This is to illustrate that there can be many instances of a class, and they all have a
.class pointer to their Class. In this example, there can be many instances of Dog that all have the same
Also notice that we call
.superclass on the
Dog class, not the dog instance. What happens if we call
.superclass on a dog instance? Try it to find out.
Because our class inherits from
Object, which inherits from
BasicObject, we know that any class we create will have those two ancestors. This is where
:new comes from. Look in the Ruby Docs BasicObject Page and you’ll see the
:new defined there.
We’ve mapped out the inheritance chain for our dog, but what about the modules? What happens if we call
included_modules on our
pry(main)> Dog.included_modules #=> [AnimalBehavior, PP::ObjectMixin, Kernel]
We can see our
AnimalBehavior module (along with some others), but we included that in
Animal, not in
Dog, so why is it showing up here?
included_modules shows all modules that were included in any superclass, so it won’t tell us where that module was included. In this case, we get more information if we start at the top of the inheritance chain to figure out where modules first appear (you may get slightly different results depending on what Ruby version you are running):
pry(main)> BasicObject.included_modules # =>  pry(main)> Object.included_modules # => [PP::ObjectMixin, Kernel] pry(main)> Animal.included_modules # => [AnimalBehavior, PP::ObjectMixin, Kernel] pry(main)> Dog.included_modules # => [AnimalBehavior, PP::ObjectMixin, Kernel]
From this information, we can deduce that
BasicObject doesn’t include any modules,
Kernel (you don’t need to worry about what those are), and
Our updated diagram:
Chart Paper Exercise
Break into small groups. Grab a chart paper and markers.
.superclass, diagram the Object Model of these several commonly-used Ruby classes: Hash, Array, String, Integer, and Float.
The Lookup Chain
We now have a mental model for how Ruby manages classes, instances, superclasses, and modules, but why does it matter? The biggest implication of the Object Model is the Lookup Chain. We know that we can store methods in several places (class, superclass, module), but what is the exact order that Ruby looks for things? If a method is defined in several places, which one will Ruby use?
Lookup Chain Exercise
Complete this activity. In the first part of the activity, you will map the Object Model for a
Chair instance, just as we did above. Then you will alter the code to explore the order of the Lookup Chain.
Once you have finished the activity, write out the order of the Lookup Chain as concisely as possible.
Other Definitions and Rules
Classes: store instance methods, have a superclass pointer
Instances: store instance variables, have a class pointer
Classesare also instances (of Class)
Classescan only inherit from one other class (its ‘superclass’)
Classescan include multiple Modules.
Modulescan be mixed-in to multiple classes (mixins)
- How does Ruby’s look up chain work? What is the order it checks things?
- What are three methods you can use to learn about where a built in Ruby method gets its components?
- Draw a diagram of where Ruby would look for the method