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)