Where The Wild Methods Are

Nearly all of the time when we’re methods in Ruby, we’re defining instance methods on some class or module. I bet nearly all of the method definitions in the applications you work on follow one of these forms:

class SomeClass
  def self.class_method()
    # Either you're defining a class method like this.
  end

  class << self
    def other_class_method()
      # Or you're defining a class method like this.
    end
  end

  def instance_method()
    # Or you're defining and instance method like this.
  end
end

Let’s ignore the first case for now; we’ll come back to it. The second two cases are really just the same. You’re defining an instance method on something.

As I mentioned in my most recent article about Rake, a given context in Ruby has two important attributes: the value of self and the current class. The current class dictates where the methods we define will actually end up.

The Most Basic Example

Let’s define a method on a class and look where it ended up. Let’s implement a Potato class.

class Potato
  def grate
  end
end

Ruby has a whole bunch of tools for introspecting classes, but for our purposes we only need to use #public_instance_methods, which returns all the instance methods of the receiving class. If we pass in false, it will only return the methods defined on the class itself and not the ones from its ancestors.

Potato.public_instance_methods(false)
#=> [:grate]

We defined a class with one public instance method, and this shows us that the class does in fact have that one public instance method on it. Perfect.

What about when we define class methods using the class << self idiom? The syntax for defining the method is exactly the same, so it follows that we must be defining a public instance method on some class, but which one?

class Potato
  class << self
    def russet
    end
  end
end

Yehuda Katz did a great job of explaining metaclasses over here, but the short version is that inside a class << SomeClass block both the current class and self are the “metaclass” (or “singleton class”) of SomeClass. For our snippet above, that means with must have defined an instance method on the singleton class of Potato. Let’s verify:

Potato.singleton_class.public_instance_methods(false)
#=> [:russet]

Ruby’s object model is pretty elegant once you dive under the hood, and we’re going to see explore that over the course of this article. “Class methods” are just instance methods on the singleton class of a class.

by Jared Norman
24 July 2018

Subscribe to stay up to date on the latest programming techniques and get Super Good articles delivered straight to your inbox!