This was the tip of the week in the August 19, 2021 Ruby Weekly Newsletter.
In the past few tips, we’ve covered ancestors,
include, all in service of learning different ways Ruby allows us to import code into a class. There is one more way to do this, with
extend. However, before we jump straight into learning about
extend, we first need to understand singleton classes.
Singleton classes are key to Ruby’s object model. Let’s say we have an instance of a class, and want to define a method on just that instance:
class ExampleClass ; end example = ExampleClass.new def example.example_method "This is a singleton method" end example.example_method => "This is a singleton method" Example.new.example_method # NoMethodError (undefined method `example_method' for #<ExampleClass:0x00007f9686858a80>)
We see that our
example instance can have its own specially defined method,
example_method, which won’t work on other instances of
ExampleClass. Where is
example_method stored though? How does Ruby keep track of it? It’s defined on
example’s singleton class.
Every Ruby object has a singleton class. This allows us to do things like the above: define methods on instances and have them work only for those instances. We also know that classes in Ruby are themselves objects - and so also have singleton classes. This is actually how class methods work under the hood!
Class methods are really instance methods on a class’ singleton class. Let’s look more deeply:
class ExampleClass def self.example_class_method "This is a class method" end end ExampleClass.example_class_method => "This is a class method" ExampleClass.singleton_class.instance_methods(false) => [:example_class_method]
Singleton classes are deep enough to deserve a whole series themselves! For now though, we’ve learned what we need in order to properly understand how
extend works next week. Namely, we’ve learned that class methods are instance methods on the singleton class of a class.