The Ruby programming language allows for you to extend and modify any class or object’s implementation.
Every Object Is a Class and Every Class Is an Object
It’s important to understand the almost Zen like implementation of Ruby: “Every class is an object and every object is a class.”
Why is that important? Because what you can do to an object you can do to a class and vice versa.
In the following example, I’m using IRB syntax.
The line that starts with
> is the prompt. The line that starts with
=> is the output of the prompted line’s evaluation.
Let’s define the
Book class; nothing special.
> class Book; end => nil
Let’s instantiate an instance of
Book by calling
Book.new method. We’ll call that instance
> dune = Book.new => #<Book:0x0000000106b56ce0>
And let’s look at the the
dune instance’s “class” methods:
> dune.methods.grep(/class/) => [:singleton_class, :class]
class method for
dune returns the
> dune.class == Book => true
What about the
singleton_class? It’s not
Book, it’s that instance’s eigenclass.
> dune.singleton_class => #<Class:#<Book:0x00000001006567a0>> > dune.singleton_class == dune.class => false
Why is the
class important? It is a conceptual foundation of the
extend method calls.
For completeness, let’s look at
Book\’s class method.
> Book.methods.grep(/class/) => [:superclass, :subclasses, :class_variable_set, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_defined?, :singleton_class?, :class_exec, :class_eval, :public_class_method, :private_class_method, :singleton_class, :class]
There are more
Book class-like methods than class-like methods for an instance of
Book. Let’s look at both the
class methods for
> Book.class => Class > Book.singleton_class => #<Class:Book>
At this point pause a moment. Get a drink of water. I don’t know if that was a lot (or not) but the conceptual difference between the
Book class and an instance of a
Book is important.
Extend versus Include
Let’s look at our Book example.
First We Explore Include
module Published # All book shall be published! def published? true end end
First lets look at the
include behavior; this adds methods to instances of a class.
class Book include Published end
All instances of the
Book class will now have the
> Book.new.published? => true
Book class will not have the
> Book.published? => undefined method `published?` for Book:Class (NoMethodError)
In fact, you can use the introspection methods
instance_methods to check what methods are defined on the class and instances of the class.
> Book.methods.include?(:published?) => false > Book.instance_methods.include?(:published?) => true
Then We Explore Extend
extend behavior adds method’s to the class.
Let’s create a new module.
module Exciting def exciting? true end end
And let’s have the
Book class extend the
> Book.extend Exciting => Book end
Book class now has the
> Book.exciting? => true
But an instance of
Book does not.
> Book.new.exciting? => undefined method `exciting?' for #<Book:0x0000000104441470> (NoMethodError)
A quick mnemonic to remember the above: include is for instances (and extend is for classes).
Let’s Extend an Object’s Singleton Class
With the above
Exciting module, let’s loop back to the every object is a class.
> left_hand_of_darknes = Book.new => #<Book:0x000000010523ccc8> > left_hand_of_darkness.extend Exciting => #<Book:0x000000010523ccc8> > left_hand_of_darkness.exciting? => true > Book.new.exciting? => undefined method `exciting?' for #<Book:0x00000001008dc510> (NoMethodError)
In the above, we extend the
singleton_class with the
Exciting module. And for this instantiation, that object has an
exciting? method. But other instances of
Book still do not have the
Now, should you “extend” the instance? Likely not, but I have in my years of Ruby found two occasions where it was the right approach. I don’t recall them now, but remember that it elegantly and easily solved a problem.
This tour of the difference between a class and an instance helps lay the conceptual foundations of Ruby’s object model. My intention in this perhaps esoteric detour is to highlight the malleability of Ruby.
In upcoming articles, I’ll build on these concepts.