Everything is an Object
There are relatively few primitive and many things in Ruby are expressed in terms of objects and methods.
An object is a collection of data and methods. Methods return the object’s data or manipulate it.
Ruby is dynamic typing. Variable can refer to objects of different types.
Terms
The terms related to Object Oriented in Ruby
- Instance Variable
- Instance Method
- Object Initialization
- Destruction
- Accessor
- Virtual Attributes
- Class Method
- Class Variable
Instance Variable @
We are setting an instance variable called title. Instance variable in Ruby are denoted with @
. They are called instance variable because each instance of the book class is going to have its own copy of title string.
|
|
Before we call title=
method, the title variable doesn’t exist in the object. The equal sign in the method name may look unusual coming from other languages. It’s there to indicate that the method is a write accessor and to allow you to write b.title = the name of the book
Let create a book instance b = Book.new
and put new title b.title = "How to Code"
. There should be no problem. But when we try to access the title by run b.title
. It will produce an error.
new
is so-called constructor, and it is and example of a class method. We can think of a new method as a factory which stamps out an object based on the blueprint defined by a class.
Open Classes
Now we’re going to add a class to read book title by running
|
|
This feature is called Open Classes.
Accessor
Writing all the read and write accessor methods (getter and setter) like previous manually would be quite an exercise in typing. Fortunately, we have a convenient alternative. We can use attr_accessor
to provide read and write accessors for an instance variable. Both code below are equivalent.
If we want to limit the user to write only, we can use attr_write :title
. If we want to limit user to read only, we can use attr_read :title
.
We can use attr
to multiple fields by separating them with coma ,
like attr_accessor :title, :author, :pub_year
.
When you use accessors inside a method in a class use self.
before the variable.
|
|
More about self
read this article https://www.rubyguides.com/2020/04/self-in-ruby/
Initialize
Is a method for creating instance by passing argument to constructor method new
.
Creating new object with acessor method like code below is cumbersome and error prone.
|
|
We can write simpler using Book.new(title: "Code", author: "Ruby Red", pub_year: 2020)
. To use this method we should define initialize
method in the class like
|
|
Virtual Attribute
If an accessor method doesn’t directly get or set an instance variable, it defines a virtual attribute.
Class Methods
- is called on a class instead of an object
- new is an example of a class method
- is independent of specific object state, but still related to the entity modeled by a class.
For example, we want to add search feature in Book
class and the convenient would be to call Book.find
. To define a class method, you need to prefix its name with self dot self.
Class Variable @@
Class variable maintain class-level state which can be referred to by class methods. For example, we might want to keep track of the number of search queries for books.
|
|
A class variable is denoted with a double @@
and initialized in the body of the class. There is only one copy of a class variable per class and it’s shared between all objects. A class variable isn’t visible outside the class so we have to write accessor class method for it if we need to access it from the outside.
Class Instance Variable
What happens with class instance variable is that the parent class and each of the subclasses has its own copy of the variable but only one per class.
Let’s define a collection class with a class instance variable for search_count.
|
|
As with class variables, I’am setting the variable in line 2, but note that single @
instead of double. Instance variables which are set in the class body or in a class method become variables in the class itself rather than in objects.
Now we can create a specialized collection class with its own search_count
, for example, for book series. And like with class variable, the class instance variable also needs to be initialized in each subclass.
|
|
Now try to run Collection.find
once, and Series.find
twice. Then for Collection.search_count
will result 1 and for Series.search_count
yield 2.
Operators
An interesting feature of Ruby is that many operators are implemented as method calls on objects. They just have a little bit of syntatic sugar on top so using them doesn’t look like calling methods.
Let’s see if this can be applied to the Collection class. The collection contains an array of book objects, and we might want to provide convenient array-like access to it without exposing the actual book array, because we don’t want it manipulated in arbitrary ways. We can do it by defining a square brackets operator.
|
|
Adding an append operator may be handy for populating Collections with books.
|
|
Now you can add collection with this way
|
|
It’s even possible to define unary operators for a class. For example, we could redefine the not operator to return false when a Collection
is empty
|
|
This would allow you to test the Collection state in conditionals like this.
|
|
Class Equality
Sometimes we may need to check whether an object is an instance of a particular class. For example, when generating the HTML for different types of collections, you might use a case expression to branch on collection class
|
|
This means that the collection class has to be compared with the classes specified in the when clauses.
There are a couple of other options:
- kind_of? or is_a, they check instance of parent as well, example:
series.kind_of(Book)
- instance_of?_, is more strict, only work if it in the same class, example:
book.instance_of(Book)