These are my notes from Dave Thomas's Ruby Object Model.
# Ruby Object Model
# http://www.engineyard.com/blog/community/scotland-on-rails/page-2/
# everything is an object
# object is a class
# class is an object
# self:
# - current object
# - where instance vars are found
# - default receiver for methods calls
# changes in self changes ruby's view of the universe
# by: methods calls & class/module definitions
animal = "cat"
puts animal.upcase
# find animal object -> find table of methods -> call method
# class => object that has a table of methods
# every single method call works exactly the same way
puts animal.object_id
# if can't methods in 1st table => goes to parent
# (superclass of the object)
# Object toplevel in Ruby 1.8, BasicObject in Ruby 1.9
# If no matching methods found in ruby, you are back
# to original object and look for method_missing method.
def animal.speak
puts "meaow"
end
animal.speak
# --- SELF changes on method call
# Method lookup -> one right and one up
# So the above object instance method sits
# one to the right of that particular object.
# So a newly created class just for the animal
# object is anonymous. But when asked what class
# the above object belongs to, it says string,
# and not the newly created anonymous class.
# AKA, singleton class or metaclasss or eigenclass
# How is the animal.speak method defined?
# (1) Ruby sets "self" to the cat string object -- self
# set to the receiver
# (2) Looks for the method in the method table associated
# with that object (look in self's class for the object)
# (3) If not found, scan for method_missing
# (4) Eventually find some method & invoke it & when done,
# restores original value of self.
#
# If a method call in Ruby doesn't have an explicit
# receiver -- then Ruby doesn't reset self -- self
# stays the same & no need to pop back at the end.
# So the method "puts" is a private method in Object (actually
# in Kernel), so you can only call it with an implicit receiver,
# not an explicit one.
class String
puts "in string"
end
begin
"hi".puts
rescue NoMethodError
puts "puts is private"
end
# In Ruby class definitions are executable code.
puts "===class defn"
puts "toplevel #{self}"
class << self
puts "class << self's #{self}"
end
class A
puts "class A's #{self}"
end
module B
puts "module B's #{self}"
end
# When you define a class like "class A; end",
# Ruby creates a brand new class and assigns that
# to the constant A.
# When excuting a class or module definition, self
# is set to the class or module object. Why? You can
# do "def self.a" for class methods (or old school
# ruby programmers using "def A.a"); But ruby doesn't
# have class methods (or static methods), all methods
# are the same. These methods are just defined on the class.
# It inserts a singleton class on that particular class object.
# To do metaprogramming in Ruby:
# - Instance variables are looked up in self
# - Methods are looked up in self's class
# these 2 are the same:
def animal.do
puts "doing"
end
# "class << animal" means open up the singleton class of this object
# self is set to that singleton class
class << animal
puts "class << animal: #{self}"
def do
puts "doing"
end
end
class N
# opening up the meta/singleton class and dumping all the
# methods defined there
class << self
# class methods
end
end
# This doesn't work
class F
@a = 10 # self is class object
def f
@a # self is the instance
end
def F.f # this work
@a
end
class << self # this also works
attr_accessor :f
end
end
class Y; end
class Z < Y; end
# the Y in "Z < Y" is an expression
wxyz = Y
class Z < wxyz; end # same as above
a = Struct.new(:a, :b, :c) # creates a class object on the fly
puts a.new(1,2,3)
# Struct is just a data holder
class T < Struct.new(:a, :b)
end
# include Module in a class, then invoke the instance methods
# of the module with class as the receiver
# It doesn't add the methods; any instance methods define
# in class overrides the module methods, even if it is included
# after the method definition.
module M
def speak; puts :no; end
end
class AM
def speak; puts :ok; end
include M
end
am = AM.new.speak
# include => singleton as immediate parent of my class
# and its methods are methods of my module
# hierarchy => singleton, class, module