July 24, 2018
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.
Let’s define a method on a class and look where it ended up. Let’s implement a
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.