Basics of Ruby Classes
Tags: ruby, oop, webdev, beginners
Published: 2023-07-26 06:33:28

Ruby is one of the easiest languages to grasp the concept of OOP (object orientated programming). In particular the power that of PORO’s (plain old Ruby objects).

Let's go through a short example using a little robot 🤖 

## Goal specifications

We want to robot to be able to do the following:
1. Move around 🏃‍♂️
2. Return home 🏠
3. Change colour 🔴🟡🔵
4. Receive a toy 🧸

To define a class in Ruby it’s quite simple, let’s start.

```Ruby
# Initializing a class
class Robot
  def initialize(north, south, east, west, colour)
    @north = north
    @south = south
    @east = east
    @west = west
    @colour = colour
  end
end
```

The initializer method is responsible for handling creating new instances of our class. In this method we define what attributes we want our class to have, this can be anything!

## Using our class

Now I suggest if you're following along, running a ruby console (assuming you have installed ruby on your machine). To create and play around with our robot.

To create an instance of a class, and assign it to a local variable
`robot = Robot.new`
As simple as that!

Now we have access to our robot, but we cannot do anything with it. This is where attribute accessors come into play.

You should [read](https://www.rubyguides.com/2018/11/attr_accessor/) more in depth on this. The principle of attribute accessors is that, they give us write and read permissions on our classes instance objects.

```Ruby
# How to access attributes of a class
class Robot
  attr_accessor :north, :south, :east, :west, :colour

  def initialize(north, south, east, west, colour)
    @north = north
    @south = south
    @east = east
    @west = west
    @colour = colour
  end
end
```
Now if we call our class in irb, we can move our robot by going.

`robot = Robot.new(0,0,0,0)`

PLEASE NOTE: At this point, if we do not assign a value to these attributes, you will get an error. We will cover how to change this later.

## Class methods and Instance methods
There are two fundamental types on methods you can use on classes in ruby. Class methods, which are called only on the class, and instance methods, called only on an instance of the class.

Here is a good article with some more detail.
https://dev.to/adamlombard/ruby-class-methods-vs-instance-methods-4aje 

Now, we can update our class with what we have learnt.

```Ruby
class Robot
  attr_accessor :north, :south, :east, :west, :colour

  def initialize(north = 0, south = 0, east = 0, west = 0, colour = "red")
    @north = north
    @south = south
    @east = east
    @west = west
    @colour = colour
  end

  # Class method, only called on the class
  def self.instructions
    'Hello, I am a Robot, I can move in any direction, and I have a colour!'
  end

  # Instance method only called on an instance of the class
  def print_coordinates
    "Hello I am Robot, I am north #{north}, south #{south}, east #{east}, west #{west}"
  end
end
```
Notice as well I have added placeholder values in the class initializer method. This means we can safely call our class with no values.

Next let's write some methods to make our robot move around, by updating its attributes.

```Ruby
class Robot
  attr_accessor :north, :south, :east, :west, :colour

  def initialize(north = 0, south = 0, east = 0, west = 0, colour = "red")
    @north = north
    @south = south
    @east = east
    @west = west
    @colour = colour
  end

  # Class method, only called on the class
  def self.instructions
    'Hello, I am a Robot, I can move in any direction, and I have a colour!'
  end

  # Instance method only called on an instance of the class
  def print_coordinates
    "Hello I am Robot, I am north #{north}, south #{south}, east #{east}, west #{west}"
  end

  # Updating an attribute on the class
  def move_north(distance)
    self.north = north + distance
  end

  def move_south(distance)
    self.south = south + distance
  end

  def move_east(distance)
    self.east = east + distance
  end

  def move_west(distance)
    self.west = west + distance
  end
end
```

Notice how I call `self` on each method. This is because on these instance methods, we want to assign new data to the instance of the class, therefore itself. I suggest reading [this](https://bootrails.com/blog/ruby-self/#:~:text=What%20is%20self%20in%20Ruby,self%20refers%20to%20that%20class.) for more explanation.

## Using Constants

One very handy tool in ruby is the use of constants. I'd explain constants as key bits of data we want to store in memory, that relate to our class. For instance, they are commonly used in Models for important business logic. This data should not change once defined.

Below I have added some constants for our robots colour. Followed up by a method that uses an array of these constants.

```Ruby
# Using constants
class Robot
  attr_accessor :north, :south, :east, :west, :colour

  # Declaring constants
  RED = 'red'.freeze
  BLUE = 'blue'.freeze
  YELLOW = 'yellow'.freeze
  GREEN = 'green'.freeze

  # Declaring an array of constants
  COLOURS = [
    RED,
    BLUE,
    YELLOW,
    GREEN
  ].freeze

  def initialize(north = 0, south = 0, east = 0, west = 0, colour = RED)
    @north = north
    @south = south
    @east = east
    @west = west
    @colour = colour
  end

  # Class method, only called on the class
  def self.instructions
    'Hello, I am a Robot, I can move in any direction, and I have a colour!'
  end

  # Instance method only called on an instance of the class
  def print_coordinates
    "Hello I am Robot, I am north #{north}, south #{south}, east #{east}, west #{west}"
  end

  # Updating an attribute on the class
  def move_north(distance)
    self.north = north + distance
  end

  def move_south(distance)
    self.south = south + distance
  end

  def move_east(distance)
    self.east = east + distance
  end

  def move_west(distance)
    self.west = west + distance
  end

  # Select a random colour
  def change_colour
    self.colour = Robot::COLOURS.sample
  end

  # reset the co-ordinates
  def bring_home
    self.north = 0
    self.south = 0
    self.east = 0
    self.west = 0
  end
end
```
Look at how our class has grown! Now think of the power we have learnt (evil laugh).

Let's have fun with our instance methods:

```Ruby
robot = Robot.new
robot.move_north(3)
robot.change_colour
robot.bring_home
```
We have now achieved so much from the spec!

But wait, there's more we can do, and we haven't fulfilled the spec. We need to give our robot a toy, the little fella earnt it, don't you think?

Not only can we pass numbers and strings into our classes, we can also pass... dun dun dun, other classes!

Let's create a toy class. A simple class with a name attribute.

```Ruby
class Toy
  attr_accessor :name

  def initialize(name = 'skateboard')
    @name = name
  end
end
```

Now, if we pass in another attribute parameter to our robot, as well as define another attribute accessor. We can have full access of the toy within an instance of our robot class.

```Ruby
# Passing in a class instance object to another class

class Robot
  attr_accessor :north, :south, :east, :west, :colour, :toy

  # Declaring constants
  RED = 'red'.freeze
  BLUE = 'blue'.freeze
  YELLOW = 'yellow'.freeze
  GREEN = 'green'.freeze

  # Declaring an array of constants
  COLOURS = [
    RED,
    BLUE,
    YELLOW,
    GREEN
  ].freeze

  def initialize(north = 0, south = 0, east = 0, west = 0, colour = RED, toy)
    @north = north
    @south = south
    @east = east
    @west = west
    @colour = colour
    @toy = toy
  end

  # Class method, only called on the class
  def self.instructions
    'Hello, I am a Robot, I can move in any direction, and I have a colour!'
  end

  # Instance method only called on an instance of the class
  def print_coordinates
    "Hello I am Robot, I am north #{north}, south #{south}, east #{east}, west #{west}"
  end

  # Updating an attribute on the class
  def move_north(distance)
    self.north = north + distance
  end

  def move_south(distance)
    self.south = south + distance
  end

  def move_east(distance)
    self.east = east + distance
  end

  def move_west(distance)
    self.west = west + distance
  end

  # Select a random colour
  def change_colour
    self.colour = Robot::COLOURS.sample
  end

  # reset the co-ordinates
  def bring_home
    self.north = 0
    self.south = 0
    self.east = 0
    self.west = 0
  end
end
```
Now if you were to call `toy = Toy.new` and `robot = Robot.new(toy)`, you can successfully met the spec, congratulations!

## Conclusion
As you can see creating our own classes in ruby is not only fun, but extremely powerful. If you harness the power of classes in your rails applications, you can create very modular, DRY, and sexy looking code. 😘

I encourage you on your ruby journey, to jump on in a console and have fun! I find it's the best way to learn. Thanks for reading, you're a 💎