The basics of Object-Oriented Programming in Ruby
Tags: ruby, beginners, oop, webdev
Published: 2022-11-09 23:03:39

In this post I will be going over Object-Oriented Programming (OOP) and object-orientation in Ruby. If Ruby is your first OOP you may have come across this concept and perhaps found it a little tricky, well you're not alone! We'll go over what object-oriented programming is and its application in the Ruby language.


# What is Object-Oriented Programming?

Wikipedia defines OOP as follows
> Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).

In other words, the underlying concept of OOP is that **everything is an object**. Objects, as we know, contain data in the form of attributes (also known as properties) and functions that run some code (also known as methods). The most common form of OOP is [class-based](https://en.wikipedia.org/wiki/Class-based_programming). In class-based OOP, objects are defined via **classes**, which act as an abstract, reusable template (or blueprint) for instances (objects) of that class. For example, an object(aka *instance*) named `bob` could belong to a class named `Person`, or an object named `dog` could belong to a class named `Animal`, and so on. A class can also define the names of attributes and/or methods an instance of said class should have, the instance will then *inherit* the attributes and methods of the *parent* class when it's created.
 
Let's look at an example:

![](https://i.ibb.co/VC6d4Zz/car-class.png)
######Source: [https://www.educative.io/blog/object-oriented-programming](https://www.educative.io/blog/object-oriented-programming)

In the above example, there is a class (*Car*) that contains attributes representing the color, brand, and model of a Car object. We can then instantiate 2 objects from the class Car (myCar and helensCar) that will inherit the attributes and methods of its parent class (**Car**).

Here's an example of what this looks like in Ruby:

```ruby
#Classes in Ruby and other OOP languages follow the camel case rule; names must be uppercase and no spaces are allowed.
class Car
  def initialize(color, brand, model)
    @color = color
    @brand = brand
    @model = model
  end

#Instance methods are declared in the parent class and are available across all instances of the parent class.
  def repaint(new_color)
    @color = new_color
  end
end

#Instantiates a new object (charger) with the .new method
charger = Car.new('red', 'Dodge', 'Charger')

p charger 
# => #<Car:0x00000000012973d0 @color="red", @brand="Dodge", @model="Charger">

charger.repaint('blue')

p charger 
# => #<Car:0x00000000012973d0 @color="blue", @brand="Dodge", @model="Charger">
```

An object is instantiated by calling the `.new` method on a class followed by the arguments that will become the instance variables (attributes) of that instance. Any methods declared in the parent class will automatically be inherited. In order to create an object with the instance variables of the parent class, a special method is needed: `initialize`.

##The Initialize method and instance variables/methods.
When instantiating an object, more often than not you will be passing one or more arguments to create instance variables for that object. In order to accomplish this, you will first need to declare a special Ruby method in the parent class, called `initialize`.

Lets look at that method from the previous example:

```ruby
def initialize(color, brand, model)
    @color = color
    @brand = brand
    @model = model
end
```

If you've come across object-orientation in other languages, such as JavaScript, this is also known as a [constructor method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor). It is a special method that is used for creating and initializing an instance object of a class.

You'll notice that within the method are variables being declared with a leading @ character. The @ character symbolizes an **instance variable**. Instance variables are variables that are available across all instances of a class. In the previous example, every object that's instantiated from the `Car` class with have the variables `@color`, `@brand`, and `@model` with their own given values. The `repaint` method is an **instance method**, and thus will also be available to all instances of that class.

##Getter/Setter methods

In order to directly access object variables from outside of the class, i.e. to be able to call `charger.color` and have it return `red`, we need to implement **getter methods**. A getter method is simply a method that allows you to read the value of an instance variable.

```ruby

class Car

  #...

  #Getter methods defined below
  def color
    @color
  end

  def brand
    @brand
  end 

  def model
    @model
  end
end

charger = Car.new('red', 'Dodge', 'Charger')

p charger.color 
# => "red"

p charger.brand 
# => "Dodge"

p charger.model 
# => "Charger"
```

We defined 3 getter methods named after an instance variable and simply had them return the value of that variable. Pretty straightforward!

If we didn't have a getter method for an instance variable, we would get an error when trying to read the variable:

```ruby
charger.color 
# => undefined method `color' for #<Car:0x00000000012973d0> (NoMethodError)
```

Now what if we wanted to *change* the value of a variable? Since we can now access variables directly using getter methods, we should just as easily be able to change the values of said variables... right?

```ruby
charger.color = 'blue'
# => undefined method `color=' for #<Car:0x0000000002616668> (NoMethodError)
#Did you mean?  color
```

Notice the part that reads `undefined method 'color='`, then the part that reads `Did you mean? color`. `color` is an already established getter method within our class, however `color=` is not currently an existing method, hence the error.

The trailing `=` character in a method name (such as `color=`) designates a method as a **setter** method, therefore `color` and `color=` are two totally separate methods.

Setter methods are set up like so:

```ruby
class Car

#...

 #Setter methods defined below
  def color=(color)
    @color = color
  end

  def brand=(brand)
    @brand = brand
  end 

  def model=(model)
    @model = model
  end
end
```

Now, we're able to both read variable values as well as change them.

```ruby
p charger.color
# => "red"

charger.color = 'blue'
p charger.color
# => "blue"
```
A little factoid: Since `=` symbolizes a setter method. We can also call our method like `charger.color=(blue)` and get the same result. Thankfully, thanks to [syntax sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) we are able to write it as `charger.color = 'blue'` for better readability.

##Accessor methods

Now if you're thinking that having to write getters and setters for every single variable is a lot of work, you thought right. A cornerstone of coding is the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle, and writing repetitive code is not only time consuming but ugly to look at.

Thankfully, Ruby gives us an easy way to set up our getters and setters in one line of code! Enter **accessor methods**.

There are 3 accessor methods available to make our lives easier:

* `attr_reader`: For creating getter methods
* `attr_writer`: For creating setter methods
* `attr_accessor`: For creating both getter and setter methods at once!!

`attr_accessor` allows us to create both getters and setters in a single line of code. Let's refactor our code to use this method.

```ruby
class Car
#Accessor methods
  attr_accessor :color, :brand, :model
  
  def initialize(color, brand, model)
    @color = color
    @brand = brand
    @model = model
  end

  def repaint(new_color)
    @color = new_color
  end
end
```

We call `attr_accessor` and pass in our variable arguments as [Symbols](https://medium.com/rubycademy/symbol-in-ruby-daca5abd4ab2). This creates both `attr_reader` and `attr_writer` for each variable all in one line.. much better!

##Everything is an object

Going back to the concept of **everything is an object**, does that mean that strings, arrays, numbers, etc are objects too? Yes, they are!

When you create an array or a string, you are creating an object from a built-in Ruby class!!

We can demonstrate this by calling the `.class` method, which shows us the class the object belongs to.

```ruby
p [].class
# => Array

p "Hello World".class
# => String

p 123.class
# => Integer
```

Arrays, strings, and numbers belong to the `Array`, `String`, and `Integer` classes respectively. These are some of the built-in classes within Ruby with their own methods and variables. Think about when you're using the `.capitalize` or `.reverse` method on a string, those are **instance methods** belonging to the `String` class!

##Conclusion
These are some of the basics of object-oriented programming in Ruby. With this knowledge you're ready for other OOP concepts such as [self](https://www.rubyguides.com/2020/04/self-in-ruby/), [class variables, and class methods](https://medium.com/geekculture/class-variable-class-methods-and-self-in-ruby-388706a4c491). Good luck and have fun!

#Sources
* https://www.educative.io/blog/object-oriented-programming
* https://fullstackheroes.com/tutorials/ruby/intro-to-classes/
* https://tinyurl.com/3u69xbn3