We’ll start with a cow named
cletus. Cows are ruminant animals. And ruminants are all part of the broader family of ungulates . Today’s topic isn’t actually about mammal taxonomy, though. Our real focus is on how objects find their methods. So we’re going to write a special method to help us with this task. We call it
where, and it will take an object and a method name as arguments. Inside, it looks through the object’s class ancestors until it finds the nearest ancestor to defined the given method. With our tools defined, let’s put Cletus through his paces. Cletus can speak, and when he does, he moos. Cletus get this behavior from the fact that he is a
Cow. When Cletus rests, he chews his cud. That’s because cows are Ruminant mammals. And when he eats, he grazes. This is fairly typical of ungulates, a group that also includes horses, deer, and giraffes.
class Ungulate def eat "graze graze graze" end end class Ruminant < Ungulate def rest "chew chew chew" end end class Cow < Ruminant def speak "moo" end end require "./classes" def where(obj, method) obj.singleton_class.ancestors.detect do |klass| klass.instance_methods(false).include?(method) end end cletus = Cow.new cletus.speak # => "moo" where(cletus, :speak) # => Cow cletus.rest # => "chew chew chew" where(cletus, :rest) # => Ruminant cletus.eat # => "graze graze graze" where(cletus, :eat) # => Ungulate def cletus.dance "tappety tappety tap" end cletus.dance # => "tappety tappety tap" where(cletus, :dance) # => #<Class:#<Cow:0x0055c1fb9c05c8>> Cow.new.singleton_class.ancestors # => [#<Class:#<Cow:0x0055978f993a78>>, # Cow, # Ruminant, # Ungulate, # Object, # PP::ObjectMixin, # Kernel, # BasicObject]
In Ruby, method lookup follows a simple and consistent rule. An object’s method definitions are found via its class inheritance chain. Always. In order for objects to be able to have individual method definitions, they have a singleton class that’s unique to them, and that singleton class is the first place Ruby looks for a method definition.