Hi, I expect are you ready, now we will try to understand what is this "hidden" object in all places when we code in ruby.
If you are a curious programmer, you know that Ruby is a Object Oriented programming language, and beyond, everything is an object in ruby, everything. When we define a House
class, for exemple, this class is also an object, a Class
instance. We can see this in the code snippet below, where we call the .class
method returning the class name of this object.
class House; end
# House is an intance of `Class`
House.class # => Class
This can be different you have seen before and works different from how I thought, so I started to ask how this language feature impacted me as a programmer? I didn't have a good answer and I searched more about this behaviour, until I found the Singleton Class (I also found references calling Eigenclass, Anonymous Class and Object-Specific Class), the responsible for the ruby class methods, like the code snippet below.
class House
def self.open
puts "...Opened"
end
end
House.open # => ...Opened
I believe there is nothing in this code different of we use to write ruby code everyday. Singleton Classes allow us to add methods to pre-defined objects that only affect the object that defines this method, like the code snippet below.
class House
def lawn_situation
puts "...this is perfect"
end
end
my_home = House.new
neighbors_house = House.new
def my_home.lawn_situation
puts "...could be better"
end
neighbors_house.lawn_situation # => ...this is perfect
my_home.lawn_situation # => ...could be better
This also explain how we create the self.open
method in the previous code, since the House
class is an object of the Class
class, so we also use this syntax (self.open
) to add a new method to this pre-defined object.
Now, I understand that the syntaxes def self.open
and def my_home.lawn_situation
are responsible for accessing the Singleton Class of these objects to add the methods and not directly to their classes, allowing these new methods to only affect their respective objects.
I think now we can understand what is this object called Singleton Class and how we use it, but we haven't discussed how it impacts our real life, so we will do it now.
In a conversation with some friends, it was mentioned a use case to this feature, we can to override object methods to help us to create some specs. Below, we used the Singleton Class to override a method to help us to test a code behaviour.
# Defining House class
class House
def self.close
self.close_back_door
self.close_windows
self.close_principal_door
nil
end
def self.close_back_door = puts "...Closed Back Door"
def self.close_windows = puts "...Closed Windows"
def self.close_principal_door = puts "...Closed Principal Door"
end
# Testing .close method
context "Error to close the house" do
def House.close_principal_door
raise EspecificException, "Error to close the principal door"
end
expect(House.close).to raise_error(EspecificException)
end
Particularly, knowing this ruby feature, made me understand clearly about a bug I created when I was developing a new system feature, I will try to illustrate the problem in a simpler context.
class Book
def self.gift_a_friend(friend, book)
@friend = friend
@book = book
return send_now(@book) unless friend_has_this_book?
puts "#{@friend.name} already has the book '#{@book}'"
end
private
def self.friend_has_this_book?
puts "Verifying if #{@friend.name} already has the book '#{@book}'"
@friend_has_this_book ||= @friend.books.include?(@book)
end
def self.send_now(book)
puts "Sending the book '#{book}' to #{@friend.name}!"
@friend.add_book(book)
end
end
class Friend
attr_reader :name, :books
def initialize(name, books = [])
@name = name
@books = books
end
def add_book(book)
@books << book
end
end
joao = Friend.new('J. Moura')
guilherme = Friend.new('Guilherme')
Book.gift_a_friend(joao, 'Lord of the Rings')
# Verifying if J. Moura already has the book 'Lord of the Rings'
# Sending the book 'Lord of the Rings' to J. Moura!
Book.gift_a_friend(joao, 'Lord of the Rings')
# Verifying if J. Moura already has the book 'Lord of the Rings'
# J. Moura already has the book 'Lord of the Rings'
Book.gift_a_friend(guilherme, 'Lord of the Rings')
# Verifying if Guilherme already has the book 'Lord of the Rings'
# Guilherme already has the book 'Lord of the Rings'
guilherme
# => #<Friend:0x000079bd4d8e6d20 @books=[], @name="Guilherme">
# WTF? Guilherme has not the book 'Lord of the Rings'
The problem is in the second time when the .friend_has_this_book?
method is called, it will not execute @friend.books.include?(@book)
because the @friend_has_this_book
instance variable value has already been set. The Singleton Class is created only once (this explains the name Singleton), the following calls will only access the existing Singleton Class keeping its state.
I would like to understood it before, but it is a good starting for who want to know deeper about the ruby language and its features, these knowledge will avoid the errors and help us to analyze the problems you will face as a programmer.
I hope you enjoyed, see you.
Some sources I used to understand about this subject:
Top comments (0)