In the previous post, I talked about this
in JS and self
in Ruby.
All the following contents are modified from the talk Dave Thomas give at 2009 Scotland Rails The Ruby Object Model and Metaprogramming in Ruby: It’s All About the Self by Yehuda Katz.
Everything is an object
I said:
Because JS is not like classic OOP, You cannot create another object from the mold(class). Although, it has some similar way(prototype) that you can work like a classic OOP.
Well, I was wrong about Ruby
. There is not such things that you create an object from the definition class. You might heard that In Ruby, everything is object
. I am confused at first. How could it be possible? class
is not an object, you create a thing from the class
that define what properties and methods that a object has. So how could it be possible that class
is an object?
class is an object called
Class
. it is different from java or C# that class is a definition
We use the analog that class is a mold of an object
. So mold is also an object.
Let’s start with this easy code:
animal = 'my cat'
animal.upcase #MY CAT
animal.object_id #8953720
animal.bark #NoMethodError
You assign the string “my cat” to the variable animal
, when you call animal.upcase
, the variable animal
don’t have the methods, so it look right to the class String
, there is an methods named upcase
. And if we call the object_id
method, but it cannot find in the String
class. It then look up at the Object
class. Bingo, it finds the method.
String.ancestors #[String, Comparable, Object, Kernel, BasicObject]
You can check the method-look-up chain of a class in Ruby by calling ancestors
. If a method cannot find, it will go up to search until it hits the BasicObject
. If there is no result there, it will return NoMethodError
. If we draw a diagram here:
So, we can have a little deduction here:
- If we can not find a method, we first go right, then we go up.
- We can think
class
as acollective box
of method for an object. - All the class in Ruby is an object that contains a table of methods.
It is object-oriented, not class-oriented programming
Actually, Ruby knows nothing about the classes, and has no idea on it If you look up the RDoc
, you read the first line in Class
Classes in Ruby are first-class objects—each is an instance of class Class.
So it is the instance of class Class
. For example, String.class => Class
. It means class String
is an instance of Class
.
class Animal
end
MyCat = Animal.new
When
Animal.new
is called to create a new object, thenew method
in Class is run by default.
Give an object a method
Now, we give my cat
with some action:
def animal.speak
puts 'meow'
end
animal.speak # meows => nil
You give my cat
with the ability to meow
.
So the same here. Ruby search if there is a method in the my cat
? No. Is there a speak
in String
? No. Where is the method ?
When you find a method in Ruby, first go to the right, then go up.
Ruby would not want you to see this class
, so we call it ghost class
, some people will call it meta class
or singleton class
. the speak
method is hiding here.
animal.class # => String
See? you can not access this ghost class
, if you call the class
on the object.
Let us recap here:
if you invoke speak method, ruby go right to search for it, but it couldn’t find one, so it simply insert the class of that animal object that contains this speak method. We create a thing here that the class pointer of our animal to be that thing.
class << animal
puts self
def climb
puts 'I can climb'
puts self
end
end
animal.climb # I can climb => nil
Uhh… What’s going on here? That is another way you give an object a method. In the line 2
, if you puts out the self
here, as soon as you define the ghost class
, it will execute puts self
.
It tells you that self
here is #<Class:#<String:0x00>>
. And if you puts self
inside the climb
method, it tell you that self
here is the object “my cat”.
You might already have known that self
in the Ruby will change when you define the class
. But It also change when you make a method invoking.
class D
def one_method
@a = 1
two
end
def two_method
puts @a
end
def three_method
puts self
end
end
d_object = D.new # self is set to the d_object
d_object.one_method # we don't change self here(it is the same, self point to d_obejct, ruby search from two method of the d_object)
d_object.three_method #<D:0x00000001183d68> => nil. self now point to the D class.
More about self
Ruby take animal
as a receiver, and set self
to be the receiver. Before anything going to happen, Ruby set self
to “my cat” object. Ruby then looks for the method at the method-look-up chain. After execution, self
pop back to the original state.
It comes down to the fact that all Ruby code is executed code–there is no separate compile or runtime phase. In Ruby, every line of code is executed against a particular self.
puts self
class Matz
puts self
end
puts self
# main
# Matz
# main
- every line is executable, even when in class definition
self
can change when method calling.
Do you know how to define a class method
?
class Matz
def self.speak
puts "I can speak Japanese"
end
end
Matz.speak
Easy! Just like that. self
here set to the Matz
. So If we define a class method. That’s how we do it. But by now, you already have known something:
class
is an object ofClass
- There is no such things about class.
So Just like singleton class
, Ruby goes right and builds the ghost class
and it finds the speak
method there. So Matz.speak
can prints out “I can speak Japanese.”
So you can also define:
class Matz
class << self
def speak
puts "I can speak Japanese"
end
end
end
Matz.speak
Now we know that <<
(less than less than) open up the ghost class
, then we can dump the methods into it.
Almost every beginner in Ruby have some issue with the following code:
class Matz
@home = "Japan"
def gohome
@home
end
end
m = Matz.new
p m.gohome # => nil
There is a bug in Ruby? why m.gohome
return nil
? Because instance variable
are always set to self
. self
in line 2 is set to the class object
. In the line 5, self
here is set to the m object
. And they are totally different object. So you can not access the instance variable of the Matz
.
class Matz
@home = "Japan"
def self.gohome
@home
end
end
p Matz.home # => "Japan"
class Matz
@home = "Japan"
class << self
attr_accessor :home
end
end
p Matz.home # => "Japan"
Matz.home = "US"
p Matz.home # => "US"
That’s it. That’s what Ruby talk about everything is an object
. And it is the object-oriented programming.