After writing about the Singleton class in Ruby, it is time to write about the Metaclass.
Note that some people use "metaclass" to mean a normal singleton class (aka eigenclass) but that is incorrect. The Ruby metaclass is a special type of singleton class, one for a Ruby class object (hence the term "metaclass", I guess).
Recapping what we know about the singleton class, in the following example we can guess where the definition of the method find_by_name goes:
class Person def self.find_by_name(name) puts "finding by name: #{name}" # ... end end Person.find_by_name('Aman') # => 'finding by name: Aman'
Yes, find_by_name actually gets defined on the singleton class of the class object referenced by Person, which is why we can invoke Person.find_by_name('Aman') but not Person.new.find_by_name('Aman').
This is how a method invoked on a class object is looked up:
# ... in continuation from previous snippet Person.class # => Class # let 'blah mean the singleton class of blah Person.find_by_name('Aman') # method looked up in 'Person; found; invoked Person.new # method looked up in 'Person; not found; looked up eventually in Class (Person is an instance of class Class); found; invoked
The above look up sequence should not be surprising given what we know about the workings of singleton classes. Now, let's consider this:
# ... in continuation from previous snippet class Employee < Person def self.find_by_employee_id(employee_id) puts "finding by employee id: #{employee_id}" # ... end end Employee.find_by_employee_id(10) # => 'finding by employee id: 10'
Again, we know that find_by_employee_id gets defined on Employee's singleton class, which is why Employee.find_by_employee_id(10) works. But what about Employee.find_by_name('Aman') -- will it work? Let's find out...
# ... in continuation from previous snippet Employee.find_by_name('Aman') # => 'finding by name: Aman'
Yep, it worked!
And it does seem logical, doesn't it? An employee is a person; so the way you search for a person should work to find an employee too.
Given the above expectation, this is how the invoked method is found for class objects:
# ... in continuation from previous snippet Employee.find_by_employee_id(10) # method looked for in 'Employee; found; invoked Employee.find_by_name('Aman') # method looked for in 'Employee; not found; looked for in 'Person; found; invoked Employee.new # method looked for in 'Employee, then in 'Person, then in 'Object, then in Class (not 'Class); found; invoked Employee.foo # method looked for in 'Employee, then in 'Person, then in 'Object, then in Class; not found; NoMethodError
The way Ruby makes the above possible is exactly how metaclasses are different from normal singleton classes:
I hope my attempted explanation clarifies why we need a special metaclass implementation in Ruby and how it is different/enhanced with respect to the normal singleton class.
(For more details, I recommend reading fellow-TWer Patrick Farley's Ruby internals blog series.)