This was the tip of the week in the August 19, 2021 Ruby Weekly Newsletter.


In the past few tips, we’ve covered ancestors, prepend and 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.