In the last blog post we introduced the concept of Object-oriented programming in Ruby, we talked about the different programming paradigms and we created our first Class and Objects. Just to recap a bit about the code we wrote in that blog post, we finished with something like this
#1. Class declaration
class Person
# 2. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#3. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#4. Printing out and calling a class method in some of them
puts p1.greet
puts p2
puts p3.greet
As we can see we have 4 steps
- Step #1 - Class declaration. Is where we give a name to the class and we open the class block
- Step #2- We have a method. This means the behavior or logic to apply in each instance that we’ll be creating outside the class
- Step #3- Instantiation. Once we close the class declaration in a block, we start to create the instances of the class. All instances are defined by the class name followed by the keyword “.new”
- Step #4- Finally we call the class method using the variable where we assigned the instantiation.
As you can see we’re following some order given by the steps. And that’s good, it hepl us to have in mind what’s the basic behavior of the classes and objects. Remember that in this example, the objects are the instances (which is a synonym inside the programming word). Now let’s see something more about the OOP in Ruby
Functions vs. Methods
Now that we know a bit more about OOP in ruby and Ruby about
Ruby functions is time to see what's a method in Ruby. Actually, it is the same thing as functions, but with a slight difference.
* A method is a function wrapped inside a class. A class is how we declare objects in OOP in Ruby. It contains the expected behavior of the class
* Functions are declared outside classes. It contains the expected behavior of part of the program
In the last exercise we have a method called “greet”
class Person
….
def greet
"Hello world!"
End
….
end
Then outside the class we called it after instantiation as follows:
p1 = Person.new
puts p1.greet
To call a method on an object (or instance) you must add a dot followed by the method name (p1.greet). These methods are also known as instance methods, because the only way to invoke them is on the object.
The constructor method
As you may already know, you can give any name to the methods (also to the functions). However inside the Ruby classes we’ll have a method known as the “constructor” method. This method has to be declared with a special name called “initialize” and as any other method (or function) can receive or not, parameters.
This concept tends to confuse beginners, but it is not more than “preload” or having a method ready for each instance of the method. Let's say that each time you need a new object (or instance), you need some pre-loaded or default values and these values could be different from object to object.
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize(name)
puts "creating the person #{name}"
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#4. Instantiation
p1 = Person.new("Ana")
p2 = Person.new("Daniel")
p3 = Person.new("Matz")
#5. Printing out and calling a class method in some of them
puts p1.greet
puts p2
puts p3.greet
What do you see differently here? Please take a while to try to catch the differences.
In the second step we now have the constructor with the method name initialize who receives a parameter called “name”. Then that parameter is used in a string under the initialize method.
In the fourth step we instantiate not the class as follows: p1 = Person.new(“Ana”). Here is the thing that tends to confuse. Why are we calling a parameter after the “.new”? Because we declare an initializer inside our class, and this initializer receives a parameter, we have to pass that value when we do the instantiation. If we don’t do that we receive an error.
Now let’s try the code in our console
➜ blog_tutorials ruby oop.rb
creating the person Ana
creating the person Daniel
creating the person Matz
Hello world!
#<Person:0x00000000029d7888>
Hello world!
As you can see, now we have 3 new outputs
creating the person Ana
creating the person Daniel
creating the person Matz
This is because we instantiated 3 times the class
Awesome, now let’s try it without passing the parameter
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize(name)
puts "creating the person #{name}"
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#4. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#5. Printing out and calling a class method in some of them
puts p1.greet
puts p2
puts p3.greet
The class is exactly the same as before, but now, in Step #4 We try to instantiate without a parameter. We have the next error:
➜ blog_tutorials ruby oop.rb
oop.rb:4:in `initialize': wrong number of arguments (given 0, expected 1) (ArgumentError)
from oop.rb:15:in `new'
from oop.rb:15:in `<main>'
Now as we mentioned before, the initialize method can come without a parameter, let’s check
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize
puts "creating the person"
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello world!"
end
end
#4. Instantiation
p1 = Person.new
p2 = Person.new
p3 = Person.new
#5. Printing out and calling a class method in some of them
puts p1.greet
puts p2
puts p3.greet
What do you notice differently? Try it by yourself. This code works now.
➜ blog_tutorials ruby oop.rb
creating the person
creating the person
creating the person
Hello world!
#<Person:0x0000000002553730>
Hello world!
Attributes (object information)
Just as an object has behavior, objects (or instances) can also have information, or attributes. A person can have a name, age, height, and so on. These are the attributes. Think of attributes as variables that are associated with the object.
In Ruby we will identify attributes in a class because they begin with the @ character. For example, we can store the argument coming from the constructor inside the Person attribute. Why is this important? Because once we have an instance variable in the constructor, we can use it in any other method. Let’s see how
#1. Class declaration
class Person
#2. Constructor method called "initialize"
def initialize(name)
@name = name
end
# 3. Behavior or logic to apply in each instance
def greet
"Hello #{@name}!"
end
end
#4. Instantiation
p1 = Person.new("Ana")
p2 = Person.new("Daniel")
p3 = Person.new("Matz")
#5. Printing out and calling a class method in some of them
puts p1.greet
puts p2
puts p3.greet
What do you see differently?
In the Step #2 we’re now using an instance variable called @name and assigning it the value we receive in the parameter of the initializer. Then in Step #3 We used that instance variable inside the “greet” method. This is amazing and is the real usage of the initializer method: it helps us to store preloaded or default values to use in any other method inside the class.
I think this is enough information to study in this blog post. Later we’ll see more details about the attributes visibility.
I hope you learned a lot.
Thanks for reading!
Daniel Morales