Ruby Basics

Hello, dear devs. 👋

I am back, bringing to you (again) the weirdest articles on the internet. I am sure you were waiting for that, right? (Please don't answer!)

Today, as the title points out, I will be briefly talking writing about the Ruby language and some of its features. 😉

If you have read my previous posts (something I don't recommend) I usually write about JavaScript related stuff and I love doing so.

But today I have decided to vary a little bit and talk about Ruby that is an extremely powerful language and, long story short, thanks to it I am a developer today. (Am I? 🤔)

Before start and given my ability to read minds I know it is time to Q & A:
Reading minds

Is Ruby A Dying Language?

Short answer: no. Indeed, it has been losing "popularity" along the years I cannot deny that but it is way far from death.

The Clash is my favorite band ever, it also has been losing its popularity but can you tell they are dying? 🎸 Yeah, you just cannot! (In musical terms, I meant.)
I am losing focus here, sorry. 😅

Ruby just got a recent minor update to 3.0.2 on July 7th but it is beyond the current scope of this article.

Is it worth learning Ruby in 2021? 💎

Paraphrasing someone I have no idea:

"... Knowledge does not occupy space!"

There are many discussions about this specific subject out there but honestly, I am here to bring you the basics of Ruby so let's get started because life is short.

⚠️ Disclaimer ⚠️

Given the language vastness it is impossible to include here, in a mere 10 minutes reading post, either all its features, different ways of doing the same thing or even best practices. I hope you understand that.

The examples are based in both my mental disorders, personal delirium and confused state of mind caused by unknown reasons, according to my psychologist.

If you are OK with that, welcome aboard !, otherwise you are free to go. There are tons of normal people waiting for you somewhere else. 😂 🤣

Ruby in a nutshell 💎 👉 🌰 🐚

🚨 The next three paragraphs I didn't write myself. They are, in my vision, plausible to explain what I have in mind. I am not a huge fan of "reinvent the wheel". 🚨

Ruby is a dynamic, interpreted, high-level and general-purpose programming language. Wikipedia:

It was was created by Yukihiro Matsumoto (Matz), in Japan in the mid 1990's and was designed for programmer productivity with the idea that programming should be fun for programmers.

It emphasizes the necessity for software to be understood by humans first and computers second. Launch School:

Please pay special attention for those three sentences:

"... necessity for software to be understood by humans first (🙋🥇) and computers second (🗣️ losers!)."

" ... It was was created by Yukihiro Matsumoto (Matz), in Japan."

" ... with the idea that programming should be fun for programmers."

Quote 1: Most of the time, Ruby statements are very similar to plain English and it turns out a language very easy to read and understand by us, mere mortals.

Quote 2: Try to convince me about something bad that is originally from Japan and fail miserably.

👉 [ Suggest a gif (about Japan) in the comments section and I will upload the best here. ]. 👈

Thanks for participating, Juan. The gif below is in your honor! 🤜🤛

Quote 3: The 'having fun' part you are going to discover very soon. If you stick with me until the end, of course. 😊

If you would like to follow along and don't have Ruby installed in your machine, don't worry. Install it! 🤣 😆 😂

Just kidding, you can use the Ruby Online Compiler & Interpreter - Replit. It is a mixing of the irb console and an IDE in the same page.

IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby expressions ...
Module: IRB

Commenting ✍️

You can comment a block of code, for example, using both the =begin and =end keywords and placing the block to be comment between them.

=begin
   Dale a tu cuerpo alegria Macarena
   Eeeh Macarena .... 
   Aaahe!
=end

You can also have an one-line comment using the #.

# Never gonna give you up, never gonna let you down ...

print / puts methods 🖥️

Both functions are used to display output on the screen. The major difference between them is that puts inserts a newline at the end.

print "Hello, "
print "darkness my old friend."

# Result: 
# Hello, darkness my old friend

puts "Hark, hark! I hear"
puts "The strain of strutting chanticleer"
puts "Cry, Cock-a-diddle-dow!"

# Result:
=begin
   Hark, hark! I hear
   The strain of strutting chanticleer
   Cry, Cock-a-diddle-dow! 
=end

Variables 📦

Ruby has four different kinds of variables where which one is declared in its specific way:

local = "Hey, I am a local variable."
    _other_local_var = "Never mess up with a local!"

    @instance = "For instance, I am an instance variable."
    @@class = "I am 'classy'."
    $global = "Globalization rules!"

Data Types 😎 😍 😈 👻 💩

Ruby has several data types: String, Integer, Float, Boolean (TrueClass, FalseClass), Symbol and Nil (NilClass).

I wouldn't classify them as primitive data types because everything is Ruby is an object. (or almost everything. Keep reading! 😜)

$my_name = "Clancy Gilroy"
puts $my_name.class        # String

name = "X Æ A-12"
puts name.class            # String

age = 35
puts age.class             # Integer

gpa = 3.22                 
puts gpa.class             # Float 

had_fun = true
puts had_fun.class         # TrueClass

is_hot = false
puts is_hot.class          # FalseClass

week_points = nil
puts week_points.class     # NilClass (absence of value)

symbol = :hello
puts symbol.class          # Symbol

Did you notice the .class method ? Since Ruby is a Fully Object Oriented language everything is an object, a property of or a method call on an object.

By the way and being shallow, an object is the instance of a given class.

Think of a class like a blueprint for creating objects, which determines initial values, attributes, behavior etc.

You can confirm the "almost everything is an object" theory using the .superclass method.

puts TrueClass.superclass        # Object
puts Integer.superclass          # Object
puts String.superclass           # Object
puts Object.superclass           # BasicObject
# so on and so forth

Some (Useful) String Methods 💪

Ruby has many built-in methods to manipulate strings.

Do you remember when I had mentioned that in Ruby things are similar to plain English? I bet you can guess what most of these methods below do, am I right? 😃

phrase = "   Sometimes I’m confused by what I think is really obvious. But what I think is really obvious obviously isn’t obvious.   "

puts phrase.upcase
puts phrase.downcase

# hint: .strip removes the empty spaces from both beginning and ending.
puts phrase.strip    

puts phrase.length
puts phrase.include? "Some"
puts phrase.include? "Sure"
puts phrase.index("confused")

puts "auckland".capitalize

It is also possible to create both interpolation and concatenation of strings.

character_age = "17"
character_name = "Link"

# Interpolation
puts "There once was a young man named #{character_name}"

# Concatenation ➕
# '.to_s' is the short method name for 'to string'
# There is more like '.to_i', '.to_f', '.to_a' etc
puts "he was " + character_age.to_s + " years old"

Some Math Methods 🔢🔣

puts 5 + 9     # addition
puts 2 - 9     # subtraction
puts 6 * 9     # multiplication
puts 10 / 7.0  # division
puts 2 ** 3    # exponentiation
puts 3 % 2     # modulo or remainder

num = -163.23
num2 = 45.47

# returns the absolute number
puts num.abs                 # 163.23   

# return a value rounded to the nearest value with "n" digits decimal digits precision.
puts num2.round              # 45

# returns the ceil value
puts num2.ceil               # 46

# returns the floor value 
puts num2.floor              # 45

# returns the square root of a given number (5.0)
puts Math.sqrt(25)           # 5.0

# returns the natural logarithm (base e) or logarithm to the specified base of a given number, e.g. Math.log(x, base).
puts Math.log(100)           # 4.605170185988092

Arrays 📚

Ruby arrays are collections of ordered, integer-indexed objects which can store numbers, strings, hashes, symbols, objects or even any other arrays.

# different ways to declare an array
friends = Array["Rapha", "Alexandre", "Andre", "Bogus"]

# I prefer this one 👇
fruits_array = ["cherimoya", "durian", "lamut", "langsat" ]

# What the heck? 🤢
hello = Array.new(5, "awful").to_s 
# output: ["awful", "awful", "awful", "awful", "awful"]
puts hello 

# Remind: Ruby Arrays indexing starts at 0.
puts friends[0]      # Rapha
puts friends[-1]     # Bogus
puts friends[0,2]    # Rapha Alexandre

# Replacing items
friends[0] = "Fael"

# output: ["Fael", "Alexandre", "Andre", "Bogus"]
puts friends.to_s    

puts friends.length  # 4

# Down here the methods syntax are pretty much understandable
puts friends.include?("Fael")
puts friends.reverse
puts friends.sort
puts friends.empty?
puts fruits_array.push("rambutan")

## the example above (`push`) could also be done as shown below
fruits_array << "akebi"

The Ruby arrays built-in methods are not limited to just those ones I have mentioned above.
If you are eager to learn more (I am sure you are 🤓), Ruby docs is a really good resource to do so.

Hashes #️⃣

Ruby hashes are collections of key-value pairs, for example:

random_hash = {
    :sao_paulo => "SP",
    "rio_de_janeiro" => "RJ",
    1 => "NY",
}

Remember that the way you declare the key is the same you will use in order to successfully access its value in a hash.

For example, trying to access random_hash["sao_paulo"] would return nothing because :sao_paulo is not the same as "sao_paulo". (Symbolx String)

This is São Paulo, by the way! 🏙️
Sao Paulo City

puts random_hash
puts random_hash[:sao_paulo]       # SP
puts random_hash["rio_de_janeiro"] # RJ
puts random_hash[1]                # NY
puts random_hash.nil?              # false
puts random_hash.length            # 3

Methods 📐📏

Ruby methods are used to tie or wrap together one or more repeatable statements into a single unit.

Along the article we have already used many methods, for example, length, sqrt, to_s etc.

The difference here is that we have only invoked them.
Invoking

Someone, probably Matz, have already written them for us and for this reason they are the so called built-in.

Even Ruby being an amazing language it is practically impossible to include every single method that we would use in our daily life. We are free to implement them by our own.

Hint: Method names should begin with a lowercase letter.

"... If you begin a method name with an uppercase letter, Ruby might think that it is a constant and hence can parse the call incorrectly. "
Source: Tutorials Point

# Method without parameter(s)

# This is how you declare a method
def say_konnichiwa
    puts "こんにちは"
end

# This is how you invoke it
say_konnichiwa

# Method with parameter(s)
def say_tadaima(title, name="Unknown")
    puts "ただいま, #{title} #{name}!"
end

say_tadaima("Mr.", "Toguro") # prints "ただいま, Mr. Toguro!"

Note that the second parameter name holds the default value Unknown. In other words, if we call the same method without providing the second argument, the default value will be put in place. Example below:

say_tadaima("Mr.")           # prints "ただいま, Mr. Unknown!"

We can also return values from inside of our methods both explicitly or implicitly.

- Explicit return:

def cube(num)
   return num * num * num, 70, "return", true, "?????"

   # Never reached
   puts "No value after the return statement is reached"
end

# IT will console the result of "num * num * num"
puts cube(3)

# Prints the number "70"
puts cube(3)[1]

# Prints the string "return" and so on
puts cube(3)[2]

- Implicit return:

def always_return
    "I am always returned"
end

# Variable created for the sake of demonstration
result = always_return

puts result   # "I am always returned"

In other words, methods are most likely those relatives we don't like. They always return. (🥁 Tu dum tsss)

Conditionals (if, else and elsif)

is_dev = true
language = "Cobol"

if(is_dev && language == "Cobol")
    puts "DISPLAY 'Hello, Cobol'."
elsif (is_dev && language == "Pascal")
    puts "writeln('Hello, Pascal!');"
elsif (is_dev && language == "Delphi")
    puts "ShowMessage('Hello Delphi');"
else 
    puts "👋"
end

We can also use comparisons inside of if / else statements.

def max_number(num1, num2, num3)
    if (num1 >= num2 and num1 >= num3)
        return num1
    elsif (num2 >= num1 and num2 >= num3)
        return num2
    else
        return num3
    end    
end

puts max_number(9, 53, 77)

Case Expressions 💼

There is no mystery here. I am sure you can read this masterpiece of code and guess what it does.

def get_day_name(day_abbreviaton)
    day_name = ""

     case day_abbreviaton.downcase
    when "ma"
        day_name = "Maanantai"
    when "ti"
        day_name = "Tiistai"
    when "ke"
        day_name = "Keskiviikko"
    when "to"
        day_name = "Torstai"
    when "pe"
        day_name = "Perjantai"
    when "la"
        day_name = "Lauantai"
    when "sun"
        day_name = "Sunnuntai"
    else
        day_name = "En tiedä!"
    end

    # Implicit returns the value
    day_name

end

puts get_day_name("ma")      # Maanantai
puts get_day_name("koira")   # En tiedä!

While Loops ↪️

lucky_num = 70

# Note that the 'not' keyword is similar to '!=' (negation)
while not lucky_num == 77
    puts "No Lucky #{lucky_num}"

    lucky_num +=1
end

Loops ↩️

# You already know what is an array :)
people = ["C. Falcon", "Fox", "DK", "Ness", "Samus"]

# It will display all names in a different line
for person in people
    puts person
end

# Same same but ... same
people.each do |person|
    puts person
end

# Display numbers from range 0 to 5 (inclusive ..)
for inclusive in 0..5
    puts inclusive
end

# Display number from range 0 to 4 (exclusive ...)
for exclusive in 0...5
    puts exclusive
end   

# Let's agree: This is plain English
# PS.: counting starts from 0
6.times do |phrase|
    puts "Counting: #{phrase}"   
end

Classes And Objects 📝 🛋️

This is my favorite part. Why? Because I remember when I have had my first contact with OOP (back in 2000's [btw, old are your ancestors 😠]) in Java.
I was like: "What the heck is going on here?".
I got to understand it better because of the Ruby way of doing things.

## This is a very simple class
class Character
    attr_accessor :name, :role, :home
end

## This is a very simple way to create a new object
character = Character.new()
character.name = "Rygar"
character.role = "Searcher"
character.home = "Black Mountains"

puts character.name

Good that you asked (🔮), under the hood the attr_... creates the so famous getters and setters for you, for instance:

-attr_accessor: creates the getter and setter methods.
-attr_reader: creates only the getter method.
-attr_writer: creates only the setter method.

TLDR;

# In other words this
attr_acessor :name

# Is the equivalent of
def name
    @name
end

def name=(value)
    @name = value
end

# Note: The equals sign is a Ruby convention when creating setters. 
# Omitting it is considering a bad practice but still works.
# (if it is working don't touch! 👍)

Cool, right? We have achieved so much just writing very few lines of code. I have to confess, when writing code using Ruby sometimes I feel like cheating.
But, in my opinion, Ruby saves our time so we can worry about stuff that really matters.

Let's check an other manner to create a class in Ruby.

class Shoes
    attr_accessor :brand, :size, :color

    # Allows you to set the initial values for a given object
    # Does 'constructor' sound familiar to you?
    def initialize(brand, size, color)
        @brand = brand
        @size = size
        @color = color        
    end

end

trainers = Shoes.new("Kichute", 47, "Heliotrope")
puts trainers.brand  # Kichute

Object / Class Methods

class Hero
    attr_accessor :name, :animal, :hp

    def initialize(name, animal, hp)
        @name = name
        @animal = animal
        @hp = hp
    end

    # It is a instance method (Good catch, Juan! 🏆)
    def has_enough_hp
        @hp > 5.5 ? "Able to play" : "Consider resting, #{@name}"
    end
end

hero1 = Hero.new("Nakoruru", "eagle", 80)
hero2 = Hero.new("Galford", "husky", 2.9)

# Here we have invoked the object(s) method
puts hero1.has_enough_hp      # Able to play
puts hero2.has_enough_hp      # Consider resting, Galford

Class Inheritance (Shallow Explanation)

Allows classes to inherit characteristics of other classes.

class Fighter
    def make_special_attack
        puts "Hadouken"
    end

    def make_uppercut_punch
        puts "Makes the uppercut move"
    end
end

ryu = Fighter.new()
ryu.make_special_attack          # Hadouken
ryu.make_uppercut_punch          # Makes the uppercut move

# Subclass - "<" means inherits from
class SpecialFighter < Fighter 

    # Overwrites 'make_special_attack' method
    def make_special_attack
        puts "Shun Goku Satsu"
    end

    # Creates a new method for this class
    def celebrate
        puts "Is that all? You must be joking!"
    end
end

gouki = SpecialFighter.new()
gouki.make_special_attack  # Shun Goku Satsu
gouki.make_uppercut_punch  # Makes the uppercut move
gouki.celebrate            # Is that all? You must be joking!

Note: as the make_uppercut_punch move is used by both characters there is no need to re-declare it in our subclass.

Modules 🛒

Modules are nothing more nothing less than a container where you can store methods.

module Greeting
    def say_hello(name="there")
        puts "Hi #{name}."
    end

    def say_bye_bye(name="dear")
        puts "Bye bye #{name}."
    end
end

In order to access modules you must include its relative path as the example shows:

require_relative "./folder/the_name_of_your_file.rb"
include Greeting

Greeting.say_hello("Bowser")   # Hi Bowser.

Error Handling ⛔

Error handlers are methods defined in the Exception class to deal with errors.

begin
    # we have never defined the 'fav_character' variable
    # so it will fire the second 'rescue' block
    # and will display the customized message even
    # knowing that we also tried to make a division by 0
    # => Reference to control flow for more <=

    fav_character["Makoto Mizuhara"]
    num = 45 / 0
rescue ZeroDivisionError => e
    puts e
    puts "Division By Zero Error"
rescue NameError
    puts "undefined local variable or method 'fav_character'."
end

If you would like to see the other error handling in action, replace the statement fav_character["Makoto Mizuhara"] for that one fav_character = ["Makoto Mizuhara"]. 😉

And that's it, folks! I mean, Ruby cannot be resumed by any means to the features I have shown so far but it is a pretty good start.

Happy Hour Moment 🍹🍻🥤🍷

The good news here are that if you have followed along until the end (and thank you a ton for doing that! 🙏), you can already use the acquired knowledge to start exploring other 'seas'. For example, why don't you try to play around with Rails (one of Ruby frameworks) a little bit?

Skip this part 📚 📖

I can see you did not skip it so don't you dare to complain later. 😂👌

" The curiosity killed the cat."

Well, if you allow me to recommend an excellent source to learn about Ruby on Rails, I would advice the Zayne's Youtube channel.
He doesn't publish videos very often but it really worth checking it out!

You can also check Web Crunch, Codemy, FreeCodeCamp and of course the Ruby Docs and Rails Guides.

Conclusion and Final Thoughts 🔚 💡

Ruby is without any doubts a very powerful, beginner friendly and lovely language. As any language, it has your flaws but don't be influenced by the "which one is the best language?" war.

Ruby has been evolving a lot in the last years and counts with a very strong and respectful community that will help you whenever you need it.

I have tried to highlight some examples I personally consider important when 'meeting' Ruby for the very first time.

As you could see, the language speaks for itself. I venture to say that it is a self.describing language. (Pun intended 🤦)

Thanks so much for your companion. I hope you have learned something new today. (It doesn't necessarily mean from this article! 🤣)

Cheerio! 👋

23