Fork me on GitHub

Project Notes

The Case of the Missing Method

Or to mix literary metaphors, where in the world do class methods get stored? An investigation into the sublime and elegant design of metaclasses in ruby.


I heard about Nadia Odunayo’s brilliant talk The Case of the Missing Method: A Ruby Mystery Story from the Chats in the Cupboard podcast.

This particular talk (which is a master class in designing and presenting a talk in its own right) introduced us to the mysterious singleton_class in ruby.

Every Class has an associated Singleton Class, and this is where class methods are defined (not in the class itself).

As Nadia explains, this approach is a good example of the minimalist and elegant design of ruby itself:

  • “class methods” are implemented under the cover as just plain old “instance methods” defined on a Singleton Class.
  • and the Singleton Class is just a plain old class that just happen to be defined as the singleton_class instance variable of the class in question.

This topic is also covered in “Experiment 5-2: Where Does Ruby Save Class Methods?”, page 127 of Ruby Under a Microscope by Pat Shaughnessy.

Nadia Odunayo - The Case of the Missing Method: A Ruby Mystery Story


Exploring the Ideas

See example.rb for code notes from the talk…

$ cat example.rb
#! /usr/bin/env ruby
require 'minitest/autorun'

class Cake
  def initialize(flavour)
    @flavour = flavour

  def tasty?
    @flavour == 'carrot'

  def self.edible?

class CaseOftheMissingMethod < Minitest::Test
  def setup
    @carrot_cake ='carrot')
    @mud_cake ='mud')

  def test_carrot_cakes_are_tasty
    assert @carrot_cake.tasty?

  def test_other_cakes_are_not_tasty
    assert !@mud_cake.tasty?

  def test_all_cakes_are_edible
    assert @carrot_cake.class.edible?
    assert @mud_cake.class.edible?

  def test_instance_methods_are_defined_on_the_class
    assert Cake.instance_methods.include?(:tasty?)

  def test_class_methods_are_not_defined_on_the_class
    assert !Cake.instance_methods.include?(:edible?)

  def test_class_has_superclass_and_singleton_class
    assert_equal 'Cake', Cake.to_s
    assert_equal 'Object', Cake.superclass.to_s
    assert_equal '#<Class:Cake>', Cake.singleton_class.to_s

  def test_class_ancestors_does_not_include_the_singleton_class
    assert_equal [Cake, Object, Minitest::Expectations, Kernel, BasicObject], Cake.ancestors
    assert Cake.ancestors.include?(BasicObject)
    assert !Cake.ancestors.include?(Cake.singleton_class)

  def test_class_methods_are_defined_on_the_singleton_class
    assert Cake.singleton_class.instance_methods.include?(:edible?)

Results of course:

$ ./example.rb
Run options: --seed 51067

# Running:


Finished in 0.002050s, 3902.4391 runs/s, 6341.4636 assertions/s.

8 runs, 13 assertions, 0 failures, 0 errors, 0 skips

Credits and References

About LCK#253 ruby
Project Source on GitHub Return to the Project Catalog

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.