Every piece of Ruby metaprogramming, and much Rails magic, follows directly from the object model.
In Ruby, classes are objects too: User is an instance of Class, which inherits from Module, which inherits from Object. Everything else follows from a few rules.
Method lookup: when you call obj.foo, Ruby looks in obj's singleton class, then its class, then each entry of obj.class.ancestors in order: prepended modules, the class, included modules (most recent first), the superclass, and so on up to Object, Kernel and BasicObject. The first match wins; super continues from there. If nothing is found, Ruby calls method_missing, which raises NoMethodError by default.
Singleton classes: every object can have its own hidden class holding methods for that object alone. def obj.shout or obj.extend(Mod) puts methods there. Class methods are simply singleton methods of the class object: def self.create inside class User defines create on User's singleton class. class << self opens that singleton class directly. Singleton classes of classes inherit from each other, which is why subclasses inherit class methods.
self changes with context: inside a class body it is the class, inside an instance method it is the instance. Calling a method without an explicit receiver sends it to self, and private methods can only be called that way.
Open classes let you reopen any class, even String, and add methods. This monkey patching is powerful and dangerous; prefer refinements or plain functions.
Example
module Greet
def hi = "hi from Greet"
end
class Base
def hi = "hi from Base"
end
class Child < Base
include Greet
def hi = "Child -> " + super
def self.build = new # singleton method of the Child object
end
p Child.ancestors.take(4) # [Child, Greet, Base, Object]
puts Child.new.hi # Child -> hi from Greet
obj = Child.build
def obj.hi = "only me" # singleton method on one instance
puts obj.hi # only me
puts Child.new.hi # Child -> hi from Greet
p obj.singleton_methods # [:hi]
p Child.singleton_class.superclass # #<Class:Base>
p Child.class, Class.superclass # Class, ModuleRemember
- Lookup goes singleton class, then ancestors in order; first match wins
- Class methods are singleton methods on the class object
- Calls without a receiver go to self; private methods require that
Watch out for
- Monkey patching core classes globally, causing conflicts between gems.