Motivation
Every time I'm learning a new language, the first thing I try to learn is how this new language
implements OOP, and now it's Ruby's turn. Before joining the company, I had been playing with Java for some
years; then I first heard about Ruby being a fully OOP language I got really excited, and
when I got my hands-on some existing Ruby code, things got confusing.
I would've liked to know all of this before looking into any code:
First things first: Objects
Things that we must know:
- An object is formed by: State + Behavior = Object
- Everything in Ruby is an object, even nil which is NilClass, false FalseClass, true TrueClass and modulesruby-1.8.7-p299 > "some".class
=> String
ruby-1.8.7-p299 > "some".class.superclass
=> Object
ruby-1.8.7-p299 > "some".class.superclass.superclass
=> nil
ruby-1.8.7-p299 > "some".class.superclass.superclass.class
=> NilClass
ruby-1.8.7-p299 > false.class
=> FalseClass
ruby-1.8.7-p299 > true.class
=> TrueClass
ruby-1.8.7-p299 > module B end
=> nil
ruby-1.8.7-p299 > B.class
=> Module
ruby-1.8.7-p299 > B.class.superclass
=> Object - Do you remember those so-called 'static classes' in other languages? They're objects too,
and when I say objects, I really mean it. Consider the following code stored in test.rb file:class A
def self.outside
self
enddef inside
self
end
end
Let's play with it using irb:
ruby-1.8.7-p299 > require './test'
ruby-1.8.7-p299 > A.outside
=> A
ruby-1.8.7-p299 > A.outside.class
=> Class
ruby-1.8.7-p299 > A.new.inside
=> #
ruby-1.8.7-p299 > A.new.inside.class
=> A
As you can see both are A objects whose type is Class
- Those class names are constants containing references to objects:ruby-1.8.7-p299 > clazz = Class.new
=> #Class:0x7f8fdec83230
ruby-1.8.7-p299 > clazz.class
=> Class
ruby-1.8.7-p299 > Blabla = clazz
=> Blabla
ruby-1.8.7-p299 > Blabla.class
=> Class
ruby-1.8.7-p299 > Blabla.new
=> #Blabla:0x7f8fdec77a70
Self
When I first knew Ruby I thought "self" was the same as this (like in many other languages). Boy was I wrong. Let’s see what it really is:
Default receiver of method calls
Since everything in this type of code is an object, then all the function definitions we find established all around
and all those functions we call like, puts, def, etc are method calls. Some may not be exactly defined inside a class but, in the end they will get either inherited or mixed in.
Look at the following example:
p "1. Current receiver is #{self}"
class A
p "2. Current receiver is #{self}"
def initialize
p "3. Current receiver is #{self}"
end
p "4. Current receiver is #{self}"
end
p "5. Current receiver is #{self}"
Running this script we see:
user@user-desktop:~$ ruby test.rb
"1. Current receiver is main"
"2. Current receiver is A"
"4. Current receiver is A"
"5. Current receiver is main"
Where instance variables are found
class A
def initialize
@a, @b = 2, 1
end
end
p A.new.instance_variables
Running this script we see:
user@user-desktop:~$ ruby test.rb
["@b", "@a"]
Metaprogramming
This is why I fell in love with ruby, sadly it's a huge topic which I won't cover, even though it allows you
to write code that writes code lets see some examples:
Create a A Class, re-open it and see how it is the same object
class A
def method1
p "I'm method one"
end
end
p A.instance_methods false
p A.object_id
class A
def method2
p "I'm method two"
end
end
p A.instance_methods false
p A.object_id
Which throws:
user@user-desktop:~$ ruby test.rb
["method1"]
70312385439840
["method2", "method1"]
70312385439840
As you can clearly see, the object id remains the same, after re-openning a class in order to add a second method, also,
we got all instance methods that one class of A would have had.
Call all instance methods of a certain class whenever you call a method whose name matches
class A
def hello
p "Hello"
end
def bye
p "Bye"
end
def welcome
p 'Welcome'
end
def method_missing(method_name, *args, &block)
method_to_call = methods.find {|name| name =~ /#{method_name.to_s}/}
send(method_to_call) if method_to_call
p 'Unknown method' unless method_to_call
end
end
instance = A.new
instance.els
instance.hel
instance.by
instance.wel
Which throws:
user@user-desktop:~$ ruby test.rb
"Unknown method"
"Hello"
"Bye"
"Welcome"
That's it. I hope this gives you a good overview and if you are ready for the whole package I
suggest you start with:
- Some good metaprogramming screencasts over at Pragmatic Programmers screencasts
- An in-depth look at Rafa Magaña's presentation on Ruby object model